[
  {
    "path": ".gitignore",
    "content": "pkg/\nGemfile.lock\ntmp/\ncoverage/\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nrvm:\n  - '2.2'\n  - '2.1'\n  - '2.0'\n  - '1.9'\nnotifications:\n  email:\n    - carlos@apache.org\n    - tim@github.com\nenv:\n  - PUPPET_VERSION=\"~> 4.2.0\"\n  - PUPPET_VERSION=\"~> 4.1.0\"\n  - PUPPET_VERSION=\"~> 4.0.0\"\n  - PUPPET_VERSION=\"~> 3.7.0\"\n  - PUPPET_VERSION=\"~> 3.6.0\"\n  - PUPPET_VERSION=\"~> 3.5.0\"\n  - PUPPET_VERSION=\"~> 3.4.0\"\n  - PUPPET_VERSION=\"~> 3.3.0\"\n  - PUPPET_VERSION=\"~> 3.2.0\"\n  - PUPPET_VERSION=\"~> 3.1.0\"\n  - PUPPET_VERSION=\"~> 3.0.0\"\n  - PUPPET_VERSION=\"~> 2.7.0\"\n\nbefore_script:\n  - puppet --version\n\nmatrix:\n  # Puppet < 3.5.0 is broken under ruby 2.1 https://tickets.puppetlabs.com/browse/PUP-1243\n  # Puppet < 3.8.0 is broken under ruby 2.2 https://tickets.puppetlabs.com/browse/PUP-3796\n  exclude:\n    - rvm: '2.1'\n      env: PUPPET_VERSION=\"~> 2.7.0\"\n    - rvm: '2.1'\n      env: PUPPET_VERSION=\"~> 3.0.0\"\n    - rvm: '2.1'\n      env: PUPPET_VERSION=\"~> 3.1.0\"\n    - rvm: '2.1'\n      env: PUPPET_VERSION=\"~> 3.2.0\"\n    - rvm: '2.1'\n      env: PUPPET_VERSION=\"~> 3.3.0\"\n    - rvm: '2.1'\n      env: PUPPET_VERSION=\"~> 3.4.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 2.7.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.0.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.1.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.2.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.3.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.4.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.5.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.6.0\"\n    - rvm: '2.2'\n      env: PUPPET_VERSION=\"~> 3.7.0\"\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Librarian-puppet\n\n# NOTE this project has moved to https://github.com/voxpupuli/librarian-puppet\n"
  },
  {
    "path": "Changelog.md",
    "content": "# Changelog\n\n## From 2.x Librarian-Puppet requires Ruby >= 1.9, uses Puppet Forge API v3. For Ruby 1.8 use 1.x\n\n### 2.2.1\n\n * [Issue #311](https://github.com/rodjek/librarian-puppet/issues/311) Omit versions with a deleted_at date\n\n### 2.2.0\n\n * Add support for Puppet 4\n * [Issue #296](https://github.com/rodjek/librarian-puppet/issues/296) Uninitialized constant Puppet::ModuleTool::ModulefileReader using Modulefiles in Puppet 4. Ignore those dependencies\n\n### 2.1.1\n\n * [Issue #302](https://github.com/rodjek/librarian-puppet/issues/302) Ensure path is not lost when default specfile is used\n * [Issue #294](https://github.com/rodjek/librarian-puppet/issues/294) Undefined variable calling Puppet version in old Puppet 2.x versions\n * [Issue #285](https://github.com/rodjek/librarian-puppet/issues/294) Update librarianp to allow overriding dependencies from multiple sources\n\n### 2.1.0\n\n * Update librarian to use the new `exclusion` syntax\n * [Issue #282](https://github.com/rodjek/librarian-puppet/issues/282) Merge duplicated dependencies and warn the user, no more `Cannot bounce Puppetfile.lock!` errors\n * [Issue #217](https://github.com/rodjek/librarian-puppet/issues/217)[Issue #244](https://github.com/rodjek/librarian-puppet/issues/244) Use librarianp 0.4.0 that no longer uses recursion to avoid `stack level too deep` errors\n * [Issue #277](https://github.com/rodjek/librarian-puppet/issues/277) Warn when there are two dependencies with the same module name\n * Use `librarianp` gem instead of `librarian`, a fork with the needed improvements and fixes.\n\n### 2.0.1\n\n * [Issue #272](https://github.com/rodjek/librarian-puppet/issues/272) Defined forge is not used when resolving dependencies\n * [Issue #150](https://github.com/rodjek/librarian-puppet/issues/150) Allow dependencies other than Puppet modules\n * [Issue #269](https://github.com/rodjek/librarian-puppet/issues/269) Better error message if metadata.json is bad\n * [Issue #264](https://github.com/rodjek/librarian-puppet/issues/264) Copying files can cause permission problems on Windows\n\n### 2.0.0\n\n * Jump from 1.3.x to 2.x to leave 1.x for Ruby 1.8 compatibility\n * [Issue #254](https://github.com/rodjek/librarian-puppet/issues/254) Add a rsync option to prevent deleting directories\n * [Issue #261](https://github.com/rodjek/librarian-puppet/issues/261) Incorrect install directory is created if the organization name contains a dash\n * [Issue #255](https://github.com/rodjek/librarian-puppet/issues/255) Ignored forge URL when using API v3\n\n### 1.5.0\n\n * Update librarian to use the new `exclusion` syntax\n * [Issue #282](https://github.com/rodjek/librarian-puppet/issues/282) Merge duplicated dependencies and warn the user, no more `Cannot bounce Puppetfile.lock!` errors\n * [Issue #217](https://github.com/rodjek/librarian-puppet/issues/217)[Issue #244](https://github.com/rodjek/librarian-puppet/issues/244) Use librarianp 0.4.0 that no longer uses recursion to avoid `stack level too deep` errors\n * [Issue #277](https://github.com/rodjek/librarian-puppet/issues/277) Warn when there are two dependencies with the same module name\n * Use `librarianp` gem instead of `librarian`, a fork with the needed improvements and fixes.\n\n### 1.4.1\n\n * [Issue #272](https://github.com/rodjek/librarian-puppet/issues/272) Defined forge is not used when resolving dependencies\n * [Issue #150](https://github.com/rodjek/librarian-puppet/issues/150) Allow dependencies other than Puppet modules\n * [Issue #269](https://github.com/rodjek/librarian-puppet/issues/269) Better error message if metadata.json is bad\n * [Issue #264](https://github.com/rodjek/librarian-puppet/issues/264) Copying files can cause permission problems on Windows\n\n### 1.4.0\n\n * Jump from 1.0.x to 1.4.x to keep Ruby 1.8 compatibility in the 1.x series\n * [Issue #254](https://github.com/rodjek/librarian-puppet/issues/254) Add a rsync option to prevent deleting directories\n * [Issue #261](https://github.com/rodjek/librarian-puppet/issues/261) Incorrect install directory is created if the organization name contains a dash\n\n### 1.3.3\n\n * [Issue #250](https://github.com/rodjek/librarian-puppet/issues/250) Fix error when module has no dependencies in `metadata.json`\n\n### 1.3.2\n\n * [Issue #246](https://github.com/rodjek/librarian-puppet/issues/246) Do not fail if modules have no `Modulefile` nor `metadata.json`\n\n### 1.3.1\n\n * Version in dependencies with `metadata.json` is ignored\n\n### 1.3.0\n\n * If no Puppetfile is present default to use the `metadata.json` or `Modulefile`\n * [Issue #235](https://github.com/rodjek/librarian-puppet/issues/235) Error when forge is not defined in `Puppetfile`\n * [Issue #243](https://github.com/rodjek/librarian-puppet/issues/243) Warn if `Modulefile` doesn't contain a version\n\n### 1.2.0\n\n * Implement `metadata` syntax for `Puppetfile`\n * [Issue #220](https://github.com/rodjek/librarian-puppet/issues/220) Add support for metadata.json\n * [Issue #242](https://github.com/rodjek/librarian-puppet/issues/242) Get organization from name correctly if name has multiple dashes\n\n### 1.1.3\n\n * [Issue #237](https://github.com/rodjek/librarian-puppet/issues/237) [Issue #238](https://github.com/rodjek/librarian-puppet/issues/238) Unable to use a custom v3 forge: add flags `--use-v1-api` and `--no-use-v1-api`\n * [Issue #239](https://github.com/rodjek/librarian-puppet/issues/239) GitHub tarball: add access_token correctly to url's which are already having query parameters\n * [Issue #234](https://github.com/rodjek/librarian-puppet/issues/234) Use organization-module instead of organization/module by default\n\n### 1.1.2\n\n * [Issue #231](https://github.com/rodjek/librarian-puppet/issues/231) Only use the `GITHUB_API_TOKEN` if it's not empty\n * [Issue #233](https://github.com/rodjek/librarian-puppet/issues/233) Fix version regex to match e.g. 1.99.15\n * Can't pass the Puppet Forge v1 api url to clients using v3 (3.6.0+ and PE 3.2.0+)\n\n### 1.1.1\n\n * [Issue #227](https://github.com/rodjek/librarian-puppet/issues/227) Fix Librarian::Puppet::VERSION undefined\n\n### 1.1.0\n\n * [Issue #210](https://github.com/rodjek/librarian-puppet/issues/210) Use forgeapi.puppetlabs.com and API v3\n   * Accesing the v3 API requires Ruby 1.9 due to the puppet_forge library used\n\n\n### 1.0.10\n\n * [Issue #250](https://github.com/rodjek/librarian-puppet/issues/250) Fix error when module has no dependencies in `metadata.json`\n\n### 1.0.9\n\n * [Issue #246](https://github.com/rodjek/librarian-puppet/issues/246) Do not fail if modules have no `Modulefile` nor `metadata.json`\n\n### 1.0.8\n\n * Version in dependencies with `metadata.json` is ignored\n\n### 1.0.7\n\n * If no Puppetfile is present default to use the `metadata.json` or `Modulefile`\n * [Issue #235](https://github.com/rodjek/librarian-puppet/issues/235) Error when forge is not defined in `Puppetfile`\n * [Issue #243](https://github.com/rodjek/librarian-puppet/issues/243) Warn if `Modulefile` doesn't contain a version\n\n\n### 1.0.6\n\n * Implement `metadata` syntax for `Puppetfile`\n * [Issue #220](https://github.com/rodjek/librarian-puppet/issues/220) Add support for metadata.json\n * [Issue #242](https://github.com/rodjek/librarian-puppet/issues/242) Get organization from name correctly if name has multiple dashes\n\n### 1.0.5\n\n * [Issue #237](https://github.com/rodjek/librarian-puppet/issues/237)[Issue #238](https://github.com/rodjek/librarian-puppet/issues/238) Unable to use a custom v3 forge: add flags `--use-v1-api` and `--no-use-v1-api`\n * [Issue #239](https://github.com/rodjek/librarian-puppet/issues/239) GitHub tarball: add access_token correctly to url's which are already having query parameters\n * [Issue #234](https://github.com/rodjek/librarian-puppet/issues/234) Use organization-module instead of organization/module by default\n\n### 1.0.4\n\n * [Issue #231](https://github.com/rodjek/librarian-puppet/issues/231) Only use the `GITHUB_API_TOKEN` if it's not empty\n * [Issue #233](https://github.com/rodjek/librarian-puppet/issues/233) Fix version regex to match e.g. 1.99.15\n * Can't pass the Puppet Forge v1 api url to clients using v3 (3.6.0+ and PE 3.2.0+)\n\n### 1.0.3\n\n * [Issue #223](https://github.com/rodjek/librarian-puppet/issues/223) `Cannot bounce Puppetfile.lock!` error when Forge modules contain duplicated dependencies\n\n### 1.0.2\n\n * [Issue #211](https://github.com/rodjek/librarian-puppet/issues/211) Pass the PuppetLabs Forge API v3 endpoint to `puppet module` when running on Puppet >= 3.6.0\n * [Issue #198](https://github.com/rodjek/librarian-puppet/issues/198) Reduce the length of tmp dirs to avoid issues in windows\n * [Issue #206](https://github.com/rodjek/librarian-puppet/issues/206) githubtarball call for released versions does not consider pagination\n * [Issue #204](https://github.com/rodjek/librarian-puppet/issues/204) Fix regex to detect Forge API v3 url\n * [Issue #199](https://github.com/rodjek/librarian-puppet/issues/199) undefined method run! packaging a git source\n * Verify SSL certificates in github calls\n\n### 1.0.1\n\n * [Issue #190](https://github.com/rodjek/librarian-puppet/issues/190) Pass the PuppetLabs Forge API v3 endpoint to `puppet module` when running on Puppet Enterprise >= 3.2\n * [Issue #196](https://github.com/rodjek/librarian-puppet/issues/196) Fix error in error handling when puppet is not installed\n\n### 1.0.0\n\n * Remove deprecation warning for github_tarball sources, some people are actually using it\n\n### 0.9.17\n\n * [Issue #193](https://github.com/rodjek/librarian-puppet/issues/193) Support Puppet 3.5.0\n\n### 0.9.16\n\n * [Issue #181](https://github.com/rodjek/librarian-puppet/issues/181) Should use qualified module names for resolution to work correctly\n * Deprecate github_tarball sources\n * Reduce number of API calls for github_tarball sources\n\n### 0.9.15\n\n * [Issue #187](https://github.com/rodjek/librarian-puppet/issues/187) Fixed parallel installation issues\n * [Issue #185](https://github.com/rodjek/librarian-puppet/issues/185) Sanitize the gem/bundler environment before spawning (ruby 1.9+)\n\n### 0.9.14\n\n * [Issue #182](https://github.com/rodjek/librarian-puppet/issues/182) Sanitize the environment before spawning (ruby 1.9+)\n * [Issue #184](https://github.com/rodjek/librarian-puppet/issues/184) Support transitive dependencies in modules using :path\n * Git dependencies using modulefile syntax make librarian-puppet fail\n * [Issue #108](https://github.com/rodjek/librarian-puppet/issues/108) Don't fail on malformed Modulefile from a git dependency\n\n### 0.9.13\n\n * [Issue #176](https://github.com/rodjek/librarian-puppet/issues/176) Upgrade to librarian 0.1.2\n * [Issue #179](https://github.com/rodjek/librarian-puppet/issues/179) Need to install extra gems just in case we are in ruby 1.8\n * [Issue #178](https://github.com/rodjek/librarian-puppet/issues/178) Print a meaningful message if puppet gem can't be loaded for :git sources\n\n### 0.9.12\n\n * Remove extra dependencies from gem added when 0.9.11 was released under ruby 1.8\n\n### 0.9.11\n\n * Add modulefile dsl to reuse Modulefile dependencies\n * Consider Puppetfile-dependencies recursively in git-source\n * Support changing tmp, cache and scratch paths\n * librarian-puppet package causes an infinite loop\n * Show a message if no versions are found for a module\n * Make download of tarballs more robust\n * Require open3_backport in ruby 1.8 and install if not present\n * Git dependencies in both Puppetfile and Modulefile cause a Cannot bounce Puppetfile.lock! error\n * Better sort of github tarball versions when there are mixed tags starting with and without 'v'\n * Fix error if a git module has a dependency without version\n * Fix git dependency with :path attribute\n * Cleaner output when no Puppetfile found\n * Reduce the number of API calls to the Forge\n * Don't sort versions as strings. Rely on the forge returning them ordered\n * Pass --module_repository to `puppet module install` to install from other forges\n * Cache forge responses and print an error if returns an invalid response\n * Add a User-Agent header to all requests to the GitHub API\n * Convert puppet version requirements to rubygems, pessimistic and ranges\n * Use librarian gem\n\n### 0.9.10\n\n * Catch GitHub API rate limit exceeded\n * Make Librarian::Manifest Semver 2.0.0 compatible\n\n### 0.9.1\n * Proper error message when a module that is sourced from the forge does not\n   exist.\n * Added support for annotated tags as git references.\n * `librarian-puppet init` adds `.tmp/` to gitignore instead of `tmp/`.\n * Fixed syntax error in the template Puppetfile created by `librarian-puppet\n   init`.\n * Checks for `lib/puppet` as well as `manifests/` when checking if the git\n   repository is a valid module.\n * When a user specifies `<foo>/<bar>` as the name of a module sources from a\n   git repository, assume the module name is actually `<bar>`.\n * Fixed gem description and summary in gemspec.\n\n### 0.9.0\n * Initial release\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\ngemspec\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2012-2014 Tim Sharpe, Carlos Sanchez and others\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Librarian-puppet\n\n# NOTE This project has moved to https://github.com/voxpupuli/librarian-puppet\n\n[![Build Status](https://travis-ci.org/rodjek/librarian-puppet.png?branch=master)](https://travis-ci.org/rodjek/librarian-puppet)\n\n## Introduction\n\nLibrarian-puppet is a bundler for your puppet infrastructure.  You can use\nlibrarian-puppet to manage the puppet modules your infrastructure depends on,\nwhether the modules come from the [Puppet Forge](https://forge.puppetlabs.com/),\nGit repositories or just a path.\n\n* Librarian-puppet can reuse the dependencies listed in your `Modulefile` or `metadata.json`\n* Forge modules can be installed from [Puppetlabs Forge](https://forge.puppetlabs.com/) or an internal Forge such as [Pulp](http://www.pulpproject.org/)\n* Git modules can be installed from a branch, tag or specific commit, optionally using a path inside the repository\n* Modules can be installed from GitHub using tarballs, without needing Git installed\n* Modules can be installed from a filesystem path\n* Module dependencies are resolved transitively without needing to list all the modules explicitly\n\n\nLibrarian-puppet manages your `modules/` directory for you based on your\n`Puppetfile`.  Your `Puppetfile` becomes the authoritative source for what\nmodules you require and at what version, tag or branch.\n\nOnce using Librarian-puppet you should not modify the contents of your `modules`\ndirectory.  The individual modules' repos should be updated, tagged with a new\nrelease and the version bumped in your Puppetfile.\n\nIt is based on [Librarian](https://github.com/applicationsonline/librarian), a\nframework for writing bundlers, which are tools that resolve, fetch, install,\nand isolate a project's dependencies.\n\n## Versions\n\nLibrarian-puppet >= 2.0 (as well as 1.1, 1.2 and 1.3) requires Ruby 1.9 and uses the Puppet Forge API v3.\nVersions < 2.0 work on Ruby 1.8.\n\nSee the [Changelog](Changelog.md) for more details.\n\n## The Puppetfile\n\nEvery Puppet repository that uses Librarian-puppet may have a file named\n`Puppetfile`, `metadata.json` or `Modulefile` in the root directory of that repository.\nThe full specification\nfor which modules your puppet infrastructure repository depends goes in here.\n\n### Simple usage\n\nIf no Puppetfile is present, `librarian-puppet` will download all the dependencies\nlisted in your `metadata.json` or `Modulefile` from the Puppet Forge,\nas if the Puppetfile contained\n\n    forge \"https://forgeapi.puppetlabs.com\"\n\n    metadata\n\n\n### Example Puppetfile\n\n    forge \"https://forgeapi.puppetlabs.com\"\n\n    mod 'puppetlabs-razor'\n    mod 'puppetlabs-ntp', \"0.0.3\"\n\n    mod 'puppetlabs-apt',\n      :git => \"git://github.com/puppetlabs/puppetlabs-apt.git\"\n\n    mod 'puppetlabs-stdlib',\n      :git => \"git://github.com/puppetlabs/puppetlabs-stdlib.git\"\n\n    mod 'puppetlabs-apache', '0.6.0',\n      :github_tarball => 'puppetlabs/puppetlabs-apache'\n\n    mod 'acme-mymodule', :path => './some_folder'\n\n    exclusion 'acme-bad_module'\n\n\n### Recursive module dependency resolution\n\nWhen fetching a module all dependencies specified in its\n`Modulefile`, `metadata.json` and `Puppetfile` will be resolved and installed.\n\n### Puppetfile Breakdown\n\n    forge \"https://forgeapi.puppetlabs.com\"\n\nThis declares that we want to use the official Puppet Labs Forge as our default\nsource when pulling down modules.  If you run your own local forge, you may\nwant to change this.\n\n    metadata\n\nDownload all the dependencies listed in your `metadata.json` or `Modulefile` from the Puppet Forge.\n\n    mod 'puppetlabs-razor'\n\nPull in the latest version of the Puppet Labs Razor module from the default\nsource.\n\n    mod 'puppetlabs-ntp', \"0.0.3\"\n\nPull in version 0.0.3 of the Puppet Labs NTP module from the default source.\n\n    mod 'puppetlabs-apt',\n      :git => \"git://github.com/puppetlabs/puppetlabs-apt.git\"\n\nOur puppet infrastructure repository depends on the `apt` module from the\nPuppet Labs GitHub repos and checks out the `master` branch.\n\n    mod 'puppetlabs-apt',\n      :git => \"git://github.com/puppetlabs/puppetlabs-apt.git\",\n      :ref => '0.0.3'\n\nOur puppet infrastructure repository depends on the `apt` module from the\nPuppet Labs GitHub repos and checks out a tag of `0.0.3`.\n\n    mod 'puppetlabs-apt',\n      :git => \"git://github.com/puppetlabs/puppetlabs-apt.git\",\n      :ref => 'feature/master/dans_refactor'\n\nOur puppet infrastructure repository depends on the `apt` module from the\nPuppet Labs GitHub repos and checks out the `dans_refactor` branch.\n\nWhen using a Git source, we do not have to use a `:ref =>`.\nIf we do not, then librarian-puppet will assume we meant the `master` branch.\n\nIf we use a `:ref =>`, we can use anything that Git will recognize as a ref.\nThis includes any branch name, tag name, SHA, or SHA unique prefix. If we use a\nbranch, we can later ask Librarian-puppet to update the module by fetching the\nmost recent version of the module from that same branch.\n\nThe Git source also supports a `:path =>` option. If we use the path option,\nLibrarian-puppet will navigate down into the Git repository and only use the\nspecified subdirectory. Some people have the habit of having a single repository\nwith many modules in it. If we need a module from such a repository, we can\nuse the `:path =>` option here to help Librarian-puppet drill down and find the\nmodule subdirectory.\n\n    mod 'puppetlabs-apt',\n      :git => \"git://github.com/fake/puppet-modules.git\",\n      :path => \"modules/apt\"\n\nOur puppet infrastructure repository depends on the `apt` module, which we have\nstored as a directory under our `puppet-modules` git repos.\n\n    mod 'puppetlabs-apache', '0.6.0',\n      :github_tarball => 'puppetlabs/puppetlabs-apache'\n\nOur puppet infrastructure repository depends on the `puppetlabs-apache` module,\nto be downloaded from GitHub tarball.\n\n    mod 'acme-mymodule', :path => './some_folder'\n\nOur puppet infrastructure repository depends on the `acme-mymodule` module,\nwhich is already in the filesystem.\n\n    exclusion 'acme-bad_module'\n\nExclude the module `acme-bad_module` from resolution and installation.\n\n## How to Use\n\nInstall librarian-puppet:\n\n    $ gem install librarian-puppet\n\nPrepare your puppet infrastructure repository:\n\n    $ cd ~/path/to/puppet-inf-repos\n    $ (git) rm -rf modules\n    $ librarian-puppet init\n\nLibrarian-puppet takes over your `modules/` directory, and will always\nreinstall (if missing) the modules listed the `Puppetfile.lock` into your\n`modules/` directory, therefore you do not need your `modules/` directory to be\ntracked in Git.\n\nLibrarian-puppet uses a `.tmp/` directory for tempfiles and caches. You should\nnot track this directory in Git.\n\nRunning `librarian-puppet init` will create a skeleton Puppetfile for you as\nwell as adding `tmp/` and `modules/` to your `.gitignore`.\n\n    $ librarian-puppet install [--clean] [--verbose]\n\nThis command looks at each `mod` declaration and fetches the module from the\nsource specified.  This command writes the complete resolution into\n`Puppetfile.lock` and then copies all of the fetched modules into your\n`modules/` directory, overwriting whatever was there before.\n\nLibrarian-puppet support both v1 and v3 of the Puppet Forge API.\nSpecify a specific API version when installing modules:\n\n    $ librarian-puppet install --use-v1-api # this is default; ignored for official Puppet Forge\n    $ librarian-puppet install --no-use-v1-api # use the v3 API; default for official Puppet Forge\n\nPlease note that this does not apply for the official Puppet Forge, where v3 is used by default.\n\nGet an overview of your `Puppetfile.lock` with:\n\n    $ librarian-puppet show\n\nInspect the details of specific resolved dependencies with:\n\n    $ librarian-puppet show NAME1 [NAME2, ...]\n\nFind out which dependencies are outdated and may be updated:\n\n    $ librarian-puppet outdated [--verbose]\n\nUpdate the version of a dependency:\n\n    $ librarian-puppet update apt [--verbose]\n    $ git diff Puppetfile.lock\n    $ git add Puppetfile.lock\n    $ git commit -m \"bumped the version of apt up to 0.0.4.\"\n\n## Configuration\n\nConfiguration comes from three sources with the following highest-to-lowest\nprecedence:\n\n* The local config (`./.librarian/puppet/config`)\n* The environment\n* The global config (`~/.librarian/puppet/config`)\n\nYou can inspect the final configuration with:\n\n    $ librarian-puppet config\n\nYou can find out where a particular key is set with:\n\n    $ librarian-puppet config KEY\n\nYou can set a key at the global level with:\n\n    $ librarian-puppet config KEY VALUE --global\n\nAnd remove it with:\n\n    $ librarian-puppet config KEY --global --delete\n\nYou can set a key at the local level with:\n\n    $ librarian-puppet config KEY VALUE --local\n\nAnd remove it with:\n\n    $ librarian-puppet config KEY --local --delete\n\nYou cannot set or delete environment-level config keys with the CLI.\n\nConfiguration set at either the global or local level will affect subsequent\ninvocations of `librarian-puppet`. Configurations set at the environment level are\nnot saved and will not affect subsequent invocations of `librarian-puppet`.\n\nYou can pass a config at the environment level by taking the original config key\nand transforming it: replace hyphens (`-`) with underscores (`_`) and periods\n(`.`) with doubled underscores (`__`), uppercase, and finally prefix with\n`LIBRARIAN_PUPPET_`. For example, to pass a config in the environment for the key\n`part-one.part-two`, set the environment variable\n`LIBRARIAN_PUPPET_PART_ONE__PART_TWO`.\n\nConfiguration affects how various commands operate.\n\n* The `path` config sets the directory to install to. If a relative\n  path, it is relative to the directory containing the `Puppetfile`. The\n  equivalent environment variable is `LIBRARIAN_PUPPET_PATH`.\n\n* The `tmp` config sets the cache directory for librarian. If a relative\n  path, it is relative to the directory containing the `Puppetfile`. The\n  equivalent environment variable is `LIBRARIAN_PUPPET_TMP`.\n\nConfiguration can be set by passing specific options to other commands.\n\n* The `path` config can be set at the local level by passing the `--path` option\n  to the `install` command. It can be unset at the local level by passing the\n  `--no-path` option to the `install` command. Note that if this is set at the\n  environment or global level then, even if `--no-path` is given as an option,\n  the environment or global config will be used.\n\n\n## Rsync Option\n\nThe default convergence strategy between the cache and the module directory is\nto execute an `rm -r` on the module directory and just `cp -r` from the cache.\nThis causes the module to be removed from the module path every time librarian\npuppet updates, regardless of whether the content has changed. This can cause\nsome problems in environments with lots of change. The problem arises when the\nmodule directory gets removed while Puppet is trying to read files inside it.\nThe `puppet master` process will lose its CWD and the catalog will fail to\ncompile. To avoid this, you can use `rsync` to implement a more conservative\nconvergence strategy. This will use `rsync` with the `-avz` and `--delete`\nflags instead of a `rm -r` and `cp -r`. To use this feature, just set the\n`rsync` configuration setting to `true`.\n\n    $ librarian-puppet config rsync true --global\n\nAlternatively, using an environment variable:\n\n    LIBRARIAN_PUPPET_RSYNC='true'\n\nNote that the directories will still be purged if you run librarian-puppet with\nthe --clean or --destructive flags.\n\n## How to Contribute\n\n * Pull requests please.\n * Bonus points for feature branches.\n\n## Reporting Issues\n\nBug reports to the github issue tracker please.\nPlease include:\n\n * Relevant `Puppetfile` and `Puppetfile.lock` files\n * Version of ruby, librarian-puppet, and puppet\n * What distro\n * Please run the `librarian-puppet` commands in verbose mode by using the\n  `--verbose` flag, and include the verbose output in the bug report as well.\n\n\n## License\nPlease see the [LICENSE](https://github.com/rodjek/librarian-puppet/blob/master/LICENSE)\nfile.\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'bundler/setup'\nrequire 'cucumber/rake/task'\nrequire 'rspec/core/rake_task'\nrequire 'bundler/gem_tasks'\nrequire 'rake/testtask'\nrequire 'rake/clean'\n\nCLEAN.include('pkg/', 'tmp/')\nCLOBBER.include('Gemfile.lock')\n\nRSpec::Core::RakeTask.new\nCucumber::Rake::Task.new(:features) do |t|\n  require 'puppet'\n  puppet_version = Puppet::version.gsub(\"~>\",\"\").split(\".\").first.to_i\n  tags = (2..4).select {|i| i != puppet_version}.map{|i| \"--tags @puppet#{puppet_version},~@puppet#{i}\"}\n  # don't run githubtarball scenarios in Travis, they easily fail with rate limit exceeded\n  tags << \"--tags ~@github\" if ENV['TRAVIS']=='true'\n  t.cucumber_opts = tags.join(\" \")\nend\n\nRake::TestTask.new do |test|\n  test.pattern = 'test/**/*_test.rb'\n  test.verbose = true\nend\n\ntask :default => [:test, :spec, :features]\n\ndesc \"Bump version to the next minor\"\ntask :bump do\n  path = 'lib/librarian/puppet/version.rb'\n  version_file = File.read(path)\n  version = version_file.match(/VERSION = \"(.*)\"/)[1]\n  v = Gem::Version.new(\"#{version}.0\")\n  new_version = v.bump.to_s\n  version_file = version_file.gsub(/VERSION = \".*\"/, \"VERSION = \\\"#{new_version}\\\"\")\n  File.open(path, \"w\") {|file| file.puts version_file}\n  sh \"git add #{path}\"\n  sh \"git commit -m \\\"Bump version to #{new_version}\\\"\"\nend\n"
  },
  {
    "path": "bin/librarian-puppet",
    "content": "#!/usr/bin/env ruby\n\nlib = File.expand_path('../../lib', __FILE__)\n$:.unshift(lib) unless $:.include?(lib)\n\nrequire 'librarian/puppet/cli'\nLibrarian::Puppet::Cli.bin!\n"
  },
  {
    "path": "features/examples/dependency_without_version/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/dependency_without_version/metadata.json",
    "content": "{\n  \"name\": \"librarian-test\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": [\n    {\n      \"name\": \"puppetlabs/stdlib\"\n    }\n  ]\n}\n"
  },
  {
    "path": "features/examples/duplicated_dependencies/Puppetfile",
    "content": "forge 'http://forge.puppetlabs.com'\n\nmetadata\n"
  },
  {
    "path": "features/examples/duplicated_dependencies/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/duplicated_dependencies/metadata.json",
    "content": "{\n  \"name\": \"librarian-duplicated_dependencies\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": [\n    {\n      \"name\": \"ripienaar-concat\",\n      \"version_requirement\": \">= 0\"\n    },\n    {\n      \"name\": \"puppetlabs-concat\",\n      \"version_requirement\": \"1.2.0\"\n    }\n  ]\n}\n"
  },
  {
    "path": "features/examples/duplicated_dependencies_transitive/Puppetfile",
    "content": "forge 'http://forge.puppetlabs.com'\n\nmetadata\n\nmod 'librarian-duplicated_dependencies', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/duplicated_dependencies'\n"
  },
  {
    "path": "features/examples/duplicated_dependencies_transitive/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/duplicated_dependencies_transitive/metadata.json",
    "content": "{\n  \"name\": \"librarian-duplicated_dependencies_transitive\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": []\n}\n"
  },
  {
    "path": "features/examples/metadata_syntax/Puppetfile",
    "content": "forge 'http://forge.puppetlabs.com'\n\nmetadata\n"
  },
  {
    "path": "features/examples/metadata_syntax/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/metadata_syntax/metadata.json",
    "content": "{\n  \"operatingsystem_support\": [\n    {\n      \"operatingsystem\": \"RedHat\",\n      \"operatingsystemrelease\": [\n        \"4\",\n        \"5\",\n        \"6\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"CentOS\",\n      \"operatingsystemrelease\": [\n        \"4\",\n        \"5\",\n        \"6\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"OracleLinux\",\n      \"operatingsystemrelease\": [\n        \"4\",\n        \"5\",\n        \"6\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"Scientific\",\n      \"operatingsystemrelease\": [\n        \"4\",\n        \"5\",\n        \"6\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"SLES\",\n      \"operatingsystemrelease\": [\n        \"11 SP1\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"Debian\",\n      \"operatingsystemrelease\": [\n        \"6\",\n        \"7\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"Ubuntu\",\n      \"operatingsystemrelease\": [\n        \"10.04\",\n        \"12.04\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"Solaris\",\n      \"operatingsystemrelease\": [\n        \"10\",\n        \"11\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"Windows\",\n      \"operatingsystemrelease\": [\n        \"Server 2003\",\n        \"Server 2003 R2\",\n        \"Server 2008\",\n        \"Server 2008 R2\",\n        \"Server 2012\",\n        \"Server 2012 R2\",\n        \"7\",\n        \"8\"\n      ]\n    },\n    {\n      \"operatingsystem\": \"AIX\",\n      \"operatingsystemrelease\": [\n        \"5.3\",\n        \"6.1\",\n        \"7.1\"\n      ]\n    }\n  ],\n  \"requirements\": [\n    {\n      \"name\": \"pe\",\n      \"version_requirement\": \"3.2.x\"\n    },\n    {\n      \"name\": \"puppet\",\n      \"version_requirement\": \">=2.7.20 <4.0.0\"\n    }\n  ],\n  \"name\": \"librarian-metadata_syntax\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": [\n    {\n      \"name\": \"maestrodev/test\",\n      \"version_requirement\": \">= 0.0.1\"\n    }\n  ]\n}\n"
  },
  {
    "path": "features/examples/modulefile_syntax/Modulefile",
    "content": "name 'librarian-modulefile_syntax'\nversion '0.0.1'\n\nauthor 'librarian'\nlicense 'Apache License, Version 2.0'\n\ndependency 'maestrodev/test'\n"
  },
  {
    "path": "features/examples/modulefile_syntax/Puppetfile",
    "content": "forge 'http://forge.puppetlabs.com'\n\nmodulefile\n"
  },
  {
    "path": "features/examples/modulefile_syntax/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/path_dependencies/Puppetfile",
    "content": "mod 'librarian/test', :path => '../../features/examples/test'\n"
  },
  {
    "path": "features/examples/path_dependencies/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/path_dependencies/metadata.json",
    "content": "{\n  \"name\": \"librarian-path_dependencies\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": [],\n  \"requirements\": []\n}\n"
  },
  {
    "path": "features/examples/test/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/test/metadata.json",
    "content": "{\n  \"name\": \"librarian-test\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": [\n    {\n      \"name\": \"puppetlabs/stdlib\",\n      \"version_requirement\": \">= 0\"\n    }\n  ]\n}\n"
  },
  {
    "path": "features/examples/with_puppetfile/Puppetfile",
    "content": "mod 'librarian/test', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/test'\n"
  },
  {
    "path": "features/examples/with_puppetfile/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/with_puppetfile/metadata.json",
    "content": "{\n  \"name\": \"librarian-with_puppetfile\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": []\n}\n"
  },
  {
    "path": "features/examples/with_puppetfile_and_metadata_json/Puppetfile",
    "content": "forge 'http://forge.puppetlabs.com'\n\nmod 'maestrodev/test'\n"
  },
  {
    "path": "features/examples/with_puppetfile_and_metadata_json/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/examples/with_puppetfile_and_metadata_json/metadata.json",
    "content": "{\n  \"name\": \"librarian-with_puppetfile_and_metadata_json\",\n  \"version\": \"0.0.1\",\n  \"license\": \"Apache 2.0\",\n  \"dependencies\": [\n    {\n      \"name\": \"maestrodev/test\",\n      \"version_requirement\": \">= 0\"\n    }\n  ]\n}\n"
  },
  {
    "path": "features/examples/with_puppetfile_and_modulefile/Modulefile",
    "content": "name 'librarian-with_puppetfile_and_modulefile'\nversion '0.0.1'\n\nauthor 'librarian'\nlicense 'Apache License, Version 2.0'\n\ndependency 'maestrodev/test'\n"
  },
  {
    "path": "features/examples/with_puppetfile_and_modulefile/Puppetfile",
    "content": "forge 'http://forge.puppetlabs.com'\n\nmod 'maestrodev/test'\n"
  },
  {
    "path": "features/examples/with_puppetfile_and_modulefile/manifests/init.pp",
    "content": "class test {}\n"
  },
  {
    "path": "features/help.feature",
    "content": "Feature: displays help if no subcommand is passed\n  In order to get started using librarian-puppet\n  A user should be able to run librarian-puppet without any subcommands or options\n  Then the exit status should be 0\n  And a useful help screen should be displayed\n\n  Scenario: App defaults to help subcommand\n    When I run `librarian-puppet`\n    Then the exit status should be 0\n    And the output should contain \"librarian-puppet version\"\n"
  },
  {
    "path": "features/init.feature",
    "content": "Feature: init subcommand should generate a Puppetfile\n  In order to start using librarian-puppet in a project\n  A project will need a Puppetfile.\n  If a user runs \"librarian-puppet init\"\n  Then the exit status should be 0\n  And a file named \"Puppetfile\" should exist\n\n\n  Scenario: init subcommand should generate a Puppetfile\n    When I run `librarian-puppet init`\n    Then the exit status should be 0\n    Then a file named \"Puppetfile\" should exist\n"
  },
  {
    "path": "features/install/forge.feature",
    "content": "Feature: cli/install/forge\n  Puppet librarian needs to install modules from the Puppet Forge\n\n  Scenario: Installing a module and its dependencies\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"https://forgeapi.puppetlabs.com\"\n\n    mod 'puppetlabs/ntp'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/ntp/metadata.json\" should match /\"name\": \"puppetlabs-ntp\"/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Running install with no Puppetfile and metadata.json\n    Given there is no Puppetfile\n    And a file named \"metadata.json\" with:\n    \"\"\"\n    {\n      \"name\": \"random name\",\n      \"dependencies\": [\n        {\n          \"name\": \"puppetlabs/stdlib\",\n          \"version_requirement\": \"4.1.0\"\n        }\n      ]\n    }\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Running install with no Puppetfile and Modulefile\n    Given there is no Puppetfile\n    And a file named \"Modulefile\" with:\n    \"\"\"\n    name \"random name\"\n    dependency \"puppetlabs/stdlib\", \"4.1.0\"\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Installing a module without forge\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'puppetlabs/stdlib', '4.1.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 1\n    And the output should contain \"forge entry is not defined in Puppetfile\"\n\n  Scenario: Installing an exact version of a module\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apt', '0.0.4'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/apt/Modulefile\" should match /name *'puppetlabs-apt'/\n    And the file \"modules/apt/Modulefile\" should match /version *'0\\.0\\.4'/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  # Puppet Module tool does not support spaces\n  # https://github.com/rodjek/librarian-puppet/issues/201\n  # https://tickets.puppetlabs.com/browse/PUP-2278\n  @spaces\n  Scenario: Installing a module in a path with spaces\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n    mod 'puppetlabs/stdlib', '4.1.0'\n    \"\"\"\n    When PENDING I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Installing a module with invalid versions in the forge\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apache', '0.4.0'\n    mod 'puppetlabs/postgresql', '2.0.1'\n    mod 'puppetlabs/apt', '< 1.4.1' # 1.4.2 causes trouble in travis\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/apache/Modulefile\" should match /name *'puppetlabs-apache'/\n    And the file \"modules/apache/Modulefile\" should match /version *'0\\.4\\.0'/\n    And the file \"modules/postgresql/Modulefile\" should match /name *'puppetlabs-postgresql'/\n    And the file \"modules/postgresql/Modulefile\" should match /version *'2\\.0\\.1'/\n\n  Scenario: Installing a module with several constraints\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apt', '>=1.0.0', '<1.0.1'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/apt/Modulefile\" should match /name *'puppetlabs-apt'/\n    And the file \"modules/apt/Modulefile\" should match /version *'1\\.0\\.0'/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Changing the path\n    Given a directory named \"puppet\"\n    And a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/ntp', '3.0.3'\n    \"\"\"\n    When I run `librarian-puppet install --path puppet/modules`\n    And I run `librarian-puppet config`\n    Then the exit status should be 0\n    And the output from \"librarian-puppet config\" should contain \"path: puppet/modules\"\n    And the file \"puppet/modules/ntp/Modulefile\" should match /name *'puppetlabs-ntp'/\n    And the file \"puppet/modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Handle range version numbers\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/postgresql', '3.2.0'\n    mod 'puppetlabs/apt', '< 1.4.1' # 1.4.2 causes trouble in travis\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/Modulefile\" should match /name 'puppetlabs-postgresql'/\n    And the file \"modules/postgresql/Modulefile\" should match /version '3\\.2\\.0'/\n\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/postgresql', :git => 'git://github.com/puppetlabs/puppet-postgresql', :ref => '3.3.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/Modulefile\" should match /name 'puppetlabs-postgresql'/\n    And the file \"modules/postgresql/Modulefile\" should match /version '3\\.3\\.0'/\n\n  Scenario: Installing a module that does not exist\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/xxxxx'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 1\n    And the output should match:\n      \"\"\"\n      Unable to find module 'puppetlabs-xxxxx' on http(s)?://forge(api)?.puppetlabs.com\n      \"\"\"\n\n  Scenario: Install a module with conflicts\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apache', '0.6.0'\n    mod 'puppetlabs/stdlib', '<2.2.1'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 1\n    And the output should contain \"Could not resolve the dependencies\"\n\n  Scenario: Install a module from the Forge with dependencies without version\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'sbadia/gitlab', '0.1.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/gitlab/Modulefile\" should match /version *'0\\.1\\.0'/\n\n  Scenario: Source dependencies from Modulefile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    modulefile\n    \"\"\"\n    And a file named \"Modulefile\" with:\n    \"\"\"\n    name \"random name\"\n    dependency \"puppetlabs/postgresql\", \"2.4.1\"\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/Modulefile\" should match /name *'puppetlabs-postgresql'/\n\n  Scenario: Source dependencies from metadata.json\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    metadata\n    \"\"\"\n    And a file named \"metadata.json\" with:\n    \"\"\"\n    {\n      \"name\": \"random name\",\n      \"dependencies\": [\n        {\n          \"name\": \"puppetlabs/postgresql\",\n          \"version_requirement\": \"2.4.1\"\n        }\n      ]\n    }\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/Modulefile\" should match /name *'puppetlabs-postgresql'/\n\n  Scenario: Source dependencies from Modulefile using dash instead of slash\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    modulefile\n    \"\"\"\n    And a file named \"Modulefile\" with:\n    \"\"\"\n    name \"random name\"\n    dependency \"puppetlabs-postgresql\", \"2.4.1\"\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/Modulefile\" should match /name *'puppetlabs-postgresql'/\n\n  Scenario: Installing a module with duplicated dependencies\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'pdxcat/collectd', '2.1.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/collectd/Modulefile\" should match /name *'pdxcat-collectd'/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Installing two modules with same name, alphabetical order wins\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'ripienaar-concat', '0.2.0'\n    mod 'puppetlabs-concat', '1.2.0'\n    \"\"\"\n    When I run `librarian-puppet install --verbose`\n    Then the exit status should be 0\n    And the file \"modules/concat/metadata.json\" should match /\"name\": \"ripienaar-concat\"/\n    And the output should contain \"Dependency on module 'concat' is fullfilled by multiple modules and only one will be used\"\n\n  @other-forge\n  Scenario: Installing from another forge with local reference should not try to download anything from the official forge\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://127.0.0.1\"\n\n    mod 'tester/tester', :path => './tester-tester'\n    \"\"\"\n    And a file named \"tester-tester/metadata.json\" with:\n    \"\"\"\n    {\n        \"name\": \"tester-tester\",\n        \"version\": \"0.1.0\",\n        \"author\": \"Basilio Vera\",\n        \"summary\": \"Just our own test\",\n        \"license\": \"MIT\",\n        \"dependencies\": [\n            { \"name\": \"puppetlabs/inifile\" },\n            { \"name\": \"tester/tester_dependency1\" }\n        ]\n    }\n    \"\"\"\n\n    When I run `librarian-puppet install --verbose`\n    And the output should not contain \"forgeapi.puppetlabs.com\"\n    And the output should contain \"Querying Forge API for module puppetlabs-inifile: http://127.0.0.1/api/v1/releases.json?module=puppetlabs/inifile\"\n"
  },
  {
    "path": "features/install/git.feature",
    "content": "Feature: cli/install/git\n  Puppet librarian needs to install modules from git repositories\n\n  Scenario: Installing a module from git \n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apache',\n        :git => 'https://github.com/puppetlabs/puppetlabs-apache.git', :ref => '1.4.0'\n\n    mod 'puppetlabs/stdlib',\n        :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git', :ref => '4.6.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/apache/metadata.json\" should match /\"name\": \"puppetlabs-apache\"/\n    And the file \"modules/apache/metadata.json\" should match /\"version\": \"1\\.4\\.0\"/\n    And the git revision of module \"apache\" should be \"e4ec6d4985fdb23e26c809e0d5786823d0689f90\"\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the file \"modules/stdlib/metadata.json\" should match /\"version\": \"4\\.6\\.0\"/\n    And the git revision of module \"stdlib\" should be \"73474b00b5ae3cbccec6cd0711311d6450139e51\"\n\n  @spaces\n  Scenario: Installing a module in a path with spaces\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'puppetlabs/stdlib', '4.6.0', :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git', :ref => '4.6.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Installing a module with invalid versions in git\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod \"apache\",\n      :git => \"https://github.com/puppetlabs/puppetlabs-apache.git\", :ref => \"1.4.0\"\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/apache/metadata.json\" should match /\"name\": \"puppetlabs-apache\"/\n    And the file \"modules/apache/metadata.json\" should match /\"version\": \"1\\.4\\.0\"/\n\n  Scenario: Switching a module from forge to git\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/postgresql', '4.0.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/metadata.json\" should match /\"name\": \"puppetlabs-postgresql\"/\n    And the file \"modules/postgresql/metadata.json\" should match /\"version\": \"4\\.0\\.0\"/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    When I overwrite \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/postgresql',\n      :git => 'https://github.com/puppetlabs/puppetlabs-postgresql.git', :ref => '4.3.0'\n    \"\"\"\n    And I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/postgresql/metadata.json\" should match /\"name\": \"puppetlabs-postgresql\"/\n    And the file \"modules/postgresql/metadata.json\" should match /\"version\": \"4\\.3\\.0\"/\n    And the file \"modules/postgresql/.git/HEAD\" should match /9ca4b42450ea9c9ed8eec52dac48cb67187ae925/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n\n  Scenario: Install a module with dependencies specified in metadata.json\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'puppetlabs-apt', :git => 'https://github.com/puppetlabs/puppetlabs-apt.git', :ref => '1.5.2'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the file \"modules/apt/metadata.json\" should match /\"name\": \"puppetlabs-apt\"/\n\n  Scenario: Install a module with dependencies specified in a Puppetfile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/with_puppetfile', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/with_puppetfile'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/with_puppetfile/metadata.json\" should match /\"name\": \"librarian-with_puppetfile\"/\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"librarian-test\"/\n\n  Scenario: Install a module with dependencies specified in a Puppetfile and Modulefile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/with_puppetfile', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/with_puppetfile_and_modulefile'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/with_puppetfile/Modulefile\" should match /name *'librarian-with_puppetfile_and_modulefile'/\n    And the file \"modules/test/Modulefile\" should match /name *'maestrodev-test'/\n\n  Scenario: Install a module with dependencies specified in a Puppetfile and metadata.json\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/with_puppetfile', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/with_puppetfile_and_metadata_json'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/with_puppetfile/metadata.json\" should match /\"name\": \"librarian-with_puppetfile_and_metadata_json\"/\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"maestrodev-test\"/\n\n  Scenario: Running install with no Modulefile nor metadata.json\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/stdlib', :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git', :ref => '4.6.0'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n\n  Scenario: Running install with metadata.json without dependencies\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/sqlite', :git => 'https://github.com/puppetlabs/puppetlabs-sqlite.git', :ref => '84a0a6'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n\n  Scenario: Install a module using modulefile syntax\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/modulefile_syntax', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/modulefile_syntax'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/modulefile_syntax/Modulefile\" should match /name *'librarian-modulefile_syntax'/\n    And the file \"modules/test/Modulefile\" should match /name *'maestrodev-test'/\n\n  Scenario: Install a module using metadata syntax\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/metadata_syntax', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/metadata_syntax'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/metadata_syntax/metadata.json\" should match /\"name\": \"librarian-metadata_syntax\"/\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"maestrodev-test\"/\n\n  Scenario: Install a module from git and using path\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'librarian-test', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/test'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/test/metadata.json\" should match /\"version\": \"0\\.0\\.1\"/\n    And a file named \"modules/stdlib/metadata.json\" should exist\n\n  Scenario: Install a module from git without version\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'test', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/dependency_without_version'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/test/metadata.json\" should match /\"version\": \"0\\.0\\.1\"/\n    And a file named \"modules/stdlib/metadata.json\" should exist\n\n  Scenario: Install a module with mismatching Puppetfile and Modulefile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'duritong/munin', :git => 'https://github.com/duritong/puppet-munin.git', :ref => '0bb71e'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/munin/Modulefile\" should match /name *'duritong-munin'/\n    And the file \"modules/concat/metadata.json\" should match /\"name\": *\"puppetlabs-concat\"/\n    And a file named \"modules/stdlib/metadata.json\" should exist\n\n  Scenario: Install from Puppetfile with duplicated entries\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'puppetlabs-stdlib',\n      :git => 'git://github.com/puppetlabs/puppetlabs-stdlib.git'\n\n    mod 'puppetlabs-stdlib',\n      :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the output should contain \"Dependency 'puppetlabs-stdlib' duplicated for module, merging\"\n"
  },
  {
    "path": "features/install/github_tarball.feature",
    "content": "Feature: cli/install/github_tarball\n  Puppet librarian needs to install tarballed modules from github repositories\n\n  @github\n  Scenario: Installing a module from github tarballs\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apache', '0.6.0', :github_tarball => 'puppetlabs/puppetlabs-apache'\n    mod 'puppetlabs/stdlib', '2.3.0', :github_tarball => 'puppetlabs/puppetlabs-stdlib'\n    \"\"\"\n    When I run `librarian-puppet install --verbose`\n    Then the exit status should be 0\n    And the output should contain \"Downloading <https://api.github.com/repos/puppetlabs/puppetlabs-apache/tarball/0.6.0\"\n    And the output should contain \"Downloading <https://api.github.com/repos/puppetlabs/puppetlabs-stdlib/tarball/2.3.0\"\n    And the file \"modules/apache/Modulefile\" should match /name *'puppetlabs-apache'/\n    And the file \"modules/apache/Modulefile\" should match /version *'0\\.6\\.0'/\n    And the file \"modules/stdlib/Modulefile\" should match /name *'puppetlabs-stdlib'/\n    And the file \"modules/stdlib/Modulefile\" should match /version *'2\\.3\\.0'/\n\n  @spaces\n  @github\n  Scenario: Installing a module in a path with spaces\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'puppetlabs/stdlib', '4.1.0', :github_tarball => 'puppetlabs/puppetlabs-stdlib'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/Modulefile\" should match /name *'puppetlabs-stdlib'/\n"
  },
  {
    "path": "features/install/path.feature",
    "content": "Feature: cli/install/path\n  Puppet librarian needs to install modules from local paths\n\n  Scenario: Install a module with dependencies specified in a Puppetfile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/with_puppetfile', :path => '../../features/examples/with_puppetfile'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/with_puppetfile/metadata.json\" should match /\"name\": \"librarian-with_puppetfile\"/\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"librarian-test\"/\n\n  Scenario: Install a module with recursive path dependencies\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/path_dependencies', :path => '../../features/examples/path_dependencies'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/path_dependencies/metadata.json\" should match /\"name\": \"librarian-path_dependencies\"/\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"librarian-test\"/\n    And a file named \"modules/stdlib/metadata.json\" should exist\n\n  Scenario: Install a module with dependencies specified in a Puppetfile and Modulefile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/with_puppetfile', :path => '../../features/examples/with_puppetfile_and_modulefile'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/with_puppetfile/Modulefile\" should match /name *'librarian-with_puppetfile_and_modulefile'/\n    And the file \"modules/test/Modulefile\" should match /name *'maestrodev-test'/\n\n  Scenario: Install a module with dependencies specified in a Puppetfile and metadata.json\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/with_puppetfile', :path => '../../features/examples/with_puppetfile_and_metadata_json'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/with_puppetfile/metadata.json\" should match /\"name\": \"librarian-with_puppetfile_and_metadata_json\"/\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"maestrodev-test\"/\n\n  Scenario: Install a module from path without version\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'test', :path => '../../features/examples/dependency_without_version'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/test/metadata.json\" should match /\"version\": \"0\\.0\\.1\"/\n    And a file named \"modules/stdlib/metadata.json\" should exist\n\n  @spaces\n  Scenario: Installing a module in a path with spaces\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    mod 'librarian/test', :path => '../../features/examples/test'\n    mod 'puppetlabs/stdlib', :git => 'https://github.com/puppetlabs/puppetlabs-stdlib'\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"modules/test/metadata.json\" should match /\"name\": \"librarian-test\"/\n"
  },
  {
    "path": "features/install.feature",
    "content": "Feature: cli/install\n  In order to be worth anything\n  Puppet librarian needs to install modules properly\n\n  Scenario: Running install with no Puppetfile nor metadata.json\n    Given there is no Puppetfile\n    When I run `librarian-puppet install`\n    Then the output should match /^Metadata file does not exist: .*metadata.json$/\n    And the exit status should be 1\n\n  Scenario: Running install with bad metadata.json\n    Given a file named \"metadata.json\" with:\n    \"\"\"\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the output should match /^Unable to parse json file .*metadata.json: .*$/\n    And the exit status should be 1\n\n  Scenario: Install a module transitive dependency from git and forge should be deterministic\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/stdlib', :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git', :ref => '4.6.0'\n    mod 'librarian/test', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/test'\n    \"\"\"\n    When I run `librarian-puppet install --verbose`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"version\": \"4\\.6\\.0\"/\n    And the output should not match /Executing puppet module install for puppetlabs.stdlib/\n\n  Scenario: Install duplicated dependencies from git and forge, last one wins\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    metadata\n    mod 'puppetlabs-stdlib', :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git', :ref => '4.6.0'\n    \"\"\"\n    And a file named \"metadata.json\" with:\n    \"\"\"\n    {\n      \"name\": \"random name\",\n      \"dependencies\": [\n        {\n          \"name\": \"puppetlabs/stdlib\",\n          \"version_requirement\": \">= 0\"\n        }\n      ]\n    }\n    \"\"\"\n    When I run `librarian-puppet install --verbose`\n    Then the exit status should be 0\n    And the file \"modules/stdlib/metadata.json\" should match /\"version\": \"4\\.6\\.0\"/\n    And the output should not match /Executing puppet module install for puppetlabs.stdlib/\n\n  Scenario: Installing two modules with same name and using exclusions\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"https://forgeapi.puppetlabs.com\"\n\n    mod 'librarian-duplicated_dependencies', :path => '../../features/examples/duplicated_dependencies'\n    exclusion 'ripienaar-concat'\n    \"\"\"\n    When I run `librarian-puppet install --verbose`\n    Then the exit status should be 0\n    And the file \"modules/concat/metadata.json\" should match /\"name\": \"puppetlabs-concat\"/\n    And the output should contain \"Excluding dependency ripienaar-concat from\"\n\n  Scenario: Installing two modules with same name and using exclusions, apply transitively\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"https://forgeapi.puppetlabs.com\"\n\n    mod 'librarian-duplicated_dependencies_transitive', :path => '../../features/examples/duplicated_dependencies_transitive'\n    \"\"\"\n    When PENDING I run `librarian-puppet install --verbose`\n    Then the exit status should be 0\n    And the file \"modules/concat/metadata.json\" should match /\"name\": \"puppetlabs-concat\"/\n\n  @puppet2 @puppet3\n  Scenario: Install a module with Modulefile without version\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'librarian-bad_modulefile', :path => 'bad_modulefile'\n    \"\"\"\n    And a directory named \"bad_modulefile/manifests\"\n    And a file named \"bad_modulefile/Modulefile\" with:\n    \"\"\"\n    # bad Modulefile\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the output should match:\n    \"\"\"\n    Unable to parse .*/bad_modulefile/Modulefile, ignoring: Missing version\n    \"\"\"\n\n  Scenario: Install a module with the rsync configuration using the --clean flag\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'maestrodev/test'\n    \"\"\"\n    And a file named \".librarian/puppet/config\" with:\n    \"\"\"\n    ---\n    LIBRARIAN_PUPPET_RSYNC: 'true'\n    \"\"\"\n    When I run `librarian-puppet config`\n    Then the exit status should be 0\n    And the output should contain \"rsync: true\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should have an inode and ctime\n    When I run `librarian-puppet install --clean`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should not have the same inode or ctime as before\n\n  Scenario: Install a module with the rsync configuration using the --destructive flag\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'maestrodev/test'\n    \"\"\"\n    And a file named \".librarian/puppet/config\" with:\n    \"\"\"\n    ---\n    LIBRARIAN_PUPPET_RSYNC: 'true'\n    \"\"\"\n    When I run `librarian-puppet config`\n    Then the exit status should be 0\n    And the output should contain \"rsync: true\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should have an inode and ctime\n    Given I wait for 1 second\n    When I run `librarian-puppet install --destructive`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should not have the same inode or ctime as before\n\n  Scenario: Install a module with the rsync configuration\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'maestrodev/test'\n    \"\"\"\n    And a file named \".librarian/puppet/config\" with:\n    \"\"\"\n    ---\n    LIBRARIAN_PUPPET_RSYNC: 'true'\n    \"\"\"\n    When I run `librarian-puppet config`\n    Then the exit status should be 0\n    And the output should contain \"rsync: true\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should have an inode and ctime\n    Given I wait for 1 second\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should have the same inode and ctime as before\n"
  },
  {
    "path": "features/outdated.feature",
    "content": "Feature: cli/outdated\n  Puppet librarian needs to print outdated modules\n\n  Scenario: Running outdated with forge modules\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/stdlib', '>=3.1.x'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    DEPENDENCIES\n      puppetlabs/stdlib (~> 3.0)\n    \"\"\"\n    When I run `librarian-puppet outdated`\n    Then the exit status should be 0\n    And the output should match:\n    \"\"\"\n    ^puppetlabs-stdlib \\(3\\.1\\.0 -> [\\.\\d]+\\)$\n    \"\"\"\n\n  Scenario: Running outdated with git modules\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'test', :git => 'https://github.com/rodjek/librarian-puppet.git', :path => 'features/examples/test'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    GIT\n      remote: https://github.com/rodjek/librarian-puppet.git\n      path: features/examples/test\n      ref: master\n      sha: 10fdf98190a7a22e479628b3616f17f48a857e81\n      specs:\n        test (0.0.1)\n          puppetlabs/stdlib (>= 0)\n\n    DEPENDENCIES\n      test (>= 0)\n    \"\"\"\n    When I run `librarian-puppet outdated`\n    Then the exit status should be 0\n    And PENDING the output should match:\n    # \"\"\"\n    # ^puppetlabs-stdlib \\(3\\.1\\.0 -> [\\.\\d]+\\)$\n    # ^test .*$\n    # \"\"\"\n"
  },
  {
    "path": "features/package.feature",
    "content": "Feature: cli/package\n  Puppet librarian needs to package modules\n\n  Scenario: Packaging a forge module\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apt', '1.4.0'\n    mod 'puppetlabs/stdlib', '4.1.0'\n    \"\"\"\n    When I run `librarian-puppet package --verbose`\n    Then the exit status should be 0\n    And the file \"modules/apt/Modulefile\" should match /name *'puppetlabs-apt'/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the following files should exist:\n      | vendor/puppet/cache/puppetlabs-apt-1.4.0.tar.gz    |\n      | vendor/puppet/cache/puppetlabs-stdlib-4.1.0.tar.gz |\n\n  Scenario: Packaging a git module\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apt', '1.5.0', :git => 'https://github.com/puppetlabs/puppetlabs-apt.git', :ref => '1.5.0'\n    mod 'puppetlabs/stdlib', '4.1.0'\n    \"\"\"\n    When I run `librarian-puppet package --verbose`\n    Then the exit status should be 0\n    And the file \"modules/apt/Modulefile\" should match /name *'puppetlabs-apt'/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the following files should exist:\n      | vendor/puppet/source/e5657a61b9ac0dd3c00002c777b0d3c615bb98a5.tar.gz |\n      | vendor/puppet/cache/puppetlabs-stdlib-4.1.0.tar.gz                   |\n\n  @github\n  Scenario: Packaging a github tarball module\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/apt', '1.4.0', :github_tarball => 'puppetlabs/puppetlabs-apt'\n    mod 'puppetlabs/stdlib', '4.1.0'\n    \"\"\"\n    When I run `librarian-puppet package --verbose`\n    Then the exit status should be 0\n    And the file \"modules/apt/Modulefile\" should match /name *'puppetlabs-apt'/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the following files should exist:\n      | vendor/puppet/cache/puppetlabs-puppetlabs-apt-1.4.0.tar.gz |\n      | vendor/puppet/cache/puppetlabs-stdlib-4.1.0.tar.gz         |\n"
  },
  {
    "path": "features/step_definitions/convergence_steps.rb",
    "content": "Then /^the file \"([^\"]*)\" should have an inode and ctime$/ do |file|\n    cd('.') do\n        stat = File.stat(File.expand_path(file))\n        @before_inode = { 'ino' => stat.ino, 'ctime' => stat.ctime }\n        expect(@before_inode['ino']).not_to eq nil\n        expect(@before_inode['ctime']).not_to eq nil\n    end\nend\n\nThen /^the file \"([^\"]*)\" should have the same inode and ctime as before$/ do |file|\n    cd('.') do\n        stat = File.stat(File.expand_path(file))\n        expect(stat.ino).to eq @before_inode['ino']\n        expect(stat.ctime).to eq @before_inode['ctime']\n    end\nend\n\nThen /^the file \"([^\"]*)\" should not have the same inode or ctime as before$/ do |file|\n    cd('.') do\n        stat = File.stat(File.expand_path(file))\n\n        begin\n            expect(stat.ino).not_to eq @before_inode['ino']\n        rescue RSpec::Expectations::ExpectationNotMetError\n            expect(stat.ctime).not_to eq @before_inode['ctime']\n        end\n    end\nend\n\nThen /^the git revision of module \"([^\"]*)\" should be \"([0-9a-f]*)\"$/ do |module_name, rev|\n    cd(\"modules/#{module_name}\")\n    cmd = \"git rev-parse HEAD\"\n    run_simple(cmd)\n    assert_exact_output(rev, output_from(cmd).strip)\n    cd(\"../..\")\nend\n\nGiven /^I wait for (\\d+) seconds?$/ do |n|\n  sleep(n.to_i)\nend\n"
  },
  {
    "path": "features/support/env.rb",
    "content": "require 'aruba/cucumber'\nrequire 'fileutils'\n\nBefore do\n  @aruba_timeout_seconds = 120\nend\n\nBefore('@spaces') do\n  @dirs = [\"tmp/aruba with spaces\"]\n  @dirs.each {|dir| FileUtils.rm_rf dir}\nend\n\nGiven /^PENDING/ do\n  pending\nend\n\nGiven(/^there is no Puppetfile$/) do\n  in_current_dir do\n    fail \"Puppetfile exists at #{File.expand_path('Puppetfile')}\" if (File.exist?('Puppetfile'))\n  end\nend\n\nENV['LIBRARIAN_PUPPET_TMP'] = '.tmp'\n"
  },
  {
    "path": "features/update.feature",
    "content": "Feature: cli/update\n  Puppet librarian needs to update modules properly\n\n  Scenario: Updating a module with no Puppetfile and with metadata.json\n    Given a file named \"metadata.json\" with:\n    \"\"\"\n    {\n      \"name\": \"random name\",\n      \"dependencies\": [\n        {\n          \"name\": \"puppetlabs/stdlib\",\n          \"version_requirement\": \"3.1.x\"\n        }\n      ]\n    }\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    DEPENDENCIES\n      puppetlabs/stdlib (~> 3.0)\n    \"\"\"\n    When I run `librarian-puppet update puppetlabs/stdlib`\n    Then the exit status should be 0\n    And the file \"Puppetfile\" should not exist\n    And the file \"Puppetfile.lock\" should match /puppetlabs.stdlib \\(3\\.1\\.1\\)/\n    And the file \"modules/stdlib/Modulefile\" should match /name *'puppetlabs-stdlib'/\n    And the file \"modules/stdlib/Modulefile\" should match /version *'3\\.1\\.1'/\n\n  Scenario: Updating a module with no Puppetfile and with Modulefile\n    Given a file named \"Modulefile\" with:\n    \"\"\"\n    name \"random name\"\n    dependency \"puppetlabs/stdlib\", \"3.1.x\"\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    DEPENDENCIES\n      puppetlabs/stdlib (~> 3.0)\n    \"\"\"\n    When I run `librarian-puppet update puppetlabs/stdlib`\n    Then the exit status should be 0\n    And the file \"Puppetfile\" should not exist\n    And the file \"Puppetfile.lock\" should match /puppetlabs.stdlib \\(3\\.1\\.1\\)/\n    And the file \"modules/stdlib/Modulefile\" should match /name *'puppetlabs-stdlib'/\n    And the file \"modules/stdlib/Modulefile\" should match /version *'3\\.1\\.1'/\n\n  Scenario: Updating a module\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/stdlib', '3.1.x'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    DEPENDENCIES\n      puppetlabs/stdlib (~> 3.0)\n    \"\"\"\n    When I run `librarian-puppet update puppetlabs-stdlib`\n    Then the exit status should be 0\n    And the file \"Puppetfile.lock\" should match /puppetlabs.stdlib \\(3\\.1\\.1\\)/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the file \"modules/stdlib/Modulefile\" should match /version *'3\\.1\\.1'/\n\n  Scenario: Updating a module using organization/module\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/stdlib', '3.1.x'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    DEPENDENCIES\n      puppetlabs/stdlib (~> 3.0)\n    \"\"\"\n    When I run `librarian-puppet update --verbose puppetlabs/stdlib`\n    Then the exit status should be 0\n    And the file \"Puppetfile.lock\" should match /puppetlabs.stdlib \\(3\\.1\\.1\\)/\n    And the file \"modules/stdlib/metadata.json\" should match /\"name\": \"puppetlabs-stdlib\"/\n    And the file \"modules/stdlib/Modulefile\" should match /version *'3\\.1\\.1'/\n\n  Scenario: Updating a module from git with a branch ref\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod \"puppetlabs-stdlib\",\n      :git => \"https://github.com/puppetlabs/puppetlabs-stdlib.git\", :ref => \"3.2.x\"\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    GIT\n      remote: https://github.com/puppetlabs/puppetlabs-stdlib.git\n      ref: 3.2.x\n      sha: 326a8fd801ecba11005189c10ca8749872ef6577\n      specs:\n        puppetlabs-stdlib (3.2.1)\n\n    DEPENDENCIES\n      puppetlabs-stdlib (>= 0)\n    \"\"\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the git revision of module \"stdlib\" should be \"326a8fd801ecba11005189c10ca8749872ef6577\"\n    When I run `librarian-puppet update`\n    Then the exit status should be 0\n    And the git revision of module \"stdlib\" should be \"c0b5ce3b015db9f50d486040c16d8de56c6b4991\"\n\n  Scenario: Updating a module with invalid versions in git\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod \"apache\",\n      :git => \"https://github.com/puppetlabs/puppetlabs-apache.git\", :ref => \"0.5.0-rc1\"\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/firewall (0.0.4)\n        puppetlabs/stdlib (3.2.0)\n\n    GIT\n      remote: https://github.com/puppetlabs/puppetlabs-apache.git\n      ref: 0.5.0-rc1\n      sha: 94ebca3aaaf2144a7b9ce7ca6a13837ec48a7e2a\n      specs:\n        apache ()\n          puppetlabs/firewall (>= 0.0.4)\n          puppetlabs/stdlib (>= 2.2.1)\n\n    DEPENDENCIES\n      apache (>= 0)\n    \"\"\"\n    When I run `librarian-puppet update apache`\n    Then the exit status should be 0\n    And the file \"Puppetfile.lock\" should match /sha: d81999533af54a6fe510575d3b143308184a5005/\n    And the file \"modules/apache/Modulefile\" should match /name *'puppetlabs-apache'/\n    And the file \"modules/apache/Modulefile\" should match /version *'0\\.5\\.0-rc1'/\n\n  Scenario: Updating a module that is not in the Puppetfile\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'puppetlabs/stdlib', '3.1.x'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        puppetlabs/stdlib (3.1.0)\n\n    DEPENDENCIES\n      puppetlabs/stdlib (~> 3.0)\n    \"\"\"\n    When I run `librarian-puppet update stdlib`\n    Then the exit status should be 1\n    And the output should contain \"Unable to find module stdlib\"\n\n  Scenario: Updating a module to a .10 release to ensure versions are correctly ordered\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'maestrodev/test'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        maestrodev/test (1.0.2)\n\n    DEPENDENCIES\n      maestrodev/test (>= 0)\n    \"\"\"\n    When I run `librarian-puppet update --verbose`\n    Then the exit status should be 0\n    And the file \"Puppetfile.lock\" should match /maestrodev.test \\(1\\.0\\.[1-9][0-9]\\)/\n    And the file \"modules/test/Modulefile\" should contain \"name 'maestrodev-test'\"\n    And the file \"modules/test/Modulefile\" should match /version '1\\.0\\.[1-9][0-9]'/\n\n  Scenario: Updating a forge module with the rsync configuration\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod 'maestrodev/test'\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    FORGE\n      remote: http://forge.puppetlabs.com\n      specs:\n        maestrodev/test (1.0.2)\n\n    DEPENDENCIES\n      maestrodev/test (>= 0)\n      \"\"\"\n    And a file named \".librarian/puppet/config\" with:\n    \"\"\"\n    ---\n    LIBRARIAN_PUPPET_RSYNC: 'true'\n    \"\"\"\n    When I run `librarian-puppet config`\n    Then the exit status should be 0\n    And the output should contain \"rsync: true\"\n    When I run `librarian-puppet update --verbose`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should have an inode and ctime\n    When I run `librarian-puppet update --verbose`\n    Then the exit status should be 0\n    And a directory named \"modules/test\" should exist\n    And the file \"modules/test\" should have the same inode and ctime as before\n\n  @announce\n  Scenario: Updating a git module with the rsync configuration\n    Given a file named \"Puppetfile\" with:\n    \"\"\"\n    forge \"http://forge.puppetlabs.com\"\n\n    mod \"puppetlabs-stdlib\",\n      :git => \"https://github.com/puppetlabs/puppetlabs-stdlib.git\", :ref => \"3.2.x\"\n    \"\"\"\n    And a file named \"Puppetfile.lock\" with:\n    \"\"\"\n    GIT\n      remote: https://github.com/puppetlabs/puppetlabs-stdlib.git\n      ref: 3.2.x\n      sha: 326a8fd801ecba11005189c10ca8749872ef6577\n      specs:\n        puppetlabs-stdlib (3.2.1)\n\n    DEPENDENCIES\n      puppetlabs-stdlib (>= 0)\n    \"\"\"\n    And a file named \".librarian/puppet/config\" with:\n    \"\"\"\n    ---\n    LIBRARIAN_PUPPET_RSYNC: 'true'\n    \"\"\"\n    When I run `librarian-puppet config`\n    Then the exit status should be 0\n    And the output should contain \"rsync: true\"\n    When I run `librarian-puppet install`\n    Then the exit status should be 0\n    And the file \"Puppetfile.lock\" should contain \"326a8fd801ecba11005189c10ca8749872ef6577\"\n    And the git revision of module \"stdlib\" should be \"326a8fd801ecba11005189c10ca8749872ef6577\"\n    And a directory named \"modules/stdlib\" should exist\n    When I run `librarian-puppet update --verbose`\n    Then the exit status should be 0\n    And a directory named \"modules/stdlib\" should exist\n    And the file \"modules/stdlib\" should have an inode and ctime\n    And the file \"Puppetfile.lock\" should contain \"c0b5ce3b015db9f50d486040c16d8de56c6b4991\"\n    And the git revision of module \"stdlib\" should be \"c0b5ce3b015db9f50d486040c16d8de56c6b4991\"\n    When I run `librarian-puppet update --verbose`\n    Then the exit status should be 0\n    And a directory named \"modules/stdlib\" should exist\n    And the file \"modules/stdlib\" should have the same inode and ctime as before\n    And the file \"Puppetfile.lock\" should contain \"c0b5ce3b015db9f50d486040c16d8de56c6b4991\"\n    And the git revision of module \"stdlib\" should be \"c0b5ce3b015db9f50d486040c16d8de56c6b4991\"\n"
  },
  {
    "path": "features/version.feature",
    "content": "Feature: cli/version\n\n  Scenario: Getting the version\n    When I run `librarian-puppet version`\n    Then the exit status should be 0\n    And the output should contain \"librarian-\"\n"
  },
  {
    "path": "lib/librarian/puppet/action/install.rb",
    "content": "require 'librarian/action/install'\n\nmodule Librarian\n  module Puppet\n    module Action\n      class Install < Librarian::Action::Install\n\n        private\n\n        def create_install_path\n          install_path.rmtree if install_path.exist? && destructive?\n          install_path.mkpath\n        end\n\n        def destructive?\n          environment.config_db.local['destructive'] == 'true'\n        end\n\n        def check_specfile\n          # don't fail if Puppetfile doesn't exist as we'll use the Modulefile or metadata.json\n        end\n\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/action/resolve.rb",
    "content": "require 'librarian/action/resolve'\n\nmodule Librarian\n  module Puppet\n    module Action\n      class Resolve < Librarian::Action::Resolve\n        include Librarian::Puppet::Util\n\n        def run\n          super\n          manifests = environment.lock.manifests.select{ |m| m.name }\n          dupes = manifests.group_by{ |m| module_name(m.name) }.select { |k, v| v.size > 1 }\n          dupes.each do |k,v|\n            warn(\"Dependency on module '#{k}' is fullfilled by multiple modules and only one will be used: #{v.map{|m|m.name}}\")\n          end\n        end\n\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/action.rb",
    "content": "require \"librarian/puppet/action/install\"\nrequire \"librarian/puppet/action/resolve\"\n"
  },
  {
    "path": "lib/librarian/puppet/cli.rb",
    "content": "require 'librarian/helpers'\n\nrequire 'librarian/cli'\nrequire 'librarian/puppet'\nrequire 'librarian/puppet/action'\n\nmodule Librarian\n  module Puppet\n    class Cli < Librarian::Cli\n      include Librarian::Puppet::Util\n\n      module Particularity\n        def root_module\n          Puppet\n        end\n      end\n\n      include Particularity\n      extend Particularity\n\n      source_root Pathname.new(__FILE__).dirname.join(\"templates\")\n\n      def init\n        copy_file environment.specfile_name\n\n        if File.exists? \".gitignore\"\n          gitignore = File.read('.gitignore').split(\"\\n\")\n        else\n          gitignore = []\n        end\n\n        gitignore << \".tmp/\" unless gitignore.include? \".tmp/\"\n        gitignore << \"modules/\" unless gitignore.include? \"modules/\"\n\n        File.open(\".gitignore\", 'w') do |f|\n          f.puts gitignore.join(\"\\n\")\n        end\n      end\n\n      desc \"install\", \"Resolves and installs all of the dependencies you specify.\"\n      option \"quiet\", :type => :boolean, :default => false\n      option \"verbose\", :type => :boolean, :default => false\n      option \"line-numbers\", :type => :boolean, :default => false\n      option \"clean\", :type => :boolean, :default => false\n      option \"strip-dot-git\", :type => :boolean\n      option \"path\", :type => :string\n      option \"destructive\", :type => :boolean, :default => false\n      option \"local\", :type => :boolean, :default => false\n      option \"use-v1-api\", :type => :boolean, :default => true\n      def install\n\n        ensure!\n        clean! if options[\"clean\"]\n        unless options[\"destructive\"].nil?\n          environment.config_db.local['destructive'] = options['destructive'].to_s\n        end\n        if options.include?(\"strip-dot-git\")\n          strip_dot_git_val = options[\"strip-dot-git\"] ? \"1\" : nil\n          environment.config_db.local[\"install.strip-dot-git\"] = strip_dot_git_val\n        end\n        if options.include?(\"path\")\n          environment.config_db.local[\"path\"] = options[\"path\"]\n        end\n\n        environment.config_db.local['use-v1-api'] = options['use-v1-api'] ? '1' : nil\n        environment.config_db.local['mode'] = options['local'] ? 'local' : nil\n\n        resolve!\n        debug { \"Install: dependencies resolved\"}\n        install!\n      end\n\n      # only used to replace / to - in the module names\n      def update(*names)\n        warn(\"Usage of module/name is deprecated, use module-name\") if names.any? {|n| n.include?(\"/\")}\n        super(*names.map{|n| normalize_name(n)})\n      end\n\n      desc \"package\", \"Cache the puppet modules in vendor/puppet/cache.\"\n      option \"quiet\", :type => :boolean, :default => false\n      option \"verbose\", :type => :boolean, :default => false\n      option \"line-numbers\", :type => :boolean, :default => false\n      option \"clean\", :type => :boolean, :default => false\n      option \"strip-dot-git\", :type => :boolean\n      option \"path\", :type => :string\n      option \"destructive\", :type => :boolean, :default => false\n      def package\n        environment.vendor!\n        install\n      end\n\n      def version\n        say \"librarian-puppet v#{Librarian::Puppet::VERSION}\"\n      end\n\n      private\n\n      # override the actions to use our own\n\n      def install!(options = { })\n        Action::Install.new(environment, options).run\n      end\n      def resolve!(options = { })\n        Action::Resolve.new(environment, options).run\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/dependency.rb",
    "content": "module Librarian\n  module Puppet\n\n    class Dependency < Librarian::Dependency\n\n      include Librarian::Puppet::Util\n\n      def initialize(name, requirement, source)\n        # Issue #235 fail if forge source is not defined\n        raise Error, \"forge entry is not defined in Puppetfile\" if source.instance_of?(Array) && source.empty?\n\n        super(normalize_name(name), requirement, source)\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/dsl.rb",
    "content": "require 'librarian/dsl'\nrequire 'librarian/dsl/target'\nrequire 'librarian/puppet/source'\nrequire 'librarian/puppet/dependency'\n\nmodule Librarian\n  module Puppet\n    class Dsl < Librarian::Dsl\n\n      FORGE_URL = \"https://forgeapi.puppetlabs.com\"\n\n      dependency :mod\n\n      source :forge => Source::Forge\n      source :git => Source::Git\n      source :path => Source::Path\n      source :github_tarball => Source::GitHubTarball\n\n      def default_specfile\n        Proc.new do\n          forge FORGE_URL\n          metadata\n        end\n      end\n\n      def self.dependency_type\n        Librarian::Puppet::Dependency\n      end\n\n      def post_process_target(target)\n        # save the default forge defined\n        default_forge = target.sources.select {|s| s.is_a? Librarian::Puppet::Source::Forge}.first\n        Librarian::Puppet::Source::Forge.default = default_forge || Librarian::Puppet::Source::Forge.from_lock_options(environment, :remote => FORGE_URL)\n      end\n\n      def receiver(target)\n        Receiver.new(target)\n      end\n\n      class Receiver < Librarian::Dsl::Receiver\n        attr_reader :specfile, :working_path\n\n        # save the specfile and call librarian\n        def run(specfile = nil)\n          @working_path = specfile.kind_of?(Pathname) ? specfile.parent : Pathname.new(Dir.pwd)\n          @specfile = specfile\n          super\n        end\n\n        # implement the 'modulefile' syntax for Puppetfile\n        def modulefile\n          f = modulefile_path\n          raise Error, \"Modulefile file does not exist: #{f}\" unless File.exists?(f)\n          File.read(f).lines.each do |line|\n            regexp = /\\s*dependency\\s+('|\")([^'\"]+)\\1\\s*(?:,\\s*('|\")([^'\"]+)\\3)?/\n            regexp =~ line && mod($2, $4)\n          end\n        end\n\n        # implement the 'metadata' syntax for Puppetfile\n        def metadata\n          f = working_path.join('metadata.json')\n          unless File.exists?(f)\n            msg = \"Metadata file does not exist: #{f}\"\n            # try modulefile, in case we don't have a Puppetfile and we are using the default template\n            if File.exists?(modulefile_path)\n              modulefile\n              return\n            else\n              raise Error, msg\n            end\n          end\n          begin\n            json = JSON.parse(File.read(f))\n          rescue JSON::ParserError => e\n            raise Error, \"Unable to parse json file #{f}: #{e}\"\n          end\n          dependencyList = json['dependencies']\n          dependencyList.each do |d|\n            mod(d['name'], d['version_requirement'])\n          end\n        end\n\n        private\n\n        def modulefile_path\n          working_path.join('Modulefile')\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/environment.rb",
    "content": "require \"librarian/environment\"\nrequire \"librarian/puppet/dsl\"\nrequire \"librarian/puppet/source\"\nrequire \"librarian/puppet/lockfile\"\n\nmodule Librarian\n  module Puppet\n    class Environment < Librarian::Environment\n\n      def adapter_name\n        \"puppet\"\n      end\n\n      def lockfile\n        Lockfile.new(self, lockfile_path)\n      end\n\n      def ephemeral_lockfile\n        Lockfile.new(self, nil)\n      end\n\n      def tmp_path\n        part = config_db[\"tmp\"] || \".tmp\"\n        project_path.join(part)\n      end\n\n      def install_path\n        part = config_db[\"path\"] || \"modules\"\n        project_path.join(part)\n      end\n\n      def vendor_path\n        project_path.join('vendor/puppet')\n      end\n\n      def vendor_cache\n        vendor_path.join('cache')\n      end\n\n      def vendor_source\n        vendor_path.join('source')\n      end\n\n      def vendor!\n        vendor_cache.mkpath  unless vendor_cache.exist?\n        vendor_source.mkpath unless vendor_source.exist?\n      end\n\n      def vendor?\n        vendor_path.exist?\n      end\n\n      def local?\n        config_db['mode'] == 'local'\n      end\n\n      def use_v1_api\n        config_db['use-v1-api']\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/extension.rb",
    "content": "require 'librarian/puppet/environment'\nrequire 'librarian/action/base'\n\nmodule Librarian\n  module Puppet\n    extend self\n    extend Librarian\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/lockfile.rb",
    "content": "# Extend Lockfile to normalize module names from acme/mod to acme-mod\nmodule Librarian\n  module Puppet\n    class Lockfile < Librarian::Lockfile\n\n      # Extend the parser to normalize module names in old .lock files, converting / to -\n      class Parser < Librarian::Lockfile::Parser\n\n        include Librarian::Puppet::Util\n\n        def extract_and_parse_sources(lines)\n          sources = super\n          sources.each do |source|\n            source[:manifests] = Hash[source[:manifests].map do |name,manifest|\n              [normalize_name(name), manifest]\n            end]\n          end\n          sources\n        end\n\n        def extract_and_parse_dependencies(lines, manifests_index)\n          # when looking up in manifests_index normalize the name beforehand\n          class << manifests_index\n            include Librarian::Puppet::Util\n            alias_method :old_lookup, :[]\n            define_method(:[]) { |k| self.old_lookup(normalize_name(k)) }\n          end\n          super(lines, manifests_index)\n        end\n\n      end\n\n      def load(string)\n        Parser.new(environment).parse(string)\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/forge/repo.rb",
    "content": "require 'json'\nrequire 'open-uri'\nrequire 'librarian/puppet/util'\nrequire 'librarian/puppet/source/repo'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class Forge\n        class Repo < Librarian::Puppet::Source::Repo\n          include Librarian::Puppet::Util\n\n          def versions\n            return @versions if @versions\n            @versions = get_versions\n            if @versions.empty?\n              info { \"No versions found for module #{name}\" }\n            else\n              debug { \"  Module #{name} found versions: #{@versions.join(\", \")}\" }\n            end\n            @versions\n          end\n\n          # fetch list of versions ordered for newer to older\n          def get_versions\n            # implement in subclasses\n          end\n\n          # return map with dependencies in the form {module_name => version,...}\n          # version: Librarian::Manifest::Version\n          def dependencies(version)\n            # implement in subclasses\n          end\n\n          # return the url for a specific version tarball\n          # version: Librarian::Manifest::Version\n          def url(name, version)\n            # implement in subclasses\n          end\n\n          def manifests\n            versions.map do |version|\n              Manifest.new(source, name, version)\n            end\n          end\n\n          def install_version!(version, install_path)\n            if environment.local? && !vendored?(name, version)\n              raise Error, \"Could not find a local copy of #{name} at #{version}.\"\n            end\n\n            if environment.vendor?\n              vendor_cache(name, version) unless vendored?(name, version)\n            end\n\n            cache_version_unpacked! version\n\n            if install_path.exist? && rsync? != true\n              install_path.rmtree\n            end\n\n            unpacked_path = version_unpacked_cache_path(version).join(module_name(name))\n\n            unless unpacked_path.exist?\n              raise Error, \"#{unpacked_path} does not exist, something went wrong. Try removing it manually\"\n            else\n              cp_r(unpacked_path, install_path)\n            end\n\n          end\n\n          def cache_version_unpacked!(version)\n            path = version_unpacked_cache_path(version)\n            return if path.directory?\n\n            # The puppet module command is only available from puppet versions >= 2.7.13\n            #\n            # Specifying the version in the gemspec would force people to upgrade puppet while it's still usable for git\n            # So we do some more clever checking\n            #\n            # Executing older versions or via puppet-module tool gives an exit status = 0 .\n            #\n            check_puppet_module_options\n\n            path.mkpath\n\n            target = vendored?(name, version) ? vendored_path(name, version).to_s : name\n\n            # can't pass the default v3 forge url (http://forgeapi.puppetlabs.com)\n            # to clients that use the v1 API (https://forge.puppetlabs.com)\n            # nor the other way around\n            module_repository = source.to_s\n\n            if Forge.client_api_version() > 1 and module_repository =~ %r{^http(s)?://forge\\.puppetlabs\\.com}\n              module_repository = \"https://forgeapi.puppetlabs.com\"\n              warn { \"Replacing Puppet Forge API URL to use v3 #{module_repository} as required by your client version #{Librarian::Puppet.puppet_version}\" }\n            end\n\n            m = module_repository.match(%r{^http(s)?://forge(api)?\\.puppetlabs\\.com})\n            if Forge.client_api_version() == 1 and m\n              ssl = m[1]\n              # Puppet 2.7 can't handle the 302 returned by the https url, so stick to http\n              if ssl and Librarian::Puppet::puppet_gem_version < Gem::Version.create('3.0.0')\n                warn { \"Using plain http as your version of Puppet #{Librarian::Puppet::puppet_gem_version} can't download from forge.puppetlabs.com using https\" }\n                ssl = nil\n              end\n              module_repository = \"http#{ssl}://forge.puppetlabs.com\"\n            end\n\n            command = %W{puppet module install --version #{version} --target-dir}\n            command.push(*[path.to_s, \"--module_repository\", module_repository, \"--modulepath\", path.to_s, \"--module_working_dir\", path.to_s, \"--ignore-dependencies\", target])\n            debug { \"Executing puppet module install for #{name} #{version}: #{command.join(\" \")}\" }\n\n            begin\n              Librarian::Posix.run!(command)\n            rescue Posix::CommandFailure => e\n              # Rollback the directory if the puppet module had an error\n              begin\n                path.unlink\n              rescue => u\n                debug(\"Unable to rollback path #{path}: #{u}\")\n              end\n              tar = Dir[File.join(path.to_s, \"**/*.tar.gz\")]\n              msg = \"\"\n              if e.message =~ /Unexpected EOF in archive/ and !tar.empty?\n                file = tar.first\n                msg = \" (looks like an incomplete download of #{file})\"\n              end\n              raise Error, \"Error executing puppet module install#{msg}. Check that this command succeeds:\\n#{command.join(\" \")}\\nError:\\n#{e.message}\"\n            end\n\n          end\n\n          def check_puppet_module_options\n            min_version    = Gem::Version.create('2.7.13')\n\n            if Librarian::Puppet.puppet_gem_version < min_version\n              raise Error, \"To get modules from the forge, we use the puppet faces module command. For this you need at least puppet version 2.7.13 and you have #{Librarian::Puppet.puppet_version}\"\n            end\n          end\n\n          def vendor_cache(name, version)\n            url = url(name, version)\n            path = vendored_path(name, version).to_s\n            debug { \"Downloading #{url} into #{path}\"}\n            environment.vendor!\n            File.open(path, 'wb') do |f|\n              open(url, \"rb\") do |input|\n                f.write(input.read)\n              end\n            end\n          end\n\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/forge/repo_v1.rb",
    "content": "require 'json'\nrequire 'open-uri'\nrequire 'librarian/puppet/source/forge/repo'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class Forge\n        class RepoV1 < Librarian::Puppet::Source::Forge::Repo\n\n          def initialize(source, name)\n            super(source, name)\n            # API returned data for this module including all versions and dependencies, indexed by module name\n            # from http://forge.puppetlabs.com/api/v1/releases.json?module=#{name}\n            @api_data = nil\n            # API returned data for this module and a specific version, indexed by version\n            # from http://forge.puppetlabs.com/api/v1/releases.json?module=#{name}&version=#{version}\n            @api_version_data = {}\n          end\n\n          def get_versions\n            api_data(name).map { |r| r['version'] }.reverse\n          end\n\n          def dependencies(version)\n            api_version_data(name, version)['dependencies']\n          end\n\n          def url(name, version)\n            info = api_version_data(name, version)\n            \"#{source}#{info[name].first['file']}\"\n          end\n\n        private\n\n          # convert organization/modulename to organization-modulename\n          def normalize_dependencies(data)\n            return nil if data.nil?\n            # convert organization/modulename to organization-modulename\n            data.keys.each do |m|\n              if m =~ %r{.*/.*}\n                data[normalize_name(m)] = data[m]\n                data.delete(m)\n              end\n            end\n            data\n          end\n\n          # get and cache the API data for a specific module with all its versions and dependencies\n          def api_data(module_name)\n            return @api_data[module_name] if @api_data\n            # call API and cache data\n            @api_data = normalize_dependencies(api_call(module_name))\n            if @api_data.nil?\n              raise Error, \"Unable to find module '#{name}' on #{source}\"\n            end\n            @api_data[module_name]\n          end\n\n          # get and cache the API data for a specific module and version\n          def api_version_data(module_name, version)\n            # if we already got all the versions, find in cached data\n            return @api_data[module_name].detect{|x| x['version'] == version.to_s} if @api_data\n            # otherwise call the api for this version if not cached already\n            @api_version_data[version] = normalize_dependencies(api_call(name, version)) if @api_version_data[version].nil?\n            @api_version_data[version]\n          end\n\n          def api_call(module_name, version=nil)\n            url = source.uri.clone\n            url.path += \"#{'/' if url.path.empty? or url.path[-1] != '/'}api/v1/releases.json\"\n            url.query = \"module=#{module_name.sub('-','/')}\" # v1 API expects \"organization/module\"\n            url.query += \"&version=#{version}\" unless version.nil?\n            debug { \"Querying Forge API for module #{name}#{\" and version #{version}\" unless version.nil?}: #{url}\" }\n\n            begin\n              data = open(url) {|f| f.read}\n              JSON.parse(data)\n            rescue OpenURI::HTTPError => e\n              case e.io.status[0].to_i\n              when 404,410\n                nil\n              else\n                raise e, \"Error requesting #{url}: #{e.to_s}\"\n              end\n            end\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/forge/repo_v3.rb",
    "content": "require 'librarian/puppet/source/forge/repo'\nrequire 'puppet_forge'\nrequire 'librarian/puppet/version'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class Forge\n        class RepoV3 < Librarian::Puppet::Source::Forge::Repo\n\n          PuppetForge.user_agent = \"librarian-puppet/#{Librarian::Puppet::VERSION}\"\n\n          def initialize(source, name)\n            PuppetForge.host = source.uri.clone\n            super(source, name)\n          end\n\n          def get_versions\n            get_module.releases.select{|r| r.deleted_at.nil?}.map{|r| r.version}\n          end\n\n          def dependencies(version)\n            array = get_release(version).metadata[:dependencies].map{|d| [d['name'], d['version_requirement']]}\n            Hash[*array.flatten(1)]\n          end\n\n          def url(name, version)\n            if name == \"#{get_module().owner.username}/#{get_module().name}\"\n              release = get_release(version)\n            else\n              # should never get here as we use one repo object for each module (to be changed in the future)\n              debug { \"Looking up url for #{name}@#{version}\" }\n              release = PuppetForge::Release.find(\"#{name}-#{version}\")\n            end\n            \"#{source}#{release.file_uri}\"\n          end\n\n        private\n\n          def get_module\n            @module ||= PuppetForge::Module.find(name)\n            raise(Error, \"Unable to find module '#{name}' on #{source}\") unless @module\n            @module\n          end\n\n          def get_release(version)\n            release = get_module.releases.find{|r| r.version == version.to_s}\n            if release.nil?\n              versions = get_module.releases.map{|r| r.version}\n              raise Error, \"Unable to find version '#{version}' for module '#{name}' on #{source} amongst #{versions}\"\n            end\n            release\n          end\n\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/forge.rb",
    "content": "require 'uri'\nrequire 'librarian/puppet/util'\nrequire 'librarian/puppet/source/forge/repo_v1'\nrequire 'librarian/puppet/source/forge/repo_v3'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class Forge\n        include Librarian::Puppet::Util\n\n        class << self\n          LOCK_NAME = 'FORGE'\n\n          def default=(source)\n            @@default = source\n          end\n\n          def default\n            @@default\n          end\n\n          def lock_name\n            LOCK_NAME\n          end\n\n          def from_lock_options(environment, options)\n            new(environment, options[:remote], options.reject { |k, v| k == :remote })\n          end\n\n          def from_spec_args(environment, uri, options)\n            recognised_options = []\n            unrecognised_options = options.keys - recognised_options\n            unless unrecognised_options.empty?\n              raise Error, \"unrecognised options: #{unrecognised_options.join(\", \")}\"\n            end\n\n            new(environment, uri, options)\n          end\n\n          def client_api_version()\n            version = 1\n            pe_version = Librarian::Puppet.puppet_version.match(/\\(Puppet Enterprise (.+)\\)/)\n\n            # Puppet 3.6.0+ uses api v3\n            if Librarian::Puppet::puppet_gem_version >= Gem::Version.create('3.6.0.a')\n              version = 3\n            # Puppet enterprise 3.2.0+ uses api v3\n            elsif pe_version and Gem::Version.create(pe_version[1].strip) >= Gem::Version.create('3.2.0')\n              version = 3\n            end\n            return version\n          end\n\n        end\n\n        attr_accessor :environment\n        private :environment=\n        attr_reader :uri\n\n        def initialize(environment, uri, options = {})\n          self.environment = environment\n\n          if uri =~ %r{^http(s)?://forge\\.puppetlabs\\.com}\n            uri = \"https://forgeapi.puppetlabs.com\"\n            warn { \"Replacing Puppet Forge API URL to use v3 #{uri}. You should update your Puppetfile\" }\n          end\n\n          @uri = URI::parse(uri)\n          @cache_path = nil\n        end\n\n        def to_s\n          clean_uri(uri).to_s\n        end\n\n        def ==(other)\n          other &&\n          self.class == other.class &&\n          self.uri == other.uri\n        end\n\n        alias :eql? :==\n\n        def hash\n          self.to_s.hash\n        end\n\n        def to_spec_args\n          [clean_uri(uri).to_s, {}]\n        end\n\n        def to_lock_options\n          {:remote => clean_uri(uri).to_s}\n        end\n\n        def pinned?\n          false\n        end\n\n        def unpin!\n        end\n\n        def install!(manifest)\n          manifest.source == self or raise ArgumentError\n\n          debug { \"Installing #{manifest}\" }\n\n          name = manifest.name\n          version = manifest.version\n          install_path = install_path(name)\n          repo = repo(name)\n\n          repo.install_version! version, install_path\n        end\n\n        def manifest(name, version, dependencies)\n          manifest = Manifest.new(self, name)\n          manifest.version = version\n          manifest.dependencies = dependencies\n          manifest\n        end\n\n        def cache_path\n          @cache_path ||= begin\n            dir = \"#{uri.host}#{uri.path}\".gsub(/[^0-9a-z\\-_]/i, '_')\n            environment.cache_path.join(\"source/puppet/forge/#{dir}\")\n          end\n        end\n\n        def install_path(name)\n          environment.install_path.join(module_name(name))\n        end\n\n        def fetch_version(name, version_uri)\n          versions = repo(name).versions\n          if versions.include? version_uri\n            version_uri\n          else\n            versions.first\n          end\n        end\n\n        def fetch_dependencies(name, version, version_uri)\n          repo(name).dependencies(version).map do |k, v|\n            v = Librarian::Dependency::Requirement.new(v).to_gem_requirement\n            Dependency.new(k, v, nil)\n          end\n        end\n\n        def manifests(name)\n          repo(name).manifests\n        end\n\n      private\n\n        def repo(name)\n          @repo ||= {}\n\n          unless @repo[name]\n            # if we are using the official Forge then use API v3, otherwise stick to v1 for now\n            if uri.hostname =~ /\\.puppetlabs\\.com$/ || !environment.use_v1_api\n              @repo[name] = RepoV3.new(self, name)\n            else\n              @repo[name] = RepoV1.new(self, name)\n            end\n          end\n          @repo[name]\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/git.rb",
    "content": "require 'librarian/source/git'\nrequire 'librarian/puppet/source/local'\n\nmodule Librarian\n  module Source\n    class Git\n      class Repository\n        def hash_from(remote, reference)\n          branch_names = remote_branch_names[remote]\n          if branch_names.include?(reference)\n            reference = \"#{remote}/#{reference}\"\n          end\n\n          command = %W(rev-parse #{reference}^{commit} --quiet)\n          run!(command, :chdir => true).strip\n        end\n      end\n    end\n  end\n\n  module Puppet\n    module Source\n      class Git < Librarian::Source::Git\n        include Local\n        include Librarian::Puppet::Util\n\n        def cache!\n          return vendor_checkout! if vendor_cached?\n\n          if environment.local?\n            raise Error, \"Could not find a local copy of #{uri}#{\" at #{sha}\" unless sha.nil?}.\"\n          end\n\n          begin\n            super\n          rescue Librarian::Posix::CommandFailure => e\n            raise Error, \"Could not checkout #{uri}#{\" at #{sha}\" unless sha.nil?}: #{e}\"\n          end\n\n          cache_in_vendor(repository.path) if environment.vendor?\n        end\n\n        private\n\n        def vendor_tar\n          environment.vendor_source.join(\"#{sha}.tar\")\n        end\n\n        def vendor_tgz\n          environment.vendor_source.join(\"#{sha}.tar.gz\")\n        end\n\n        def vendor_cached?\n          vendor_tgz.exist?\n        end\n\n        def vendor_checkout!\n          repository.path.rmtree if repository.path.exist?\n          repository.path.mkpath\n\n          Librarian::Posix.run!(%W{tar xzf #{vendor_tgz}}, :chdir => repository.path.to_s)\n\n          repository_cached!\n        end\n\n        def cache_in_vendor(tmp_path)\n          Librarian::Posix.run!(%W{git archive -o #{vendor_tar} #{sha}}, :chdir => tmp_path.to_s)\n          Librarian::Posix.run!(%W{gzip #{vendor_tar}}, :chdir => tmp_path.to_s)\n        end\n\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/githubtarball/repo.rb",
    "content": "require 'uri'\nrequire 'net/https'\nrequire 'open-uri'\nrequire 'json'\n\nrequire 'librarian/puppet/version'\nrequire 'librarian/puppet/source/repo'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class GitHubTarball\n        class Repo < Librarian::Puppet::Source::Repo\n          include Librarian::Puppet::Util\n\n          TOKEN_KEY = 'GITHUB_API_TOKEN'\n\n          def versions\n            return @versions if @versions\n            data = api_call(\"/repos/#{source.uri}/tags\")\n            if data.nil?\n              raise Error, \"Unable to find module '#{source.uri}' on https://github.com\"\n            end\n\n            all_versions = data.map { |r| r['name'].gsub(/^v/, '') }.sort.reverse\n\n            all_versions.delete_if do |version|\n              version !~ /\\A\\d+\\.\\d+(\\.\\d+.*)?\\z/\n            end\n\n            @versions = all_versions.compact\n            debug { \"  Module #{name} found versions: #{@versions.join(\", \")}\" }\n            @versions\n          end\n\n          def manifests\n            versions.map do |version|\n              Manifest.new(source, name, version)\n            end\n          end\n\n          def install_version!(version, install_path)\n            if environment.local? && !vendored?(vendored_name, version)\n              raise Error, \"Could not find a local copy of #{source.uri} at #{version}.\"\n            end\n\n            vendor_cache(source.uri.to_s, version) unless vendored?(vendored_name, version)\n\n            cache_version_unpacked! version\n\n            if install_path.exist? && rsync? != true\n              install_path.rmtree\n            end\n\n            unpacked_path = version_unpacked_cache_path(version).children.first\n            cp_r(unpacked_path, install_path)\n          end\n\n          def cache_version_unpacked!(version)\n            path = version_unpacked_cache_path(version)\n            return if path.directory?\n\n            path.mkpath\n\n            target = vendored?(vendored_name, version) ? vendored_path(vendored_name, version) : name\n\n            Librarian::Posix.run!(%W{tar xzf #{target} -C #{path}})\n          end\n\n          def vendor_cache(name, version)\n            clean_up_old_cached_versions(vendored_name(name))\n\n            url = \"https://api.github.com/repos/#{name}/tarball/#{version}\"\n            add_api_token_to_url(url)\n\n            environment.vendor!\n            File.open(vendored_path(vendored_name(name), version).to_s, 'wb') do |f|\n              begin\n                debug { \"Downloading <#{url}> to <#{f.path}>\" }\n                open(url,\n                  \"User-Agent\" => \"librarian-puppet v#{Librarian::Puppet::VERSION}\") do |res|\n                  while buffer = res.read(8192)\n                    f.write(buffer)\n                  end\n                end\n              rescue OpenURI::HTTPError => e\n                raise e, \"Error requesting <#{url}>: #{e.to_s}\"\n              end\n            end\n          end\n\n          def clean_up_old_cached_versions(name)\n            Dir[\"#{environment.vendor_cache}/#{name}*.tar.gz\"].each do |old_version|\n              FileUtils.rm old_version\n            end\n          end\n\n          def token_key_value\n            ENV[TOKEN_KEY]\n          end\n\n          def token_key_nil?\n            token_key_value.nil? || token_key_value.empty?\n          end\n\n          def add_api_token_to_url url\n            if token_key_nil?\n              debug { \"#{TOKEN_KEY} environment value is empty or missing\" }\n            elsif url.include? \"?\"\n              url << \"&access_token=#{ENV[TOKEN_KEY]}\"\n            else\n              url << \"?access_token=#{ENV[TOKEN_KEY]}\"\n            end\n            url\n          end\n\n        private\n\n          def api_call(path)\n            tags = []\n            url = \"https://api.github.com#{path}?page=1&per_page=100\"\n            while true do\n              debug { \"  Module #{name} getting tags at: #{url}\" }\n              add_api_token_to_url(url)\n              response = http_get(url, :headers => {\n                \"User-Agent\" => \"librarian-puppet v#{Librarian::Puppet::VERSION}\"\n              })\n\n              code, data = response.code.to_i, response.body\n\n              if code == 200\n                tags.concat JSON.parse(data)\n              else\n                begin\n                  message = JSON.parse(data)['message']\n                  if code == 403 && message && message.include?('API rate limit exceeded')\n                    raise Error, message + \" -- increase limit by authenticating via #{TOKEN_KEY}=your-token\"\n                  elsif message\n                    raise Error, \"Error fetching #{url}: [#{code}] #{message}\"\n                  end\n                rescue JSON::ParserError\n                  # response does not return json\n                end\n                raise Error, \"Error fetching #{url}: [#{code}] #{response.body}\"\n              end\n\n              # next page\n              break if response[\"link\"].nil?\n              next_link = response[\"link\"].split(\",\").select{|l| l.match /rel=.*next.*/}\n              break if next_link.empty?\n              url = next_link.first.match(/<(.*)>/)[1]\n            end\n            return tags\n          end\n\n          def http_get(url, options)\n            uri = URI.parse(url)\n            http = Net::HTTP.new(uri.host, uri.port)\n            http.use_ssl = true\n            request = Net::HTTP::Get.new(uri.request_uri)\n            options[:headers].each { |k, v| request.add_field k, v }\n            http.request(request)\n          end\n\n          def vendored_name(name = source.uri.to_s)\n            name.sub('/','-')\n          end\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/githubtarball.rb",
    "content": "require 'uri'\nrequire 'librarian/puppet/util'\nrequire 'librarian/puppet/source/githubtarball/repo'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class GitHubTarball\n        include Librarian::Puppet::Util\n\n        class << self\n          LOCK_NAME = 'GITHUBTARBALL'\n\n          def lock_name\n            LOCK_NAME\n          end\n\n          def from_lock_options(environment, options)\n            new(environment, options[:remote], options.reject { |k, v| k == :remote })\n          end\n\n          def from_spec_args(environment, uri, options)\n            recognised_options = []\n            unrecognised_options = options.keys - recognised_options\n            unless unrecognised_options.empty?\n              raise Error, \"unrecognised options: #{unrecognised_options.join(\", \")}\"\n            end\n\n            new(environment, uri, options)\n          end\n        end\n\n        attr_accessor :environment\n        private :environment=\n        attr_reader :uri\n\n        def initialize(environment, uri, options = {})\n          self.environment = environment\n          @uri = URI::parse(uri)\n          @cache_path = nil\n        end\n\n        def to_s\n          clean_uri(uri).to_s\n        end\n\n        def ==(other)\n          other &&\n          self.class == other.class &&\n          self.uri == other.uri\n        end\n\n        alias :eql? :==\n\n        def hash\n          self.to_s.hash\n        end\n\n        def to_spec_args\n          [clean_uri(uri).to_s, {}]\n        end\n\n        def to_lock_options\n          {:remote => clean_uri(uri).to_s}\n        end\n\n        def pinned?\n          false\n        end\n\n        def unpin!\n        end\n\n        def install!(manifest)\n          manifest.source == self or raise ArgumentError\n\n          debug { \"Installing #{manifest}\" }\n\n          name = manifest.name\n          version = manifest.version\n          install_path = install_path(name)\n          repo = repo(name)\n\n          repo.install_version! version, install_path\n        end\n\n        def manifest(name, version, dependencies)\n          manifest = Manifest.new(self, name)\n          manifest.version = version\n          manifest.dependencies = dependencies\n          manifest\n        end\n\n        def cache_path\n          @cache_path ||= begin\n            environment.cache_path.join(\"source/puppet/githubtarball/#{uri.host}#{uri.path}\")\n          end\n        end\n\n        def install_path(name)\n          environment.install_path.join(module_name(name))\n        end\n\n        def fetch_version(name, version_uri)\n          versions = repo(name).versions\n          if versions.include? version_uri\n            version_uri\n          else\n            versions.first\n          end\n        end\n\n        def fetch_dependencies(name, version, version_uri)\n          {}\n        end\n\n        def manifests(name)\n          repo(name).manifests\n        end\n\n      private\n\n        def repo(name)\n          @repo ||= {}\n          @repo[name] ||= Repo.new(self, name)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/local.rb",
    "content": "require 'librarian/puppet/util'\n\nmodule Librarian\n  module Puppet\n    module Source\n      module Local\n        include Librarian::Puppet::Util\n\n        def install!(manifest)\n          manifest.source == self or raise ArgumentError\n\n          debug { \"Installing #{manifest}\" }\n\n          name, version = manifest.name, manifest.version\n          found_path = found_path(name)\n          raise Error, \"Path for #{name} doesn't contain a puppet module\" if found_path.nil?\n\n          unless name.include? '/' or name.include? '-'\n            warn { \"Invalid module name '#{name}', you should qualify it with 'ORGANIZATION-#{name}' for resolution to work correctly\" }\n          end\n\n          install_path = environment.install_path.join(module_name(name))\n          if install_path.exist? && rsync? != true\n            debug { \"Deleting #{relative_path_to(install_path)}\" }\n            install_path.rmtree\n          end\n\n          install_perform_step_copy!(found_path, install_path)\n        end\n\n        def fetch_version(name, extra)\n          cache!\n          found_path = found_path(name)\n          module_version\n        end\n\n        def fetch_dependencies(name, version, extra)\n          dependencies = Set.new\n\n          if specfile?\n            spec = environment.dsl(Pathname(specfile))\n            dependencies.merge spec.dependencies\n          end\n\n          parsed_metadata['dependencies'].each do |d|\n            gem_requirement = Librarian::Dependency::Requirement.new(d['version_requirement']).to_gem_requirement\n            new_dependency = Dependency.new(d['name'], gem_requirement, forge_source)\n            dependencies << new_dependency\n          end\n\n          dependencies\n        end\n\n        def forge_source\n          Forge.default\n        end\n\n        private\n\n        # Naming this method 'version' causes an exception to be raised.\n        def module_version\n          if parsed_metadata['version']\n            parsed_metadata['version']\n          else\n            warn { \"Module #{to_s} does not have version, defaulting to 0.0.1\" }\n            '0.0.1'\n          end\n        end\n\n        def require_puppet\n          begin\n            require 'puppet'\n            require 'puppet/module_tool'\n          rescue LoadError\n            $stderr.puts <<-EOF\n          Unable to load puppet, the puppet gem is required for :git and :path source.\n          Install it with: gem install puppet\n            EOF\n            exit 1\n          end\n          true\n        end\n\n        def evaluate_modulefile(modulefile)\n          @@require_puppet ||= require_puppet\n\n          metadata = ::Puppet::ModuleTool::Metadata.new\n\n          # Puppet 4 does not have the class\n          unless defined? ::Puppet::ModuleTool::ModulefileReader\n            warn { \"Can't parse Modulefile in Puppet >= 4.0 and you are using #{Librarian::Puppet::puppet_version}. Ignoring dependencies in #{modulefile}\" }\n            return metadata\n          end\n\n          begin\n            ::Puppet::ModuleTool::ModulefileReader.evaluate(metadata, modulefile)\n            raise SyntaxError, \"Missing version\" unless metadata.version\n          rescue ArgumentError, SyntaxError => error\n            warn { \"Unable to parse #{modulefile}, ignoring: #{error}\" }\n            if metadata.respond_to? :version=\n              metadata.version = '0.0.1' # puppet < 3.6\n            else\n              metadata.update({'version' => '0.0.1'}) # puppet >= 3.6\n            end\n          end\n          metadata\n        end\n\n        def parsed_metadata\n          if @metadata.nil?\n            @metadata = if metadata?\n              begin\n                JSON.parse(File.read(metadata))\n              rescue JSON::ParserError => e\n                raise Error, \"Unable to parse json file #{metadata}: #{e}\"\n              end\n            elsif modulefile?\n              # translate Modulefile to metadata.json\n              evaluated = evaluate_modulefile(modulefile)\n              {\n                'version' => evaluated.version,\n                'dependencies' => evaluated.dependencies.map do |dependency|\n                  {\n                    'name' => dependency.instance_variable_get(:@full_module_name),\n                    'version_requirement' => dependency.instance_variable_get(:@version_requirement)\n                  }\n                end\n              }\n            else\n              {}\n            end\n            @metadata['dependencies'] ||= []\n          end\n          @metadata\n        end\n\n        def modulefile\n          File.join(filesystem_path, 'Modulefile')\n        end\n\n        def modulefile?\n          File.exists?(modulefile)\n        end\n\n        def metadata\n          File.join(filesystem_path, 'metadata.json')\n        end\n\n        def metadata?\n          File.exists?(metadata)\n        end\n\n        def specfile\n          File.join(filesystem_path, environment.specfile_name)\n        end\n\n        def specfile?\n          File.exists?(specfile)\n        end\n\n        def install_perform_step_copy!(found_path, install_path)\n          debug { \"Copying #{relative_path_to(found_path)} to #{relative_path_to(install_path)}\" }\n          cp_r(found_path, install_path)\n        end\n\n        def manifest?(name, path)\n          return true if path.join('manifests').exist?\n          return true if path.join('lib').join('puppet').exist?\n          return true if path.join('lib').join('facter').exist?\n          debug { \"Could not find manifests, lib/puppet or lib/facter under #{path}, maybe it is not a puppet module\" }\n          true\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/path.rb",
    "content": "require 'librarian/source/path'\nrequire 'librarian/puppet/source/local'\n\nmodule Librarian\n  module Puppet\n    module Source\n      class Path < Librarian::Source::Path\n        include Local\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source/repo.rb",
    "content": "# parent class for githubtarball and forge source Repos\nmodule Librarian\n  module Puppet\n    module Source\n      class Repo\n\n        attr_accessor :source, :name\n        private :source=, :name=\n\n        def initialize(source, name)\n          self.source = source\n          self.name = name\n        end\n\n        def environment\n          source.environment\n        end\n\n        def cache_path\n          @cache_path ||= source.cache_path.join(name)\n        end\n\n        def version_unpacked_cache_path(version)\n          cache_path.join(version.to_s)\n        end\n\n        def vendored?(name, version)\n          vendored_path(name, version).exist?\n        end\n\n        def vendored_path(name, version)\n          environment.vendor_cache.join(\"#{name}-#{version}.tar.gz\")\n        end\n\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/source.rb",
    "content": "require 'librarian/puppet/source/path'\nrequire 'librarian/puppet/source/git'\nrequire 'librarian/puppet/source/forge'\nrequire 'librarian/puppet/source/githubtarball'\n"
  },
  {
    "path": "lib/librarian/puppet/templates/Puppetfile",
    "content": "#!/usr/bin/env ruby\n#^syntax detection\n\nforge \"https://forgeapi.puppetlabs.com\"\n\n# use dependencies defined in metadata.json\nmetadata\n\n# use dependencies defined in Modulefile\n# modulefile\n\n# A module from the Puppet Forge\n# mod 'puppetlabs-stdlib'\n\n# A module from git\n# mod 'puppetlabs-ntp',\n#   :git => 'git://github.com/puppetlabs/puppetlabs-ntp.git'\n\n# A module from a git branch/tag\n# mod 'puppetlabs-apt',\n#   :git => 'https://github.com/puppetlabs/puppetlabs-apt.git',\n#   :ref => '1.4.x'\n\n# A module from Github pre-packaged tarball\n# mod 'puppetlabs-apache', '0.6.0', :github_tarball => 'puppetlabs/puppetlabs-apache'\n"
  },
  {
    "path": "lib/librarian/puppet/util.rb",
    "content": "require 'rsync'\n\nmodule Librarian\n  module Puppet\n\n    module Util\n\n      def debug(*args, &block)\n        environment.logger.debug(*args, &block)\n      end\n      def info(*args, &block)\n        environment.logger.info(*args, &block)\n      end\n      def warn(*args, &block)\n        environment.logger.warn(*args, &block)\n      end\n\n      def rsync?\n          environment.config_db.local['rsync'] == 'true'\n      end\n\n      # workaround Issue #173 FileUtils.cp_r will fail if there is a symlink that points to a missing file\n      # or when the symlink is copied before the target file when preserve is true\n      # see also https://tickets.opscode.com/browse/CHEF-833\n      #\n      # If the rsync configuration parameter is set, use rsync instead of FileUtils\n      def cp_r(src, dest)\n        if rsync?\n          Rsync.run(File.join(src, \"/\"), dest, ['-avz', '--delete'])\n        else\n          begin\n            FileUtils.cp_r(src, dest, :preserve => true)\n          rescue Errno::ENOENT, Errno::EACCES\n            debug { \"Failed to copy from #{src} to #{dest} preserving file types, trying again without preserving them\" }\n            FileUtils.rm_rf(dest)\n            FileUtils.cp_r(src, dest)\n          end\n        end\n      end\n\n      # Remove user and password from a URI object\n      def clean_uri(uri)\n        new_uri = uri.clone\n        new_uri.user = nil\n        new_uri.password = nil\n        new_uri\n      end\n\n      # normalize module name to use organization-module instead of organization/module\n      def normalize_name(name)\n        name.sub('/','-')\n      end\n\n      # get the module name from organization-module\n      def module_name(name)\n        # module name can't have dashes, so let's assume it is everything after the last dash\n        name.rpartition('-').last\n      end\n\n      # deprecated\n      alias :organization_name :module_name\n    end\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet/version.rb",
    "content": "module Librarian\n  module Puppet\n    VERSION = \"2.2.1\"\n  end\nend\n"
  },
  {
    "path": "lib/librarian/puppet.rb",
    "content": "require 'librarian'\nrequire 'fileutils'\n\nrequire 'librarian/puppet/extension'\nrequire 'librarian/puppet/version'\n\nrequire 'librarian/action/install'\n\nmodule Librarian\n  module Puppet\n    @@puppet_version = nil\n\n    # Output of puppet --version, typically x.y.z\n    # For Puppet Enterprise it contains the PE version too, ie. 3.4.3 (Puppet Enterprise 3.2.1)\n    def puppet_version\n      return @@puppet_version unless @@puppet_version.nil?\n\n      begin\n        @@puppet_version = Librarian::Posix.run!(%W{puppet --version}).strip\n      rescue Errno::ENOENT, Librarian::Posix::CommandFailure => error\n        msg = \"Unable to load puppet. Please install it using native packages for your platform (eg .deb, .rpm, .dmg, etc).\"\n        msg += \"\\npuppet --version returned #{error.status}\" if error.respond_to? :status\n        msg += \"\\n#{error.message}\" unless error.message.nil?\n        $stderr.puts msg\n        exit 1\n      end\n      return @@puppet_version\n    end\n\n    # Puppet version x.y.z translated as a Gem version\n    def puppet_gem_version\n      Gem::Version.create(puppet_version.split(' ').first.strip.gsub('-', '.'))\n    end\n\n  end\nend\n"
  },
  {
    "path": "librarian-puppet.gemspec",
    "content": "$:.push File.expand_path(\"../lib\", __FILE__)\n\nrequire 'librarian/puppet/version'\n\nGem::Specification.new do |s|\n  s.name = 'librarian-puppet'\n  s.version = Librarian::Puppet::VERSION\n  s.platform = Gem::Platform::RUBY\n  s.authors = ['Tim Sharpe', 'Carlos Sanchez']\n  s.license = 'MIT'\n  s.email = ['tim@sharpe.id.au', 'carlos@apache.org']\n  s.homepage = 'https://github.com/rodjek/librarian-puppet'\n  s.summary = 'Bundler for your Puppet modules'\n  s.description = 'Simplify deployment of your Puppet infrastructure by\n  automatically pulling in modules from the forge and git repositories with\n  a single command.'\n\n  # puppet_forge gem requires ruby 1.9 so we do too, use version 1.x in ruby 1.8\n  s.required_ruby_version = '>= 1.9.0'\n\n  s.files = [\n    '.gitignore',\n    'LICENSE',\n    'README.md',\n  ] + Dir['{bin,lib}/**/*']\n\n  s.executables = ['librarian-puppet']\n\n  s.add_dependency \"librarianp\", \">=0.6.3\"\n  s.add_dependency \"rsync\"\n  s.add_dependency \"puppet_forge\", \"~> 1.0\"\n\n  s.add_development_dependency \"rake\"\n  s.add_development_dependency \"rspec\"\n  s.add_development_dependency \"cucumber\"\n  s.add_development_dependency \"aruba\", \"<0.8.0\"\n  s.add_development_dependency \"puppet\", ENV[\"PUPPET_VERSION\"]\n  s.add_development_dependency \"minitest\", \"~> 5\"\n  s.add_development_dependency \"mocha\"\n  s.add_development_dependency \"simplecov\", \">= 0.9.0\"\nend\n"
  },
  {
    "path": "spec/action/resolve_spec.rb",
    "content": "require 'spec_helper'\nrequire_relative '../../lib/librarian/puppet/action/resolve'\nrequire 'librarian/ui'\nrequire 'thor'\n\ndescribe 'Librarian::Puppet::Action::Resolve' do\n\n  let(:path) { File.expand_path(\"../../../features/examples/test\", __FILE__) }\n  let(:environment) { Librarian::Puppet::Environment.new(:project_path => path) }\n\n  before do\n    # run with DEBUG=true envvar to get debug output\n    environment.ui = Librarian::UI::Shell.new(Thor::Shell::Basic.new)\n  end\n\n  describe '#run' do\n\n    it 'should resolve dependencies' do\n      Librarian::Puppet::Action::Resolve.new(environment, :force => true).run\n      resolution = environment.lock.manifests.map { |m| {:name => m.name, :version => m.version.to_s, :source => m.source.to_s} }\n      expect(resolution.size).to eq(1)\n      expect(resolution.first[:name]).to eq(\"puppetlabs-stdlib\")\n      expect(resolution.first[:source]).to eq(\"https://forgeapi.puppetlabs.com\")\n      expect(resolution.first[:version]).to match(/\\d+\\.\\d+\\.\\d+/)\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/librarian_puppet_spec.rb",
    "content": "describe Librarian::Puppet do\n  it 'exits with error if puppet is not installed' do\n    error = Librarian::Posix::CommandFailure.new 'puppet not installed'\n    error.status = 42\n\n    expect(Librarian::Posix).to receive(:run!).and_raise(error)\n    expect($stderr).to receive(:puts) do |message|\n      expect(message).to match /42/\n      expect(message).to match /puppet not installed/\n    end\n\n    expect { Librarian::Puppet::puppet_version }.to raise_error(SystemExit)\n  end\nend\n"
  },
  {
    "path": "spec/receiver_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe 'Librarian::Puppet::Dsl::Receiver' do\n\n  let(:dsl) { Librarian::Puppet::Dsl.new({}) }\n  let(:target) { Librarian::Dsl::Target.new(dsl) }\n  let(:receiver) { Librarian::Puppet::Dsl::Receiver.new(target) }\n  let(:environment) { Librarian::Puppet::Environment.new(:project_path => '/tmp/tmp_module') }\n  describe '#run' do\n\n    it 'should get working_dir from pwd when specfile is nil' do\n      receiver.run(nil) {}\n      expect(receiver.working_path).to eq(Pathname.new(Dir.pwd))\n    end\n\n    it 'should get working_dir from pwd with default specfile' do\n      receiver.run(dsl.default_specfile) {}\n      expect(receiver.working_path).to eq(Pathname.new(Dir.pwd))\n    end\n\n    it 'should get working_dir from given path' do\n      receiver.run(Pathname.new('/tmp/tmp_module/Puppetfile')) {}\n      expect(receiver.working_path).to eq(Pathname.new('/tmp/tmp_module'))\n    end\n\n    it 'test receiver run' do\n      error_message = 'Metadata file does not exist: '+File.join(environment.project_path, 'metadata.json')\n      expect{environment.dsl(environment.specfile.path, [])}.to raise_error(Librarian::Error,error_message)\n    end\n  end\nend\n"
  },
  {
    "path": "spec/source/forge_repo_spec.rb",
    "content": "require \"librarian/puppet/source/forge\"\nrequire \"librarian/puppet/environment\"\n\ndescribe Librarian::Puppet::Source::Forge::Repo do\n\n  let(:environment) { Librarian::Puppet::Environment.new }\n  let(:uri) { \"https://forge.puppetlabs.com\" }\n  let(:source) { Librarian::Puppet::Source::Forge.new(environment, uri) }\n  subject { Librarian::Puppet::Source::Forge::Repo.new(source, \"puppetlabs/stdlib\") }\n\n  describe \"#check_puppet_module_options\" do\n    context \"Puppet OS\" do\n    end\n  end\nend\n"
  },
  {
    "path": "spec/source/forge_spec.rb",
    "content": "require \"librarian/puppet/source/forge\"\nrequire \"librarian/puppet/environment\"\nrequire 'librarian/puppet/extension'\n\ninclude Librarian::Puppet::Source\n\ndescribe Forge do\n\n  let(:environment) { Librarian::Puppet::Environment.new }\n  let(:uri) { \"https://forge.puppetlabs.com\" }\n  let(:puppet_version) { \"3.6.0\" }\n  subject { Forge.new(environment, uri) }\n\n  describe \"#manifests\" do\n    let(:manifests) { [] }\n    before do\n      expect_any_instance_of(Librarian::Puppet::Source::Forge::RepoV3).to receive(:get_versions).at_least(:once) { manifests }\n    end\n    it \"should return the manifests\" do\n      expect(subject.manifests(\"x\")).to eq(manifests)\n    end\n  end\n\n  describe \"#check_puppet_module_options\" do\n    before do\n      expect(Librarian::Puppet).to receive(:puppet_version).at_least(:once) { puppet_version }\n      expect(Librarian::Puppet).to receive(:puppet_gem_version).at_least(:once) { Gem::Version.create(puppet_version.split(' ').first.strip.gsub('-', '.')) }\n    end\n    context \"Puppet OS\" do\n      context \"3.4.3\" do\n        let(:puppet_version) { \"3.4.3\" }\n        it { Forge.client_api_version().should == 1 }\n      end\n      context \"2.7.13\" do\n        let(:puppet_version) { \"2.7.13\" }\n        it { Forge.client_api_version().should == 1 }\n      end\n      context \"3.6.0\" do\n        it { Forge.client_api_version().should == 3 }\n      end\n    end\n    context \"Puppet Enterprise\" do\n      context \"3.2.1\" do\n        let(:puppet_version) { \"3.4.3 (Puppet Enterprise 3.2.1)\" }\n        it { Forge.client_api_version().should == 3 }\n      end\n      context \"3.1.3\" do\n        let(:puppet_version) { \"3.3.3 (Puppet Enterprise 3.1.3)\" }\n        it { Forge.client_api_version().should == 1 }\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "require 'simplecov'\nSimpleCov.start\n\nrequire 'rubygems'\nrequire 'rspec'\n\nrequire 'librarian/puppet'\n"
  },
  {
    "path": "spec/util_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Librarian::Puppet::Util do\n\n  subject { Class.new { include Librarian::Puppet::Util }.new }\n\n  it 'should get organization name' do\n    expect(subject.module_name('puppetlabs-xy')).to eq('xy')\n  end\n\n  it 'should get organization name when org contains dashes' do\n    expect(subject.module_name('puppet-labs-xy')).to eq('xy')\n  end\nend\n"
  },
  {
    "path": "test/librarian/puppet/source/githubtarball_test.rb",
    "content": "require File.expand_path(\"../../../../test_helper\", __FILE__)\nrequire \"librarian/puppet/source/githubtarball\"\n\ndescribe Librarian::Puppet::Source::GitHubTarball::Repo do\n  def assert_exact_error(klass, message)\n    yield\n  rescue Exception => e\n    e.class.must_equal klass\n    e.message.must_equal message\n  else\n    raise \"No exception was raised!\"\n  end\n\n  class FakeResponse\n    attr_accessor :code,:body\n    def initialize(code,body)\n      @code = code\n      @body = body\n    end\n    def [](key)\n      nil\n    end\n  end\n\n  describe \"#api_call\" do\n    let(:environment) { Librarian::Puppet::Environment.new }\n    let(:source) { Librarian::Puppet::Source::GitHubTarball.new(environment, \"foo\") }\n    let(:repo) { Librarian::Puppet::Source::GitHubTarball::Repo.new(source, \"bar\") }\n    let(:headers) { {'User-Agent' => \"librarian-puppet v#{Librarian::Puppet::VERSION}\"} }\n    let(:url) { \"https://api.github.com/foo?page=1&per_page=100\" }\n    let(:url_with_token) { \"https://api.github.com/foo?page=1&per_page=100&access_token=bar\" }\n    ENV['GITHUB_API_TOKEN'] = ''\n\n    it \"succeeds\" do\n      response = []\n      repo.expects(:http_get).with(url, {:headers => headers}).returns(FakeResponse.new(200, JSON.dump(response)))\n      repo.send(:api_call, \"/foo\").must_equal(response)\n    end\n\n    it \"adds GITHUB_API_TOKEN if present\" do\n      ENV['GITHUB_API_TOKEN'] = 'bar'\n      response = []\n      repo.expects(:http_get).with(url_with_token, {:headers => headers}).returns(FakeResponse.new(200, JSON.dump(response)))\n      repo.send(:api_call, \"/foo\").must_equal(response)\n      ENV['GITHUB_API_TOKEN'] = ''\n    end\n\n    it \"fails when we hit api limit\" do\n      response = {\"message\" => \"Oh boy! API rate limit exceeded!!!\"}\n      repo.expects(:http_get).with(url, {:headers => headers}).returns(FakeResponse.new(403, JSON.dump(response)))\n      message = \"Oh boy! API rate limit exceeded!!! -- increase limit by authenticating via GITHUB_API_TOKEN=your-token\"\n      assert_exact_error Librarian::Error, message do\n        repo.send(:api_call, \"/foo\")\n      end\n    end\n\n    it \"fails with unknown error message\" do\n      repo.expects(:http_get).with(url, {:headers => headers}).returns(FakeResponse.new(403, \"\"))\n      assert_exact_error Librarian::Error, \"Error fetching #{url}: [403] \" do\n        repo.send(:api_call, \"/foo\")\n      end\n    end\n\n    it \"fails with html\" do\n      repo.expects(:http_get).with(url, {:headers => headers}).returns(FakeResponse.new(403, \"<html>Oh boy!</html>\"))\n      assert_exact_error Librarian::Error, \"Error fetching #{url}: [403] <html>Oh boy!</html>\" do\n        repo.send(:api_call, \"/foo\")\n      end\n    end\n\n    it \"fails with unknown code\" do\n      repo.expects(:http_get).with(url, {:headers => headers}).returns(FakeResponse.new(500, \"\"))\n      assert_exact_error Librarian::Error, \"Error fetching #{url}: [500] \" do\n        repo.send(:api_call, \"/foo\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_helper.rb",
    "content": "require 'bundler/setup'\nrequire 'minitest/autorun'\nrequire 'minitest/spec'\nrequire 'mocha/setup'\n\n$LOAD_PATH << \"vendor/librarian/lib\"\nrequire 'librarian/puppet'\n"
  }
]