├── .devcontainer ├── Dockerfile ├── README.md └── devcontainer.json ├── .fixtures.yml ├── .gitattributes ├── .github ├── pull_request_template.md └── workflows │ ├── ci.yml │ ├── mend.yml │ ├── nightly.yml │ ├── release.yml │ └── release_prep.yml ├── .gitignore ├── .gitpod.Dockerfile ├── .gitpod.yml ├── .pdkignore ├── .puppet-lint.rc ├── .repo └── config ├── .rspec ├── .rubocop.yml ├── .rubocop_todo.yml ├── .sync.yml ├── .vscode └── extensions.json ├── .yardopts ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── Gemfile ├── HISTORY.md ├── LICENSE ├── MAINTAINERS.md ├── NOTICE ├── README.md ├── REFERENCE.md ├── Rakefile ├── data └── common.yaml ├── examples └── init.pp ├── hiera.yaml ├── lib ├── facter │ ├── choco_install_path.rb │ ├── choco_temp_dir.rb │ └── chocolateyversion.rb ├── puppet │ ├── provider │ │ ├── chocolateyconfig │ │ │ └── windows.rb │ │ ├── chocolateyfeature │ │ │ └── windows.rb │ │ ├── chocolateysource │ │ │ └── windows.rb │ │ └── package │ │ │ └── chocolatey.rb │ └── type │ │ ├── chocolateyconfig.rb │ │ ├── chocolateyfeature.rb │ │ └── chocolateysource.rb └── puppet_x │ └── chocolatey │ ├── chocolatey_common.rb │ ├── chocolatey_install.rb │ └── chocolatey_version.rb ├── locales └── config.yaml ├── manifests ├── config.pp ├── init.pp └── install.pp ├── metadata.json ├── pdk.yaml ├── provision.yaml ├── spec ├── acceptance │ ├── config_values_spec.rb │ ├── features_spec.rb │ ├── package_spec.rb │ └── sources_spec.rb ├── classes │ ├── config_spec.rb │ ├── init_spec.rb │ └── install_spec.rb ├── default_facts.yml ├── spec_helper.rb ├── spec_helper_acceptance.rb ├── spec_helper_acceptance_local.rb ├── spec_helper_local.rb ├── support │ ├── backup_and_reset_config_context.rb │ └── scripts │ │ ├── backup_config.ps1 │ │ └── reset_config.ps1 ├── tasks │ ├── chocolatey_outdated_task_spec.rb │ ├── chocolatey_pin_task_spec.rb │ ├── chocolatey_status_task_spec.rb │ └── chocolatey_task_spec.rb └── unit │ ├── facter │ ├── choco_install_path_spec.rb │ ├── choco_temp_dir_spec.rb │ └── chocolateyversion_spec.rb │ ├── puppet │ ├── provider │ │ ├── chocolateyconfig │ │ │ └── windows_spec.rb │ │ ├── chocolateyfeature │ │ │ └── windows_spec.rb │ │ ├── chocolateysource │ │ │ └── windows_spec.rb │ │ └── package │ │ │ └── chocolatey_spec.rb │ └── type │ │ ├── chocolateyconfig_spec.rb │ │ ├── chocolateyfeature_spec.rb │ │ └── chocolateysource_spec.rb │ └── puppet_x │ └── chocolatey │ ├── chocolatey_common_spec.rb │ ├── chocolatey_install_spec.rb │ └── chocolatey_version_spec.rb ├── tasks ├── init.json ├── init.rb ├── outdated.json ├── outdated.rb ├── pin.json ├── pin.rb ├── status.json └── status.rb └── templates └── InstallChocolatey.ps1.epp /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM puppet/pdk:latest 2 | 3 | # [Optional] Uncomment this section to install additional packages. 4 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | # && apt-get -y install --no-install-recommends 6 | 7 | -------------------------------------------------------------------------------- /.devcontainer/README.md: -------------------------------------------------------------------------------- 1 | # devcontainer 2 | 3 | 4 | For format details, see https://aka.ms/devcontainer.json. 5 | 6 | For config options, see the README at: 7 | https://github.com/microsoft/vscode-dev-containers/tree/v0.140.1/containers/puppet 8 | 9 | ``` json 10 | { 11 | "name": "Puppet Development Kit (Community)", 12 | "dockerFile": "Dockerfile", 13 | 14 | // Set *default* container specific settings.json values on container create. 15 | "settings": { 16 | "terminal.integrated.profiles.linux": { 17 | "bash": { 18 | "path": "bash", 19 | } 20 | } 21 | }, 22 | 23 | // Add the IDs of extensions you want installed when the container is created. 24 | "extensions": [ 25 | "puppet.puppet-vscode", 26 | "rebornix.Ruby" 27 | ], 28 | 29 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 30 | "forwardPorts": [], 31 | 32 | // Use 'postCreateCommand' to run commands after the container is created. 33 | "postCreateCommand": "pdk --version", 34 | } 35 | ``` 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Puppet Development Kit (Community)", 3 | "dockerFile": "Dockerfile", 4 | 5 | "settings": { 6 | "terminal.integrated.profiles.linux": { 7 | "bash": { 8 | "path": "bash" 9 | } 10 | } 11 | }, 12 | 13 | "extensions": [ 14 | "puppet.puppet-vscode", 15 | "rebornix.Ruby" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | forge_modules: 3 | stdlib: 'puppetlabs/stdlib' 4 | powershell: 'puppetlabs/powershell' 5 | registry: 'puppetlabs/registry' 6 | ruby_task_helper: 'puppetlabs/ruby_task_helper' 7 | repositories: 8 | facts: 'https://github.com/puppetlabs/puppetlabs-facts.git' 9 | puppet_agent: 10 | repo: 'https://github.com/puppetlabs/puppetlabs-puppet_agent.git' 11 | ref: v4.13.0 12 | provision: 'https://github.com/puppetlabs/provision.git' 13 | symlinks: 14 | chocolatey: "#{source_dir}" 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rb eol=lf 2 | *.pp eol=lf 3 | *.sh eol=lf 4 | *.epp eol=lf 5 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | Provide a detailed description of all the changes present in this pull request. 3 | 4 | ## Additional Context 5 | Add any additional context about the problem here. 6 | - [ ] Root cause and the steps to reproduce. (If applicable) 7 | - [ ] Thought process behind the implementation. 8 | 9 | ## Related Issues (if any) 10 | Mention any related issues or pull requests. 11 | 12 | ## Checklist 13 | - [ ] 🟢 Spec tests. 14 | - [ ] 🟢 Acceptance tests. 15 | - [ ] Manually verified. (For example `puppet apply`) -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "ci" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | setup_matrix: 11 | name: "Setup Test Matrix" 12 | runs-on: ubuntu-24.04 13 | outputs: 14 | spec_matrix: ${{ steps.get-matrix.outputs.spec_matrix }} 15 | 16 | steps: 17 | - name: Checkout Source 18 | uses: actions/checkout@v2 19 | 20 | - name: Activate Ruby 2.7 21 | uses: ruby/setup-ruby@v1 22 | with: 23 | ruby-version: "2.7" 24 | bundler-cache: true 25 | 26 | - name: Print bundle environment 27 | run: | 28 | echo ::group::bundler environment 29 | bundle env 30 | echo ::endgroup:: 31 | 32 | - name: Setup Spec Test Matrix 33 | id: get-matrix 34 | run: | 35 | bundle exec matrix_from_metadata_v2 36 | 37 | Spec: 38 | name: "Spec Tests (Puppet: ${{matrix.puppet_version}}, Ruby Ver: ${{matrix.ruby_version}})" 39 | needs: 40 | - setup_matrix 41 | if: ${{ needs.setup_matrix.outputs.spec_matrix != '{}' }} 42 | 43 | runs-on: ubuntu-24.04 44 | strategy: 45 | fail-fast: false 46 | matrix: ${{fromJson(needs.setup_matrix.outputs.spec_matrix)}} 47 | 48 | env: 49 | PUPPET_GEM_VERSION: ${{ matrix.puppet_version }} 50 | FACTER_GEM_VERSION: 'https://github.com/puppetlabs/facter#main' 51 | 52 | steps: 53 | 54 | - name: Checkout Source 55 | uses: actions/checkout@v2 56 | 57 | - name: "Activate Ruby ${{ matrix.ruby_version }}" 58 | uses: ruby/setup-ruby@v1 59 | with: 60 | ruby-version: ${{matrix.ruby_version}} 61 | bundler-cache: true 62 | 63 | - name: Print bundle environment 64 | run: | 65 | echo ::group::bundler environment 66 | bundle env 67 | echo ::endgroup:: 68 | 69 | - name: Create task helper symlink 70 | run: | 71 | ln -s "${PWD}/spec/fixtures/modules/ruby_task_helper" .. 72 | 73 | - name: "Run Static & Syntax Tests" 74 | run: | 75 | bundle exec rake syntax lint metadata_lint check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop 76 | 77 | - name: Run parallel_spec tests 78 | run: | 79 | bundle exec rake parallel_spec 80 | 81 | Acceptance: 82 | needs: Spec 83 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_acceptance.yml@main" 84 | secrets: "inherit" 85 | -------------------------------------------------------------------------------- /.github/workflows/mend.yml: -------------------------------------------------------------------------------- 1 | name: "mend" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "main" 7 | schedule: 8 | - cron: "0 0 * * *" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | 13 | mend: 14 | uses: "puppetlabs/cat-github-actions/.github/workflows/mend_ruby.yml@main" 15 | secrets: "inherit" 16 | -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: "nightly" 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | setup_matrix: 10 | name: "Setup Test Matrix" 11 | runs-on: ubuntu-24.04 12 | outputs: 13 | spec_matrix: ${{ steps.get-matrix.outputs.spec_matrix }} 14 | 15 | steps: 16 | - name: Checkout Source 17 | uses: actions/checkout@v2 18 | 19 | - name: Activate Ruby 2.7 20 | uses: ruby/setup-ruby@v1 21 | with: 22 | ruby-version: "2.7" 23 | bundler-cache: true 24 | 25 | - name: Print bundle environment 26 | run: | 27 | echo ::group::bundler environment 28 | bundle env 29 | echo ::endgroup:: 30 | 31 | - name: Setup Spec Test Matrix 32 | id: get-matrix 33 | run: | 34 | bundle exec matrix_from_metadata_v2 35 | 36 | Spec: 37 | name: "Spec Tests (Puppet: ${{matrix.puppet_version}}, Ruby Ver: ${{matrix.ruby_version}})" 38 | needs: 39 | - setup_matrix 40 | if: ${{ needs.setup_matrix.outputs.spec_matrix != '{}' }} 41 | 42 | runs-on: ubuntu-24.04 43 | strategy: 44 | fail-fast: false 45 | matrix: ${{fromJson(needs.setup_matrix.outputs.spec_matrix)}} 46 | 47 | env: 48 | PUPPET_GEM_VERSION: ${{ matrix.puppet_version }} 49 | FACTER_GEM_VERSION: 'https://github.com/puppetlabs/facter#main' 50 | 51 | steps: 52 | 53 | - name: Checkout Source 54 | uses: actions/checkout@v2 55 | 56 | - name: "Activate Ruby ${{ matrix.ruby_version }}" 57 | uses: ruby/setup-ruby@v1 58 | with: 59 | ruby-version: ${{matrix.ruby_version}} 60 | bundler-cache: true 61 | 62 | - name: Print bundle environment 63 | run: | 64 | echo ::group::bundler environment 65 | bundle env 66 | echo ::endgroup:: 67 | 68 | - name: Create task helper symlink 69 | run: | 70 | ln -s "${PWD}/spec/fixtures/modules/ruby_task_helper" .. 71 | 72 | - name: "Run Static & Syntax Tests" 73 | run: | 74 | bundle exec rake syntax lint metadata_lint check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop 75 | 76 | - name: Run parallel_spec tests 77 | run: | 78 | bundle exec rake parallel_spec 79 | 80 | Acceptance: 81 | needs: Spec 82 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_acceptance.yml@main" 83 | secrets: "inherit" 84 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "Publish module" 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | release: 8 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_release.yml@main" 9 | secrets: "inherit" 10 | -------------------------------------------------------------------------------- /.github/workflows/release_prep.yml: -------------------------------------------------------------------------------- 1 | name: "Release Prep" 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "Module version to be released. Must be a valid semver string. (1.2.3)" 8 | required: true 9 | 10 | jobs: 11 | release_prep: 12 | uses: "puppetlabs/cat-github-actions/.github/workflows/module_release_prep.yml@main" 13 | with: 14 | version: "${{ github.event.inputs.version }}" 15 | secrets: "inherit" 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .*.sw[op] 3 | .metadata 4 | .yardoc 5 | .yardwarns 6 | *.iml 7 | /.bundle/ 8 | /.idea/ 9 | /.vagrant/ 10 | /coverage/ 11 | /bin/ 12 | /doc/ 13 | /Gemfile.local 14 | /Gemfile.lock 15 | /junit/ 16 | /log/ 17 | /pkg/ 18 | /spec/fixtures/manifests/ 19 | /spec/fixtures/modules/* 20 | /tmp/ 21 | /vendor/ 22 | /.vendor/ 23 | /convert_report.txt 24 | /update_report.txt 25 | .DS_Store 26 | .project 27 | .envrc 28 | /inventory.yaml 29 | /spec/fixtures/litmus_inventory.yaml 30 | .resource_types 31 | .modules 32 | .task_cache.json 33 | .plan_cache.json 34 | .rerun.json 35 | bolt-debug.log 36 | -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full 2 | RUN sudo wget https://apt.puppet.com/puppet-tools-release-bionic.deb && \ 3 | wget https://apt.puppetlabs.com/puppet6-release-bionic.deb && \ 4 | sudo dpkg -i puppet6-release-bionic.deb && \ 5 | sudo dpkg -i puppet-tools-release-bionic.deb && \ 6 | sudo apt-get update && \ 7 | sudo apt-get install -y pdk zsh puppet-agent && \ 8 | sudo apt-get clean && \ 9 | sudo rm -rf /var/lib/apt/lists/* 10 | RUN sudo usermod -s $(which zsh) gitpod && \ 11 | sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" && \ 12 | echo "plugins=(git gitignore github gem pip bundler python ruby docker docker-compose)" >> /home/gitpod/.zshrc && \ 13 | echo 'PATH="$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/puppetlabs/bin:/opt/puppetlabs/puppet/bin"' >> /home/gitpod/.zshrc && \ 14 | sudo /opt/puppetlabs/puppet/bin/gem install puppet-debugger hub -N && \ 15 | mkdir -p /home/gitpod/.config/puppet && \ 16 | /opt/puppetlabs/puppet/bin/ruby -r yaml -e "puts ({'disabled' => true}).to_yaml" > /home/gitpod/.config/puppet/analytics.yml 17 | RUN rm -f puppet6-release-bionic.deb puppet-tools-release-bionic.deb 18 | ENTRYPOINT /usr/bin/zsh 19 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod.Dockerfile 3 | 4 | tasks: 5 | - init: pdk bundle install 6 | 7 | vscode: 8 | extensions: 9 | - puppet.puppet-vscode@1.2.0:f5iEPbmOj6FoFTOV6q8LTg== 10 | -------------------------------------------------------------------------------- /.pdkignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .*.sw[op] 3 | .metadata 4 | .yardoc 5 | .yardwarns 6 | *.iml 7 | /.bundle/ 8 | /.idea/ 9 | /.vagrant/ 10 | /coverage/ 11 | /bin/ 12 | /doc/ 13 | /Gemfile.local 14 | /Gemfile.lock 15 | /junit/ 16 | /log/ 17 | /pkg/ 18 | /spec/fixtures/manifests/ 19 | /spec/fixtures/modules/* 20 | /tmp/ 21 | /vendor/ 22 | /.vendor/ 23 | /convert_report.txt 24 | /update_report.txt 25 | .DS_Store 26 | .project 27 | .envrc 28 | /inventory.yaml 29 | /spec/fixtures/litmus_inventory.yaml 30 | .resource_types 31 | .modules 32 | .task_cache.json 33 | .plan_cache.json 34 | .rerun.json 35 | bolt-debug.log 36 | /.fixtures.yml 37 | /Gemfile 38 | /.gitattributes 39 | /.github/ 40 | /.gitignore 41 | /.pdkignore 42 | /.puppet-lint.rc 43 | /Rakefile 44 | /rakelib/ 45 | /.rspec 46 | /..yml 47 | /.yardopts 48 | /spec/ 49 | /.vscode/ 50 | /.sync.yml 51 | /.devcontainer/ 52 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --relative 2 | -------------------------------------------------------------------------------- /.repo/config: -------------------------------------------------------------------------------- 1 | # vim: set ts=2 sw=2 ai et ruler: 2 | [push] 3 | default = tracking # push to tracking branch by default 4 | 5 | [remote "upstream"] 6 | url = git@github.com:chocolatey/puppet-chocolatey.git 7 | fetch = +refs/heads/*:refs/remotes/upstream/* 8 | 9 | [remote "rismoney"] 10 | url = git@github.com:rismoney/puppet-chocolatey.git 11 | fetch = +refs/heads/*:refs/remotes/rismoney/* 12 | 13 | [remote "jumanjiman"] 14 | url = git@github.com:jumanjiman/puppet-chocolatey.git 15 | fetch = +refs/heads/*:refs/remotes/jumanjiman/* 16 | 17 | [alias] 18 | # maintainer should use these when reviewing PRs 19 | authors = shortlog -sn 20 | behind = !git log ..upstream/main --oneline 21 | ahead = !git log upstream/main.. --oneline 22 | unmerged = !git cherry upstream/main 23 | files = !git diff --name-only upstream/main.. 24 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2023-11-28 14:12:00 UTC using RuboCop version 1.48.1. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 4 10 | # Configuration parameters: EnforcedStyle, IgnoreSharedExamples. 11 | # SupportedStyles: always, named_only 12 | RSpec/NamedSubject: 13 | Exclude: 14 | - 'spec/classes/config_spec.rb' 15 | - 'spec/classes/install_spec.rb' 16 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ".gitattributes": 3 | exclude: 4 | - "*.erb" 5 | ".gitlab-ci.yml": 6 | delete: true 7 | appveyor.yml: 8 | delete: true 9 | .rubocop.yml: 10 | include_todos: true 11 | 12 | Gemfile: 13 | required: 14 | ":system_tests": 15 | - gem: nokogiri 16 | platforms: 17 | - ruby 18 | - mswin 19 | - mingw 20 | - x64_mingw 21 | optional: 22 | ":development": 23 | - gem: ruby-pwsh 24 | spec/spec_helper.rb: 25 | mock_with: ":rspec" 26 | coverage_report: true 27 | .gitpod.Dockerfile: 28 | unmanaged: false 29 | .gitpod.yml: 30 | unmanaged: false 31 | .github/workflows/auto_release.yml: 32 | unmanaged: false 33 | .github/workflows/ci.yml: 34 | unmanaged: true 35 | .github/workflows/nightly.yml: 36 | unmanaged: true 37 | .github/workflows/release.yml: 38 | unmanaged: false 39 | .travis.yml: 40 | delete: true 41 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "puppet.puppet-vscode", 4 | "Shopify.ruby-lsp" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Setting ownership to the modules team 2 | * @puppetlabs/modules 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Puppet modules 2 | 3 | Check out our [Contributing to Supported Modules Blog Post](https://puppetlabs.github.io/iac/docs/contributing_to_a_module.html) to find all the information that you will need. 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || 'https://rubygems.org' 2 | 3 | def location_for(place_or_version, fake_version = nil) 4 | git_url_regex = %r{\A(?(https?|git)[:@][^#]*)(#(?.*))?} 5 | file_url_regex = %r{\Afile:\/\/(?.*)} 6 | 7 | if place_or_version && (git_url = place_or_version.match(git_url_regex)) 8 | [fake_version, { git: git_url[:url], branch: git_url[:branch], require: false }].compact 9 | elsif place_or_version && (file_url = place_or_version.match(file_url_regex)) 10 | ['>= 0', { path: File.expand_path(file_url[:path]), require: false }] 11 | else 12 | [place_or_version, { require: false }] 13 | end 14 | end 15 | 16 | group :development do 17 | gem "json", '= 2.1.0', require: false if Gem::Requirement.create(['>= 2.5.0', '< 2.7.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 18 | gem "json", '= 2.3.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 19 | gem "json", '= 2.5.1', require: false if Gem::Requirement.create(['>= 3.0.0', '< 3.0.5']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 20 | gem "json", '= 2.6.1', require: false if Gem::Requirement.create(['>= 3.1.0', '< 3.1.3']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 21 | gem "json", '= 2.6.3', require: false if Gem::Requirement.create(['>= 3.2.0', '< 4.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 22 | gem "racc", '~> 1.4.0', require: false if Gem::Requirement.create(['>= 2.7.0', '< 3.0.0']).satisfied_by?(Gem::Version.new(RUBY_VERSION.dup)) 23 | gem "deep_merge", '~> 1.2.2', require: false 24 | gem "voxpupuli-puppet-lint-plugins", '~> 5.0', require: false 25 | gem "facterdb", '~> 2.1', require: false 26 | gem "metadata-json-lint", '~> 4.0', require: false 27 | gem "rspec-puppet-facts", '~> 4.0', require: false 28 | gem "dependency_checker", '~> 1.0.0', require: false 29 | gem "parallel_tests", '= 3.12.1', require: false 30 | gem "pry", '~> 0.10', require: false 31 | gem "simplecov-console", '~> 0.9', require: false 32 | gem "puppet-debugger", '~> 1.0', require: false 33 | gem "rubocop", '~> 1.50.0', require: false 34 | gem "rubocop-performance", '= 1.16.0', require: false 35 | gem "rubocop-rspec", '= 2.19.0', require: false 36 | gem "rb-readline", '= 0.5.5', require: false, platforms: [:mswin, :mingw, :x64_mingw] 37 | gem "rexml", '>= 3.3.9', require: false 38 | gem "ruby-pwsh", require: false 39 | end 40 | group :development, :release_prep do 41 | gem "puppet-strings", '~> 4.0', require: false 42 | gem "puppetlabs_spec_helper", '~> 7.0', require: false 43 | end 44 | group :system_tests do 45 | gem "puppet_litmus", '~> 1.0', require: false, platforms: [:ruby, :x64_mingw] 46 | gem "CFPropertyList", '< 3.0.7', require: false, platforms: [:mswin, :mingw, :x64_mingw] 47 | gem "serverspec", '~> 2.41', require: false 48 | gem "nokogiri", require: false, platforms: [:ruby, :mswin, :mingw, :x64_mingw] 49 | end 50 | 51 | puppet_version = ENV['PUPPET_GEM_VERSION'] 52 | facter_version = ENV['FACTER_GEM_VERSION'] 53 | hiera_version = ENV['HIERA_GEM_VERSION'] 54 | 55 | gems = {} 56 | 57 | gems['puppet'] = location_for(puppet_version) 58 | 59 | # If facter or hiera versions have been specified via the environment 60 | # variables 61 | 62 | gems['facter'] = location_for(facter_version) if facter_version 63 | gems['hiera'] = location_for(hiera_version) if hiera_version 64 | 65 | gems.each do |gem_name, gem_params| 66 | gem gem_name, *gem_params 67 | end 68 | 69 | # Evaluate Gemfile.local and ~/.gemfile if they exist 70 | extra_gemfiles = [ 71 | "#{__FILE__}.local", 72 | File.join(Dir.home, '.gemfile'), 73 | ] 74 | 75 | extra_gemfiles.each do |gemfile| 76 | if File.file?(gemfile) && File.readable?(gemfile) 77 | eval(File.read(gemfile), binding) 78 | end 79 | end 80 | # vim: syntax=ruby 81 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | ## 3.3.0 2 | 3 | ### Added 4 | 5 | - Warning note to package parameter documentation for `install_options` to clarify best practices for secrets management with this parameter ([MODULES-8491](https://tickets.puppetlabs.com/browse/MODULES-8491)). 6 | - Parameter `seven_zip_download_url` to make the source of the 7zip binary configurable, allowing the use of this module when the previously hardcoded URL cannot be reached ([MODULES-6652](https://tickets.puppetlabs.com/browse/MODULES-6652)). Thanks, [Daniel Helgenberger](https://github.com/helge000)! 7 | 8 | ### Fixed 9 | 10 | - Ensure that `puppet resource chocolateyconfig` runs without erroring ([MODULES-8047].(https://tickets.puppetlabs.com/browse/MODULES-8047)). 11 | 12 | ## [3.2.0] - 2019-02-19 13 | 14 | ### Added 15 | 16 | - Configuration option for `chocolateysource` to allow bypassing any system-configured proxies ([MODULES-4418](https://tickets.puppetlabs.com/browse/MODULES-4418)). 17 | - Configuration option for `chocolateysource` to make a source visible only to Windows users in the Administrators group ([MODULES-5898](https://tickets.puppetlabs.com/browse/MODULES-5898)). 18 | - Configuration option for `chocolateysource` to make a source usable with Chocolatey Self Service ([MODULES-5897](https://tickets.puppetlabs.com/browse/MODULES-5897)) 19 | - Install Chocolatey from behind a proxy. ([MODULES-5654](https://tickets.puppetlabs.com/browse/MODULES-5654)) Thanks, [Geoff Williams](https://github.com/GeoffWilliams) 20 | 21 | ### Fixed 22 | 23 | - Ensure the `source` syntax in the provider is correct ([MODULES-8493](https://tickets.puppetlabs.com/browse/MODULES-8493)). Thanks, [@jcwest](https://github.com/jcwest)! 24 | - Ensure that if `usePackageExitCodes` is explicitly set to `true` in the Chocolatey configuration then it is observed ([MODULES-5383](https://tickets.puppetlabs.com/browse/MODULES-5383)). Thanks, [David Wood](https://github.com/davidtwco)! 25 | - Only initialize constant when not defined. ([MODULES-7068](https://tickets.puppetlabs.com/browse/MODULES-7068)). Thanks, [Bas Toonk](https://github.com/btoonk)! 26 | - Fix collision in type generation ([MODULES-6948](https://tickets.puppetlabs.com/browse/MODULES-6948)). Thanks [Rico Spiess](https://github.com/rico89)! 27 | 28 | ## [3.1.1] - 2018-12-10 29 | 30 | ### Changed 31 | 32 | - Changelog converted to KAC format 33 | 34 | ### Fixed 35 | 36 | - Already Initialized Constant Warning ([MODULES-5859](https://tickets.puppetlabs.com/browse/MODULES-5859)). Thanks Paul Reed ([@psreed](https://github.com/psreed)) 37 | 38 | ## [3.1.0] - 2018-10-10 39 | 40 | ### Fixed 41 | 42 | - Choco version rendering error ([MODULES-5788](https://tickets.puppetlabs.com/browse/MODULES-5788)) 43 | - Convert tests to rspec format ([MODULES-6746](https://tickets.puppetlabs.com/browse/MODULES-6746)) 44 | 45 | ### Added 46 | 47 | - Add support for Puppet 5 ([MODULES-5144](https://tickets.puppetlabs.com/browse/MODULES-5144)) 48 | - Add support for Server 2016 ([MODULES-4271](https://tickets.puppetlabs.com/browse/MODULES-4271)) 49 | - Add support for Puppet 6 ([MODULES-7832](https://tickets.puppetlabs.com/browse/MODULES-7832)) 50 | - Add Beaker Testmode Switcher ([MODULES-6734](https://tickets.puppetlabs.com/browse/MODULES-6734)) 51 | 52 | ### Changed 53 | 54 | - Convert module for PDK ([MODULES-7398](https://tickets.puppetlabs.com/browse/MODULES-7398)) 55 | - Update Stdlib to 6.0.0 ([MODULES-7705](https://tickets.puppetlabs.com/browse/MODULES-7705)) 56 | 57 | ## [3.0.0] - 2017-06-02 58 | 59 | ### Fixed 60 | 61 | - Explicitly close configuration files after reading ([MODULES-4678](https://tickets.puppetlabs.com/browse/MODULES-4678)) 62 | - Use actual choco.exe instead of the shim ([MODULES-4562](https://tickets.puppetlabs.com/browse/MODULES-4562)) 63 | - Updated Puppet version compatibility for modern Puppet agents ([MODULES-4846](https://tickets.puppetlabs.com/browse/MODULES-4846)) 64 | 65 | ## [2.0.2] - 2017-04-04 66 | 67 | ### Fixed 68 | 69 | - Use two dashes when getting the package version ([MODULES-4508](https://tickets.puppetlabs.com/browse/MODULES-4508)) 70 | 71 | ## [2.0.1] - 2017-01-03 72 | 73 | ### Fixed 74 | 75 | - ChocolateyInstall environment variable not set for alternate installation directory ([MODULES-4091](https://tickets.puppetlabs.com/browse/MODULES-4091)) 76 | - Unsuitable providers should not cause errors ([MODULES-4149](https://tickets.puppetlabs.com/browse/MODULES-4149)) 77 | - Version is malformed with any extraneous messages ([MODULES-4135](https://tickets.puppetlabs.com/browse/MODULES-4135)) 78 | - Module does not propagate null source error correctly ([MODULES-4056](https://tickets.puppetlabs.com/browse/MODULES-4056)) 79 | - Install fails on Windows 10 when using built-in compression ([MODULES-4210](https://tickets.puppetlabs.com/browse/MODULES-4210)) 80 | 81 | ### Added 82 | 83 | - Document considerations for install to "C:\Chocolatey" ([MODULES-4090](https://tickets.puppetlabs.com/browse/MODULES-4090)) 84 | 85 | ### Changed 86 | 87 | - Set TLS 1.1+ when available 88 | 89 | ## [2.0.0] - 2016-09-29 90 | 91 | ### Added 92 | 93 | - `chocolateysource` - explicitly require location in ensure ([MODULES-3430](https://tickets.puppet.com/browse/MODULES-3430)) 94 | - Supported tag on the forge. 95 | 96 | ### Changed 97 | 98 | - set ignore package exit codes when Chocolatey is on version 0.9.10+ ([MODULES-3880](https://tickets.puppet.com/browse/MODULES-3880)) 99 | 100 | ### Fixed 101 | 102 | - Ensure config file exists before `chocolateyfeature`, `chocolateyconfig`, or `chocolateysource` ([MODULES-3677](https://tickets.puppet.com/browse/MODULES-3677)) 103 | - `chocolateysource` - ensure flush when disabling source ([MODULES-3430](https://tickets.puppet.com/browse/MODULES-3430)) 104 | - `chocolateysource` - erroneous user sync messages ([MODULES-3758](https://tickets.puppet.com/browse/MODULES-3758)) 105 | 106 | ## [0.8.0] - Unsupported 2016-07-13 107 | 108 | ### Added 109 | 110 | - Includes community module releases up to 1.2.6 (changelog below). 111 | - Manage features - `chocolateyfeature` - see [MODULES-3034](https://tickets.puppet.com/browse/MODULES-3034) 112 | - Manage config settings - `chocolateyconfig` - see [MODULES-3035](https://tickets.puppet.com/browse/MODULES-3035) 113 | 114 | ## [0.7.0] - Unsupported 2016-06-01 115 | 116 | ### Added 117 | 118 | - Manage sources - `chocolateysource` - see [MODULES-3037](https://tickets.puppetlabs.com/browse/MODULES-3037) 119 | - Includes community module releases up to 1.2.1 (changelog below up to 1.2.1) 120 | 121 | ### Fixed 122 | 123 | - `$::chocolateyversion` fact is optional - see [#110](https://github.com/chocolatey/puppet-chocolatey/issues/110) 124 | - puppet apply works again - see [#105](https://github.com/chocolatey/puppet-chocolatey/issues/105) 125 | 126 | # Note: 127 | The puppetlabs-chocolatey module replaces the community chocolatey-chocolatey module. We have retained its changelog below as there were a couple of releases where we tracked the puppetlabs-chocolatey modules changes and bug fixes here. 128 | 129 | ## [1.2.6] - 2016-07-11 130 | 131 | ### Fixed 132 | 133 | - AutoUninstaller runs every time in 0.9.9.x [#134](https://github.com/chocolatey/puppet-chocolatey/issues/134) 134 | 135 | ## [1.2.5] - 2016-06-20 136 | 137 | ### Changed 138 | 139 | - Support feature list changes in v0.9.10+ [#133](https://github.com/chocolatey/puppet-chocolatey/issues/133) 140 | 141 | ### Fixed 142 | 143 | - Chocolatey fails to install in PowerShell v2 with PowerShell Module 1.x [#128](https://github.com/chocolatey/puppet-chocolatey/issues/128) 144 | 145 | ## [1.2.4] - 2016-06-04 146 | 147 | ### Added 148 | 149 | - Compatibility with puppetlabs-powershell 2.x [#125](https://github.com/chocolatey/puppet-chocolatey/issues/125). 150 | 151 | ## [1.2.3] - 2016-05-06 152 | 153 | ### Added 154 | 155 | - Announce [Chocolatey for Business](https://chocolatey.org/compare) in ReadMe. 156 | 157 | ### Changed 158 | 159 | - Do not call choco with --debug --verbose by default [#100](https://github.com/chocolatey/puppet-chocolatey/issues/100). 160 | 161 | ## [1.2.2] - 2016-04-06 162 | 163 | ### Changed 164 | 165 | - `$::chocolateyversion` fact is optional - see [#110](https://github.com/chocolatey/puppet-chocolatey/issues/110) 166 | 167 | ### Fixed 168 | 169 | - puppet apply works again [#105](https://github.com/chocolatey/puppet-chocolatey/issues/105). 170 | - Implement PowerShell Redirection Fix for Windows 2008 / PowerShell v2 - see [#119](https://github.com/chocolatey/puppet-chocolatey/issues/119) 171 | 172 | ## [1.2.1] - 2015-12-08 173 | 174 | ### Added 175 | 176 | - Support for newer versions of PE 177 | 178 | ## [1.2.0] - 2015-11-03 179 | 180 | ### Added 181 | 182 | - holdable ([#95](https://github.com/chocolatey/puppet-chocolatey/issues/95)) 183 | 184 | ### Fixed 185 | 186 | - Use install unless the version is specified in install ([#71](https://github.com/chocolatey/puppet-chocolatey/issues/71)) 187 | 188 | ## [1.1.2] - 2015-10-02 189 | 190 | ### Fixed 191 | 192 | - Ensure 0.9.9.9 compatibility ([#94](https://github.com/chocolatey/puppet-chocolatey/issues/94)) 193 | - Mixed stale environment variables of existing choco install causing issues ([#86](https://github.com/chocolatey/puppet-chocolatey/issues/86)) 194 | - Upgrade From POSH Version of Chocolatey Fails from Puppet ([#60](https://github.com/chocolatey/puppet-chocolatey/issues/60)) 195 | 196 | ## [1.1.1] - 2015-09-25 197 | 198 | ### Added 199 | 200 | - Add log_output for chocolatey bootstrap installer script 201 | - Allow file location for installing nupkg file. 202 | 203 | ### Changed 204 | 205 | - Ensure bootstrap enforces chocolatey.nupkg in libs folder 206 | 207 | ### [1.1.0] - 2015-09-09 208 | 209 | - Install Chocolatey itself / ensure Chocolatey is installed ([PUP-1691](https://tickets.puppetlabs.com/browse/PUP-1691)) 210 | - Custom facts for chocolateyversion and choco_install_path 211 | 212 | ## [1.0.2] - 2015-07-23 213 | 214 | ### Fixed 215 | 216 | - Allow `ensure => $version` to work with already installed packages [#71](https://github.com/chocolatey/puppet-chocolatey/issues/71) 217 | 218 | ## [1.0.1] - 2015-07-01 219 | 220 | ### Fixed 221 | 222 | - Check for choco existence more comprehensively [#66](https://github.com/chocolatey/puppet-chocolatey/issues/66) 223 | 224 | ## [1.0.0] - 2015-06-08 225 | 226 | ## [0.5.3] - 2015-05-22 227 | 228 | ### Changed 229 | 230 | - Update ReadMe - fix/clarify how options with quotes need to be passed. 231 | 232 | ### Fixed 233 | 234 | - Manifest issue 235 | - Choco path issue 236 | 237 | ## [0.5.2] - 2015-04-23 238 | 239 | ### Changed 240 | 241 | - Readme 242 | 243 | ### Added 244 | 245 | - Support for Windows 10 246 | 247 | ### Fixed 248 | 249 | - Avoiding Puppet returning 2 instead of 0 when there are no changes to be made [#56](https://github.com/chocolatey/puppet-chocolatey/pull/56) 250 | 251 | ## [0.5.1] - 2015-03-31 252 | 253 | ### Fixed 254 | 255 | - Blocking: Linux servers throw error if the module is present [#54](https://github.com/chocolatey/puppet-chocolatey/issues/54) 256 | 257 | ## [0.5.0] - 2015-03-30 258 | 259 | ### Added 260 | 261 | - Provider enhancements 262 | - Better docs 263 | - Works with both compiled and powershell Chocolatey clients 264 | - Document best way to pass options with spaces (#15 also related) - [#52](https://github.com/chocolatey/puppet-chocolatey/issues/52) 265 | - Document Chocolatey needs to be installed by other means - [#26](https://github.com/chocolatey/puppet-chocolatey/issues/26) 266 | 267 | ### Fixed 268 | 269 | - work with newer compiled Chocolatey client (0.9.9+) - [#50](https://github.com/chocolatey/puppet-chocolatey/issues/50) 270 | - check for installed packages that are case sensitive - [#43](https://github.com/chocolatey/puppet-chocolatey/issues/43) 271 | - The OS handle's position is not what FileStream expected. - [#18](https://github.com/chocolatey/puppet-chocolatey/issues/18) 272 | 273 | ## [0.3] 274 | 275 | ## [0.2] 276 | 277 | [3.3.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/3.2.0...3.3.0 278 | [3.2.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/3.1.1...3.2.0 279 | [3.1.1]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/3.1.0...3.1.1 280 | [3.1.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/3.0.0...3.1.0 281 | [3.0.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/2.0.2...3.0.0 282 | [2.0.2]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/2.0.1...2.0.2 283 | [2.0.1]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/2.0.0...2.0.1 284 | [2.0.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.6...2.0.0 285 | [1.2.6]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.5...1.2.6 286 | [1.2.5]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.4...1.2.5 287 | [1.2.4]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.3...1.2.4 288 | [1.2.3]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.2...1.2.3 289 | [1.2.2]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.1...1.2.2 290 | [1.2.1]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.2.0...1.2.1 291 | [1.2.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.1.2...1.2.0 292 | [1.1.2]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.1.1...1.1.2 293 | [1.1.1]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.1.0...1.1.1 294 | [1.1.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.0.2...1.1.0 295 | [1.0.2]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.0.1...1.0.2 296 | [1.0.1]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/1.0.0...1.0.1 297 | [1.0.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.8.0...1.0.0 298 | [0.8.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.7.0...0.8.0 299 | [0.7.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.5.3...0.7.0 300 | [0.5.3]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.5.2...0.5.3 301 | [0.5.2]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.5.1...0.5.2 302 | [0.5.1]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.5.0...0.5.1 303 | [0.5.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.4.0...0.5.0 304 | [0.4.0]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.3...0.4.0 305 | [0.3]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/0.2...0.3 306 | [0.2]: https://github.com/puppetlabs/puppetlabs-chocolatey/compare/af285ea8dbb2b9dd2a08c5374f174cc73ca19e3b...0.2 307 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | ## Maintenance 2 | 3 | Maintainers: 4 | - Puppet Windows Team `windows |at| puppet |dot| com` 5 | 6 | Tickets: https://tickets.puppet.com/browse/MODULES. Make sure to set component to `chocolatey`. 7 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Puppet Module - puppetlabs-chocolatey 2 | 3 | Copyright 2012 - 2014 Rich Siegel, Paul Morgan 4 | Copyright 2014 - 2016 RealDimensions Software, LLC & original authors/contributors from https://github.com/chocolatey/puppet-chocolatey 5 | Copyright 2016 Puppet, Inc. 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. -------------------------------------------------------------------------------- /REFERENCE.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | 4 | 5 | ## Table of Contents 6 | 7 | ### Classes 8 | 9 | #### Public Classes 10 | 11 | * [`chocolatey`](#chocolatey): Used for managing installation and configuration of Chocolatey itself. 12 | 13 | #### Private Classes 14 | 15 | * `chocolatey::config`: Handles configuration of Chocolatey 16 | * `chocolatey::install`: Handles installation of Chocolatey 17 | 18 | ### Resource types 19 | 20 | * [`chocolateyconfig`](#chocolateyconfig): Allows managing config settings for Chocolatey. Configuration values provide settings for users to configure aspects of Chocolatey and the wa 21 | * [`chocolateyfeature`](#chocolateyfeature): Allows managing features for Chocolatey. Features are configuration that act as feature flippers to turn on or off certain aspects of how Cho 22 | * [`chocolateysource`](#chocolateysource): Allows managing sources for Chocolatey. A source can be a folder, a CIFS share, a NuGet Http OData feed, or a full Package Gallery. Learn mor 23 | 24 | ### Tasks 25 | 26 | * [`init`](#init): Manage a package 27 | * [`outdated`](#outdated): List outdated packages 28 | * [`pin`](#pin): Manage package pinning 29 | * [`status`](#status): List currently installed packages 30 | 31 | ## Classes 32 | 33 | ### `chocolatey` 34 | 35 | Used for managing installation and configuration of Chocolatey itself. 36 | 37 | #### Examples 38 | 39 | ##### Default - This will by default ensure Chocolatey is installed and ready for use. 40 | 41 | ```puppet 42 | include chocolatey 43 | ``` 44 | 45 | ##### Override default install location 46 | 47 | ```puppet 48 | class {'chocolatey': 49 | choco_install_location => 'D:\secured\choco', 50 | } 51 | ``` 52 | 53 | ##### Use an internal Chocolatey.nupkg for installation 54 | 55 | ```puppet 56 | class {'chocolatey': 57 | chocolatey_download_url => 'https://internalurl/to/chocolatey.nupkg', 58 | use_7zip => false, 59 | choco_install_timeout_seconds => 2700, 60 | } 61 | ``` 62 | 63 | ##### Use a file chocolatey.0.9.9.9.nupkg for installation 64 | 65 | ```puppet 66 | class {'chocolatey': 67 | chocolatey_download_url => 'file:///c:/location/of/chocolatey.0.9.9.9.nupkg', 68 | use_7zip => false, 69 | choco_install_timeout_seconds => 2700, 70 | } 71 | ``` 72 | 73 | ##### Log chocolatey bootstrap installer script output 74 | 75 | ```puppet 76 | class {'chocolatey': 77 | log_output => true, 78 | } 79 | ``` 80 | 81 | ##### Disable autouninstaller (use when less than 0.9.9.8) 82 | 83 | ```puppet 84 | class {'chocolatey': 85 | enable_autouninstaller => false, 86 | } 87 | ``` 88 | 89 | #### Parameters 90 | 91 | The following parameters are available in the `chocolatey` class: 92 | 93 | * [`choco_install_location`](#-chocolatey--choco_install_location) 94 | * [`use_7zip`](#-chocolatey--use_7zip) 95 | * [`seven_zip_download_url`](#-chocolatey--seven_zip_download_url) 96 | * [`choco_install_timeout_seconds`](#-chocolatey--choco_install_timeout_seconds) 97 | * [`chocolatey_download_url`](#-chocolatey--chocolatey_download_url) 98 | * [`enable_autouninstaller`](#-chocolatey--enable_autouninstaller) 99 | * [`log_output`](#-chocolatey--log_output) 100 | * [`chocolatey_version`](#-chocolatey--chocolatey_version) 101 | * [`install_proxy`](#-chocolatey--install_proxy) 102 | 103 | ##### `choco_install_location` 104 | 105 | Data type: `Stdlib::Windowspath` 106 | 107 | Where Chocolatey install should be 108 | located. This needs to be an absolute path starting with a drive letter 109 | e.g. `c:\`. Defaults to the currently detected install location based on 110 | the `ChocolateyInstall` environment variable, falls back to 111 | `'C:\ProgramData\chocolatey'`. 112 | 113 | Default value: `$facts['choco_install_path']` 114 | 115 | ##### `use_7zip` 116 | 117 | Data type: `Boolean` 118 | 119 | Whether to use built-in shell or allow installer 120 | to download 7zip to extract `chocolatey.nupkg` during installation. 121 | Defaults to `false`. 122 | 123 | Default value: `false` 124 | 125 | ##### `seven_zip_download_url` 126 | 127 | Data type: `String[1]` 128 | 129 | Specifies the source file for 7za.exe. 130 | Supports all sources supported by Puppet's file resource. You should use 131 | a 32bit binary for compatibility. 132 | Defaults to 'https://chocolatey.org/7za.exe'. 133 | 134 | Default value: `'https://chocolatey.org/7za.exe'` 135 | 136 | ##### `choco_install_timeout_seconds` 137 | 138 | Data type: `Integer` 139 | 140 | How long in seconds should 141 | be allowed for the install of Chocolatey (including .NET Framework 4 if 142 | necessary). Defaults to `1500` (25 minutes). 143 | 144 | Default value: `1500` 145 | 146 | ##### `chocolatey_download_url` 147 | 148 | Data type: `Stdlib::Filesource` 149 | 150 | A url that will return 151 | `chocolatey.nupkg`. This must be a url, but not necessarily an OData feed. 152 | Any old url location will work. Defaults to 153 | `'https://chocolatey.org/api/v2/package/chocolatey/'`. 154 | 155 | Default value: `'https://chocolatey.org/api/v2/package/chocolatey/'` 156 | 157 | ##### `enable_autouninstaller` 158 | 159 | Data type: `Boolean` 160 | 161 | [Deprecated] - Should auto 162 | uninstaller be turned on? Auto uninstaller is what allows Chocolatey to 163 | automatically manage the uninstall of software from Programs and Features 164 | without necessarily requiring a `chocolateyUninstall.ps1` file in the 165 | package. Defaults to `true`. Setting is ignored in Chocolatey v0.9.10+. 166 | 167 | Default value: `true` 168 | 169 | ##### `log_output` 170 | 171 | Data type: `Boolean` 172 | 173 | Log output from the installer. Defaults to 174 | `false`. 175 | 176 | Default value: `false` 177 | 178 | ##### `chocolatey_version` 179 | 180 | Data type: `String[1]` 181 | 182 | - **Informational** parameter to tell 183 | Chocolatey what version to expect and to pre-load features with, falls 184 | back to `$::chocolateyversion`. 185 | 186 | Default value: `$facts['chocolateyversion']` 187 | 188 | ##### `install_proxy` 189 | 190 | Data type: `Optional[String[1]]` 191 | 192 | Proxy server to use to use for installation of chocolatey itself or 193 | `undef` to not use a proxy 194 | 195 | Default value: `undef` 196 | 197 | ## Resource types 198 | 199 | ### `chocolateyconfig` 200 | 201 | Allows managing config settings for Chocolatey. 202 | Configuration values provide settings for users 203 | to configure aspects of Chocolatey and the way it 204 | functions. Similar to features, except allow for user 205 | configured values. Requires 0.9.10+. Learn more about 206 | config at https://chocolatey.org/docs/commands-config 207 | 208 | #### Properties 209 | 210 | The following properties are available in the `chocolateyconfig` type. 211 | 212 | ##### `ensure` 213 | 214 | Valid values: `present`, `absent` 215 | 216 | Specifies state of resource 217 | 218 | Default value: `present` 219 | 220 | ##### `value` 221 | 222 | The value of the config setting. If the 223 | name includes 'password', then the value is 224 | not ensurable due to being encrypted in the 225 | configuration file. 226 | 227 | #### Parameters 228 | 229 | The following parameters are available in the `chocolateyconfig` type. 230 | 231 | * [`name`](#-chocolateyconfig--name) 232 | * [`provider`](#-chocolateyconfig--provider) 233 | 234 | ##### `name` 235 | 236 | namevar 237 | 238 | The name of the config setting. Used for uniqueness. 239 | Puppet is not able to easily manage any values that 240 | include Password in the key name in them as they 241 | will be encrypted in the configuration file. 242 | 243 | ##### `provider` 244 | 245 | The specific backend to use for this `chocolateyconfig` resource. You will seldom need to specify this --- Puppet will 246 | usually discover the appropriate provider for your platform. 247 | 248 | ### `chocolateyfeature` 249 | 250 | Allows managing features for Chocolatey. Features are 251 | configuration that act as feature flippers to turn on or 252 | off certain aspects of how Chocolatey works. 253 | Learn more about features at 254 | https://chocolatey.org/docs/commands-feature 255 | 256 | #### Properties 257 | 258 | The following properties are available in the `chocolateyfeature` type. 259 | 260 | ##### `ensure` 261 | 262 | Valid values: `enabled`, `disabled` 263 | 264 | Specifies state of resource 265 | 266 | #### Parameters 267 | 268 | The following parameters are available in the `chocolateyfeature` type. 269 | 270 | * [`name`](#-chocolateyfeature--name) 271 | * [`provider`](#-chocolateyfeature--provider) 272 | 273 | ##### `name` 274 | 275 | namevar 276 | 277 | The name of the feature. Used for uniqueness. 278 | 279 | ##### `provider` 280 | 281 | The specific backend to use for this `chocolateyfeature` resource. You will seldom need to specify this --- Puppet will 282 | usually discover the appropriate provider for your platform. 283 | 284 | ### `chocolateysource` 285 | 286 | Allows managing sources for Chocolatey. A source can be a 287 | folder, a CIFS share, a NuGet Http OData feed, or a full 288 | Package Gallery. Learn more about sources at 289 | https://chocolatey.org/docs/how-to-host-feed 290 | 291 | #### Properties 292 | 293 | The following properties are available in the `chocolateysource` type. 294 | 295 | ##### `admin_only` 296 | 297 | Valid values: `true`, `false` 298 | 299 | Option to specify whether this source should 300 | visible to Windows user accounts in the Administrators 301 | group only. 302 | 303 | Requires Chocolatey for Business (C4B) v1.12.2+ and at 304 | least Chocolatey v0.10.8 for the setting to be respected. 305 | Defaults to false. 306 | 307 | Default value: `false` 308 | 309 | ##### `allow_self_service` 310 | 311 | Valid values: `true`, `false` 312 | 313 | Option to specify whether this source should be 314 | allowed to be used with Chocolatey Self Service. 315 | 316 | Requires Chocolatey for Business (C4B) v1.10.0+ with the 317 | feature useBackgroundServiceWithSelfServiceSourcesOnly 318 | turned on in order to be respected. 319 | Also requires at least Chocolatey v0.10.4 for the setting 320 | to be enabled. 321 | Defaults to false. 322 | 323 | Default value: `false` 324 | 325 | ##### `bypass_proxy` 326 | 327 | Valid values: `true`, `false` 328 | 329 | Option to specify whether this source should 330 | explicitly bypass any explicitly or system 331 | configured proxies. 332 | Requires at least Chocolatey v0.10.4. 333 | Defaults to false. 334 | 335 | Default value: `false` 336 | 337 | ##### `ensure` 338 | 339 | Valid values: `present`, `disabled`, `absent` 340 | 341 | Specifies state of resource 342 | 343 | Default value: `present` 344 | 345 | ##### `location` 346 | 347 | The location of the source repository. Can be a url pointing to 348 | an OData feed (like chocolatey/chocolatey_server), a CIFS (UNC) share, 349 | or a local folder. Required when `ensure => present` (the default for 350 | `ensure`). 351 | 352 | ##### `priority` 353 | 354 | Optional priority for explicit feed order when 355 | searching for packages across multiple feeds. 356 | The lower the number the higher the priority. 357 | Sources with a 0 priority are considered no priority 358 | and are added after other sources with a priority 359 | number. 360 | Requires at least Chocolatey v0.9.9.9. 361 | Defaults to 0. 362 | 363 | Default value: `0` 364 | 365 | ##### `user` 366 | 367 | Optional user name for authenticated feeds. 368 | Requires at least Chocolatey v0.9.9.0. 369 | Defaults to `nil`. Specifying an empty value is the 370 | same as setting the value to nil or not specifying 371 | the property at all. 372 | 373 | Default value: `''` 374 | 375 | #### Parameters 376 | 377 | The following parameters are available in the `chocolateysource` type. 378 | 379 | * [`name`](#-chocolateysource--name) 380 | * [`password`](#-chocolateysource--password) 381 | * [`provider`](#-chocolateysource--provider) 382 | 383 | ##### `name` 384 | 385 | namevar 386 | 387 | The name of the source. Used for uniqueness. 388 | 389 | ##### `password` 390 | 391 | Optional user password for authenticated feeds. 392 | Not ensurable. Value is not able to be checked 393 | with current value. If you need to update the password, 394 | update another setting as well. 395 | Requires at least Chocolatey v0.9.9.0. 396 | Defaults to `nil`. Specifying an empty value is the 397 | same as setting the value to nil or not specifying 398 | the property at all. 399 | 400 | Default value: `''` 401 | 402 | ##### `provider` 403 | 404 | The specific backend to use for this `chocolateysource` resource. You will seldom need to specify this --- Puppet will 405 | usually discover the appropriate provider for your platform. 406 | 407 | ## Tasks 408 | 409 | ### `init` 410 | 411 | Manage a package 412 | 413 | **Supports noop?** false 414 | 415 | #### Parameters 416 | 417 | ##### `action` 418 | 419 | Data type: `Enum[install,upgrade,uninstall]` 420 | 421 | Action to perform 422 | 423 | ##### `package` 424 | 425 | Data type: `String[1]` 426 | 427 | Package to manipulate 428 | 429 | ##### `version` 430 | 431 | Data type: `Optional[String[1]]` 432 | 433 | Use a specific version 434 | 435 | ### `outdated` 436 | 437 | List outdated packages 438 | 439 | **Supports noop?** false 440 | 441 | ### `pin` 442 | 443 | Manage package pinning 444 | 445 | **Supports noop?** false 446 | 447 | #### Parameters 448 | 449 | ##### `action` 450 | 451 | Data type: `Enum[list,add,remove]` 452 | 453 | Action to perform 454 | 455 | ##### `package` 456 | 457 | Data type: `Optional[String[1]]` 458 | 459 | Package to manipulate 460 | 461 | ##### `version` 462 | 463 | Data type: `Optional[String[1]]` 464 | 465 | Use a specific version 466 | 467 | ### `status` 468 | 469 | List currently installed packages 470 | 471 | **Supports noop?** false 472 | 473 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler' 4 | require 'puppet_litmus/rake_tasks' if Gem.loaded_specs.key? 'puppet_litmus' 5 | require 'puppetlabs_spec_helper/rake_tasks' 6 | require 'puppet-syntax/tasks/puppet-syntax' 7 | require 'puppet-strings/tasks' if Gem.loaded_specs.key? 'puppet-strings' 8 | 9 | PuppetLint.configuration.send('disable_relative') 10 | -------------------------------------------------------------------------------- /data/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | chocolatey::choco_install_location: C:\ProgramData\chocolatey 3 | -------------------------------------------------------------------------------- /examples/init.pp: -------------------------------------------------------------------------------- 1 | # The baseline for module testing used by Puppet Labs is that each manifest 2 | # should have a corresponding test manifest that declares that class or defined 3 | # type. 4 | # 5 | # Tests are then run by using puppet apply --noop (to check for compilation errors 6 | # and view a log of events) or by fully applying the test in a virtual environment 7 | # (to compare the resulting system state to the desired state). 8 | # 9 | # Learn more about module testing here: http://docs.puppetlabs.com/guides/tests_smoke.html 10 | # 11 | # With symlinks on Windows, please run the following command an administrative command prompt (substituting the proper directories): 12 | 13 | package { $pkg: 14 | ensure => 'latest', 15 | provider => 'chocolatey', 16 | } 17 | 18 | # mklink /D C:\ProgramData\PuppetLabs\puppet\etc\modules\chocolatey C:\code\puppetlabs\puppetlabs-chocolatey 19 | # mklink /D C:\ProgramData\PuppetLabs\code\environments\production\modules\chocolatey C:\code\puppetlabs\puppetlabs-chocolatey 20 | 21 | chocolateysource { 'local': 22 | location => 'c:\packages', 23 | } 24 | -------------------------------------------------------------------------------- /hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 5 3 | 4 | defaults: # Used for any hierarchy level that omits these keys. 5 | datadir: data # This path is relative to hiera.yaml's directory. 6 | data_hash: yaml_data # Use the built-in YAML backend. 7 | 8 | hierarchy: 9 | - name: "osfamily/major release" 10 | paths: 11 | # Used to distinguish between Debian and Ubuntu 12 | - "os/%{facts.os.name}/%{facts.os.release.major}.yaml" 13 | - "os/%{facts.os.family}/%{facts.os.release.major}.yaml" 14 | # Used for Solaris 15 | - "os/%{facts.os.family}/%{facts.kernelrelease}.yaml" 16 | - name: "osfamily" 17 | paths: 18 | - "os/%{facts.os.name}.yaml" 19 | - "os/%{facts.os.family}.yaml" 20 | - name: 'common' 21 | path: 'common.yaml' 22 | -------------------------------------------------------------------------------- /lib/facter/choco_install_path.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | require Pathname.new(__FILE__).dirname + '../' + 'puppet_x/chocolatey/chocolatey_install' 5 | 6 | Facter.add('choco_install_path') do 7 | confine osfamily: :windows 8 | setcode do 9 | PuppetX::Chocolatey::ChocolateyInstall.install_path || 'C:\ProgramData\chocolatey' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/facter/choco_temp_dir.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | require Pathname.new(__FILE__).dirname + '../' + 'puppet_x/chocolatey/chocolatey_install' 5 | 6 | Facter.add('choco_temp_dir') do 7 | confine osfamily: :windows 8 | setcode do 9 | PuppetX::Chocolatey::ChocolateyInstall.temp_dir || ENV.fetch('TEMP', nil) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/facter/chocolateyversion.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | require Pathname.new(__FILE__).dirname + '../' + 'puppet_x/chocolatey/chocolatey_version' 5 | 6 | Facter.add('chocolateyversion') do 7 | confine osfamily: :windows 8 | setcode do 9 | choco_ver = PuppetX::Chocolatey::ChocolateyVersion.version || '0' 10 | choco_ver.to_s 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/puppet/provider/chocolateyconfig/windows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/type' 4 | require 'pathname' 5 | require 'rexml/document' 6 | 7 | Puppet::Type.type(:chocolateyconfig).provide(:windows) do 8 | @doc = 'Windows based provider for chocolateyconfig type.' 9 | 10 | confine operatingsystem: :windows 11 | defaultfor operatingsystem: :windows 12 | 13 | require Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/chocolatey/chocolatey_common' 14 | include PuppetX::Chocolatey::ChocolateyCommon 15 | 16 | CONFIG_MINIMUM_SUPPORTED_CHOCO_VERSION = '0.9.10.0' 17 | 18 | commands chocolatey: PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command 19 | 20 | def initialize(value = {}) 21 | super(value) 22 | @property_flush = {} 23 | end 24 | 25 | def properties 26 | if @property_hash.empty? 27 | @property_hash = query || { ensure: :absent } 28 | @property_hash[:ensure] = :absent if @property_hash.empty? 29 | end 30 | @property_hash.dup 31 | end 32 | 33 | def query 34 | self.class.configs.each do |config| 35 | return config.properties if @resource[:name][%r{\A\S*}].casecmp(config.name.downcase).zero? 36 | end 37 | 38 | {} 39 | end 40 | 41 | def self.read_configs 42 | PuppetX::Chocolatey::ChocolateyCommon.set_env_chocolateyinstall 43 | 44 | choco_config = PuppetX::Chocolatey::ChocolateyCommon.choco_config_file 45 | raise Puppet::ResourceError, 'Config file not found for Chocolatey. Please make sure you have Chocolatey installed.' if choco_config.nil? 46 | raise Puppet::ResourceError, "An install was detected, but was unable to locate config file at #{choco_config}." unless PuppetX::Chocolatey::ChocolateyCommon.file_exists?(choco_config) 47 | 48 | Puppet.debug("Gathering sources from '#{choco_config}'.") 49 | config = REXML::Document.new File.open(choco_config) 50 | 51 | config.elements.to_a('//add') 52 | end 53 | 54 | def self.get_config(element) 55 | config = {} 56 | return config if element.nil? 57 | 58 | config[:name] = element.attributes['key'] if element.attributes['key'] 59 | config[:value] = element.attributes['value'] if element.attributes['value'] 60 | config[:description] = element.attributes['description'] if element.attributes['description'] 61 | # If the value is empty it is the default value and so is not set by Puppet. 62 | # If a config item is ensured as absent it sets the value to an empty string. 63 | config[:ensure] = element.attributes['value'].to_s.empty? ? :absent : :present 64 | Puppet.debug("Loaded config '#{config.inspect}'.") 65 | 66 | config 67 | end 68 | 69 | def self.configs 70 | @configs ||= read_configs.map do |item| 71 | config = get_config(item) 72 | new(config) 73 | end 74 | end 75 | 76 | def self.refresh_configs 77 | @configs = nil 78 | configs 79 | end 80 | 81 | def self.instances 82 | configs 83 | end 84 | 85 | def self.prefetch(resources) 86 | instances.each do |provider| 87 | if (resource = resources[provider.name]) 88 | resource.provider = provider 89 | end 90 | end 91 | end 92 | 93 | def create 94 | @property_flush[:ensure] = :present 95 | end 96 | 97 | def exists? 98 | @property_hash[:ensure] == :present 99 | end 100 | 101 | def destroy 102 | @property_flush[:ensure] = :absent 103 | end 104 | 105 | def validate 106 | # We want to ensure that specifying a config item as :present fails if no :value for the config 107 | # is specified. However, during puppet resource runs the resource has an :ensure of present. 108 | # We are able to overcome this by checking if the hash is empty: 109 | # The hash *is* empty when the validate block is called during a puppet apply run. 110 | # The hash is *not* empty when the validate block is called during a puppet resource run. 111 | # If the hash is empty, fail only if :ensure is true and :value is not specified or is an empty string. 112 | raise ArgumentError, 'Unless ensure => absent, value is required.' if @property_hash.empty? && resource[:ensure] == :present && resource[:value].to_s.empty? 113 | 114 | choco_version = Gem::Version.new(PuppetX::Chocolatey::ChocolateyCommon.choco_version) 115 | validate_check = PuppetX::Chocolatey::ChocolateyCommon.file_exists?(PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command) && 116 | choco_version < Gem::Version.new(CONFIG_MINIMUM_SUPPORTED_CHOCO_VERSION) 117 | if validate_check # rubocop:disable Style/GuardClause 118 | raise Puppet::ResourceError, "Chocolatey version must be '#{CONFIG_MINIMUM_SUPPORTED_CHOCO_VERSION}' to manage configuration values. Detected '#{choco_version}' as your version. " \ 119 | 'Please upgrade Chocolatey.' 120 | end 121 | end 122 | 123 | mk_resource_methods 124 | 125 | def flush 126 | # choco_version = Gem::Version.new(PuppetX::Chocolatey::ChocolateyCommon.choco_version) 127 | 128 | args = [] 129 | args << 'config' 130 | 131 | # look at the hash, then flush if present. 132 | # If all else fails, looks at resource[:ensure] 133 | property_ensure = @property_hash[:ensure] 134 | property_ensure = @property_flush[:ensure] if @property_flush[:ensure] 135 | property_ensure = resource[:ensure] if property_ensure.nil? 136 | 137 | command = 'set' 138 | command = 'unset' if property_ensure == :absent 139 | 140 | args << command 141 | args << '--name' << resource[:name] 142 | 143 | args << '--value' << resource[:value] if property_ensure != :absent 144 | 145 | begin 146 | Puppet::Util::Execution.execute([command(:chocolatey), *args], { sensitive: true }) 147 | rescue Puppet::ExecutionFailure => e 148 | raise Puppet::Error, "An error occurred running choco. Unable to set Chocolateyconfig[#{name}]: #{e}" 149 | end 150 | 151 | @property_hash.clear 152 | @property_flush.clear 153 | 154 | self.class.refresh_configs 155 | @property_hash = query 156 | end 157 | end 158 | -------------------------------------------------------------------------------- /lib/puppet/provider/chocolateyfeature/windows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/type' 4 | require 'pathname' 5 | require 'rexml/document' 6 | 7 | Puppet::Type.type(:chocolateyfeature).provide(:windows) do 8 | @doc = 'Windows based provider for chocolateyfeature type.' 9 | 10 | confine operatingsystem: :windows 11 | defaultfor operatingsystem: :windows 12 | 13 | require Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/chocolatey/chocolatey_common' 14 | include PuppetX::Chocolatey::ChocolateyCommon 15 | 16 | FEATURE_MINIMUM_SUPPORTED_CHOCO_VERSION = '0.9.9.0' 17 | 18 | commands chocolatey: PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command 19 | 20 | def initialize(value = {}) 21 | super(value) 22 | @property_flush = {} 23 | end 24 | 25 | def properties 26 | @property_hash = query if @property_hash.empty? 27 | @property_hash.dup 28 | end 29 | 30 | def query 31 | self.class.choco_features.each do |feature| 32 | return feature.properties if @resource[:name][%r{\A\S*}].casecmp(feature.name.downcase).zero? 33 | end 34 | 35 | {} 36 | end 37 | 38 | def self.read_choco_features 39 | PuppetX::Chocolatey::ChocolateyCommon.set_env_chocolateyinstall 40 | 41 | choco_config = PuppetX::Chocolatey::ChocolateyCommon.choco_config_file 42 | raise Puppet::ResourceError, 'Config file not found for Chocolatey. Please make sure you have Chocolatey installed.' if choco_config.nil? 43 | raise Puppet::ResourceError, "An install was detected, but was unable to locate config file at #{choco_config}." unless PuppetX::Chocolatey::ChocolateyCommon.file_exists?(choco_config) 44 | 45 | Puppet.debug("Gathering features from '#{choco_config}'.") 46 | config = REXML::Document.new File.open(choco_config) 47 | 48 | config.elements.to_a('//feature') 49 | end 50 | 51 | def self.get_choco_feature(element) 52 | feature = {} 53 | return feature if element.nil? 54 | 55 | feature[:name] = element.attributes['name'].downcase if element.attributes['name'] 56 | feature[:description] = element.attributes['description'].downcase if element.attributes['description'] 57 | 58 | enabled = false 59 | enabled = element.attributes['enabled'].casecmp('true').zero? if element.attributes['enabled'] 60 | 61 | feature[:ensure] = :disabled 62 | feature[:ensure] = :enabled if enabled 63 | 64 | Puppet.debug("Loaded feature '#{feature.inspect}'.") 65 | 66 | feature 67 | end 68 | 69 | def self.choco_features 70 | read_choco_features.map do |item| 71 | feature = get_choco_feature(item) 72 | new(feature) 73 | end 74 | end 75 | 76 | def self.instances 77 | choco_features 78 | end 79 | 80 | def self.prefetch(resources) 81 | instances.each do |provider| 82 | if (resource = resources[provider.name]) 83 | resource.provider = provider 84 | end 85 | end 86 | end 87 | 88 | def enable 89 | @property_flush[:ensure] = :enabled 90 | end 91 | 92 | def exists? 93 | @property_hash[:ensure] == :enabled 94 | end 95 | 96 | def disable 97 | @property_flush[:ensure] = :disabled 98 | end 99 | 100 | def validate 101 | choco_version = Gem::Version.new(PuppetX::Chocolatey::ChocolateyCommon.choco_version) 102 | validate_check = PuppetX::Chocolatey::ChocolateyCommon.file_exists?(PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command) && 103 | choco_version < Gem::Version.new(FEATURE_MINIMUM_SUPPORTED_CHOCO_VERSION) 104 | 105 | if validate_check # rubocop:disable Style/GuardClause 106 | raise Puppet::ResourceError, "Chocolatey version must be '#{FEATURE_MINIMUM_SUPPORTED_CHOCO_VERSION}' to manage configuration values with Puppet. " \ 107 | "Detected '#{choco_version}' as your version. Please upgrade Chocolatey to use this resource." 108 | end 109 | end 110 | 111 | mk_resource_methods 112 | 113 | def flush 114 | args = [] 115 | args << 'feature' 116 | 117 | command = 'enable' 118 | command = 'disable' if @property_flush[:ensure] == :disabled 119 | 120 | args << command 121 | args << '--name' << resource[:name] 122 | 123 | Puppet::Util::Execution.execute([command(:chocolatey), *args]) 124 | 125 | @property_hash.clear 126 | @property_flush.clear 127 | 128 | self.class.choco_features 129 | @property_hash = query 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /lib/puppet/provider/chocolateysource/windows.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/type' 4 | require 'pathname' 5 | require 'rexml/document' 6 | 7 | Puppet::Type.type(:chocolateysource).provide(:windows) do 8 | @doc = 'Windows based provider for chocolateysource type.' 9 | 10 | confine operatingsystem: :windows 11 | defaultfor operatingsystem: :windows 12 | 13 | require Pathname.new(__FILE__).dirname + '../../../' + 'puppet_x/chocolatey/chocolatey_common' 14 | include PuppetX::Chocolatey::ChocolateyCommon 15 | 16 | MINIMUM_SUPPORTED_CHOCO_VERSION = '0.9.9.0' 17 | MINIMUM_SUPPORTED_CHOCO_VERSION_PRIORITY = '0.9.9.9' 18 | MINIMUM_SUPPORTED_CHOCO_VERSION_BYPASS_PROXY = '0.10.4' 19 | MINIMUM_SUPPORTED_CHOCO_VERSION_ALLOW_SELF_SERVICE = '0.10.4' 20 | MINIMUM_SUPPORTED_CHOCO_VERSION_ADMIN_ONLY = '0.10.8' 21 | 22 | commands chocolatey: PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command 23 | 24 | def initialize(value = {}) 25 | super(value) 26 | @property_flush = {} 27 | end 28 | 29 | def properties 30 | if @property_hash.empty? 31 | @property_hash = query || { ensure: :absent } 32 | @property_hash[:ensure] = :absent if @property_hash.empty? 33 | end 34 | @property_hash.dup 35 | end 36 | 37 | def query 38 | self.class.sources.each do |source| 39 | return source.properties if @resource[:name][%r{\A\S*}].casecmp(source.name.downcase).zero? 40 | end 41 | 42 | {} 43 | end 44 | 45 | def self.read_sources 46 | PuppetX::Chocolatey::ChocolateyCommon.set_env_chocolateyinstall 47 | 48 | choco_config = PuppetX::Chocolatey::ChocolateyCommon.choco_config_file 49 | raise Puppet::ResourceError, 'Config file not found for Chocolatey. Please make sure you have Chocolatey installed.' if choco_config.nil? 50 | raise Puppet::ResourceError, "An install was detected, but was unable to locate config file at #{choco_config}." unless PuppetX::Chocolatey::ChocolateyCommon.file_exists?(choco_config) 51 | 52 | Puppet.debug("Gathering sources from '#{choco_config}'.") 53 | config = REXML::Document.new File.open(choco_config) 54 | 55 | config.elements.to_a('//source') 56 | end 57 | 58 | def self.get_source(element) 59 | source = {} 60 | return source if element.nil? 61 | 62 | source[:name] = element.attributes['id'].downcase if element.attributes['id'] 63 | source[:location] = element.attributes['value'].downcase if element.attributes['value'] 64 | 65 | disabled = false 66 | disabled = element.attributes['disabled'].casecmp('true').zero? if element.attributes['disabled'] 67 | source[:ensure] = :present 68 | source[:ensure] = :disabled if disabled 69 | 70 | source[:priority] = '0' 71 | source[:priority] = element.attributes['priority'].downcase if element.attributes['priority'] 72 | 73 | source[:bypass_proxy] = false 74 | source[:bypass_proxy] = element.attributes['bypassProxy'].downcase if element.attributes['bypassProxy'] 75 | 76 | source[:admin_only] = false 77 | source[:admin_only] = element.attributes['adminOnly'].downcase if element.attributes['adminOnly'] 78 | 79 | source[:allow_self_service] = false 80 | source[:allow_self_service] = element.attributes['selfService'].downcase if element.attributes['selfService'] 81 | 82 | source[:user] = '' 83 | source[:user] = element.attributes['user'].downcase if element.attributes['user'] 84 | 85 | Puppet.debug("Loaded source '#{source.inspect}'.") 86 | 87 | source 88 | end 89 | 90 | def self.sources 91 | @sources ||= read_sources.map do |item| 92 | source = get_source(item) 93 | new(source) 94 | end 95 | end 96 | 97 | def self.refresh_sources 98 | @sources = nil 99 | sources 100 | end 101 | 102 | def self.instances 103 | sources 104 | end 105 | 106 | def self.prefetch(resources) 107 | instances.each do |provider| 108 | if (resource = resources[provider.name]) 109 | resource.provider = provider 110 | end 111 | end 112 | end 113 | 114 | def create 115 | @property_flush[:ensure] = :present 116 | end 117 | 118 | def exists? 119 | @property_hash[:ensure] == :present 120 | end 121 | 122 | def disable 123 | @property_flush[:ensure] = :disabled 124 | end 125 | 126 | def destroy 127 | @property_flush[:ensure] = :absent 128 | end 129 | 130 | def validate 131 | choco_version = Gem::Version.new(PuppetX::Chocolatey::ChocolateyCommon.choco_version) 132 | if PuppetX::Chocolatey::ChocolateyCommon.file_exists?(PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command) && choco_version < Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION) 133 | raise Puppet::ResourceError, "Chocolatey version must be '#{MINIMUM_SUPPORTED_CHOCO_VERSION}' to manage configuration values with Puppet. " \ 134 | "Detected '#{choco_version}' as your version. Please upgrade Chocolatey to use this resource." 135 | end 136 | 137 | if choco_version < Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_PRIORITY) && resource[:priority] && resource[:priority] != '0' 138 | Puppet.warning("Chocolatey is unable to manage priority for sources when version is less than #{MINIMUM_SUPPORTED_CHOCO_VERSION_PRIORITY}. The value you set will be ignored.") 139 | end 140 | 141 | if choco_version < Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_BYPASS_PROXY) && resource[:bypass_proxy] && resource[:bypass_proxy] != :false 142 | Puppet.warning("Chocolatey is unable to specify bypassing system proxy for sources when version is less than #{MINIMUM_SUPPORTED_CHOCO_VERSION_BYPASS_PROXY}. The value you set will be ignored.") 143 | end 144 | 145 | if choco_version < Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_ALLOW_SELF_SERVICE) && resource[:allow_self_service] && resource[:allow_self_service] != :false 146 | Puppet.warning("Chocolatey is unable to specify self-service for sources when version is less than #{MINIMUM_SUPPORTED_CHOCO_VERSION_ALLOW_SELF_SERVICE}. The value you set will be ignored.") 147 | end 148 | 149 | if choco_version < Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_ADMIN_ONLY) && resource[:admin_only] && resource[:admin_only] != :false 150 | Puppet.warning("Chocolatey is unable to specify administrator only visibility for sources when version is less than #{MINIMUM_SUPPORTED_CHOCO_VERSION_ADMIN_ONLY}. " \ 151 | 'The value you set will be ignored.') 152 | end 153 | 154 | # location is always filled in with puppet resource, but 155 | # resource[:location] is always empty (because it has a different 156 | # code path where validation occurs before all properties/params 157 | # have been set), resulting in errors 158 | # location is always :absent when a manifest runs this with a missing 159 | # `location => value` 160 | location_check = location 161 | # location could be :absent, which mk_resource_method will set it to 162 | # resource[:location] is nil when running puppet resource 163 | # if you remove `location => value` 164 | location_check = resource[:location] if location_check == :absent 165 | raise ArgumentError, 'A non-empty location must be specified when ensure => present.' if resource[:ensure] == :present && (location_check.nil? || location_check.strip == '') 166 | 167 | if resource[:password] && resource[:password] != '' # rubocop:disable Style/GuardClause 168 | Puppet.debug('The password is not ensurable, so Puppet is unable to change the value using chocolateysource resource. ' \ 169 | "As a workaround, a password change can be in the form of an exec. Reference Chocolateysource[#{resource[:name]}]") 170 | end 171 | end 172 | 173 | mk_resource_methods 174 | 175 | def flush 176 | args = [] 177 | opts = { sensitive: true } 178 | 179 | args << 'source' 180 | 181 | # look at the hash, then flush if present. 182 | # If all else fails, looks at resource[:ensure] 183 | property_ensure = @property_hash[:ensure] 184 | property_ensure = @property_flush[:ensure] if @property_flush[:ensure] 185 | property_ensure = resource[:ensure] if property_ensure.nil? 186 | 187 | command = 'add' 188 | command = 'remove' if property_ensure == :absent 189 | command = 'disable' if property_ensure == :disabled 190 | 191 | args << command 192 | args << '--name' << resource[:name] 193 | 194 | if command == 'add' 195 | args << '--source' << resource[:location] 196 | 197 | if resource[:user] && resource[:user] != '' 198 | args << '--user' << resource[:user] 199 | args << '--password' << resource[:password] 200 | opts[:failonfail] = true 201 | opts[:combine] = true 202 | end 203 | 204 | choco_gem_version = Gem::Version.new(PuppetX::Chocolatey::ChocolateyCommon.choco_version) 205 | args << '--bypass-proxy' if choco_gem_version >= Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_BYPASS_PROXY) && (resource[:bypass_proxy].to_s == 'true') 206 | 207 | args << '--allow-self-service' if choco_gem_version >= Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_ALLOW_SELF_SERVICE) && (resource[:allow_self_service] == :true) 208 | 209 | args << '--admin-only' if choco_gem_version >= Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_ADMIN_ONLY) && (resource[:admin_only].to_s == 'true') 210 | 211 | args << '--priority' << resource[:priority] if choco_gem_version >= Gem::Version.new(MINIMUM_SUPPORTED_CHOCO_VERSION_PRIORITY) 212 | end 213 | 214 | begin 215 | Puppet::Util::Execution.execute([command(:chocolatey), *args], opts) 216 | rescue Puppet::ExecutionFailure 217 | raise Puppet::Error, "An error occurred running choco. Unable to set Chocolatey source configuration for #{inspect}" 218 | end 219 | 220 | if property_ensure == :present 221 | begin 222 | Puppet::Util::Execution.execute([command(:chocolatey), 'source', 'enable', '--name', resource[:name]], { sensitive: true }) 223 | rescue Puppet::ExecutionFailure 224 | raise Puppet::Error, "An error occurred running choco. Unable to set Chocolatey source configuration for #{inspect}" 225 | end 226 | end 227 | 228 | @property_hash.clear 229 | @property_flush.clear 230 | 231 | self.class.refresh_sources 232 | @property_hash = query 233 | end 234 | end 235 | -------------------------------------------------------------------------------- /lib/puppet/type/chocolateyconfig.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/type' 4 | require 'pathname' 5 | 6 | Puppet::Type.newtype(:chocolateyconfig) do 7 | @doc = <<-DOC 8 | Allows managing config settings for Chocolatey. 9 | Configuration values provide settings for users 10 | to configure aspects of Chocolatey and the way it 11 | functions. Similar to features, except allow for user 12 | configured values. Requires 0.9.10+. Learn more about 13 | config at https://chocolatey.org/docs/commands-config 14 | DOC 15 | 16 | ensurable do 17 | desc 'Specifies state of resource' 18 | 19 | newvalue(:present) { provider.create } 20 | newvalue(:absent) { provider.destroy } 21 | defaultto :present 22 | 23 | def retrieve 24 | provider.properties[:ensure] 25 | end 26 | end 27 | 28 | newparam(:name) do 29 | desc "The name of the config setting. Used for uniqueness. 30 | Puppet is not able to easily manage any values that 31 | include Password in the key name in them as they 32 | will be encrypted in the configuration file." 33 | 34 | validate do |value| 35 | raise ArgumentError, 'A non-empty name must be specified.' if value.nil? || value.empty? 36 | end 37 | 38 | isnamevar 39 | 40 | munge do |value| 41 | value.downcase 42 | end 43 | 44 | def insync?(is) 45 | is.casecmp(should.downcase).zero? 46 | end 47 | end 48 | 49 | newproperty(:value) do 50 | desc "The value of the config setting. If the 51 | name includes 'password', then the value is 52 | not ensurable due to being encrypted in the 53 | configuration file." 54 | 55 | validate do |value| 56 | raise ArgumentError, 'A non-empty value must be specified. To unset value, use ensure => absent' if value.nil? || value.empty? 57 | end 58 | 59 | def insync?(is) 60 | if resource[:name].match?(%r{password}i) 61 | # If name contains password, it is 62 | # always in sync if there is a value 63 | (is.nil? || is.empty?) == (should.nil? || should.empty?) 64 | else 65 | is.casecmp(should.downcase).zero? 66 | end 67 | end 68 | end 69 | 70 | validate do 71 | provider.validate if provider.respond_to?(:validate) 72 | end 73 | 74 | autorequire(:exec) do 75 | ['install_chocolatey_official'] 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/puppet/type/chocolateyfeature.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/type' 4 | require 'pathname' 5 | 6 | Puppet::Type.newtype(:chocolateyfeature) do 7 | @doc = <<-EOT 8 | Allows managing features for Chocolatey. Features are 9 | configuration that act as feature flippers to turn on or 10 | off certain aspects of how Chocolatey works. 11 | Learn more about features at 12 | https://chocolatey.org/docs/commands-feature 13 | 14 | EOT 15 | 16 | newparam(:name) do 17 | desc 'The name of the feature. Used for uniqueness.' 18 | 19 | validate do |value| 20 | raise ArgumentError, 'A non-empty name must be specified.' if value.nil? || value.empty? 21 | end 22 | 23 | isnamevar 24 | 25 | munge do |value| 26 | value.downcase 27 | end 28 | 29 | def insync?(is) 30 | is.casecmp(should.downcase).zero? 31 | end 32 | end 33 | 34 | ensurable do 35 | desc 'Specifies state of resource' 36 | 37 | newvalue(:enabled) { provider.enable } 38 | newvalue(:disabled) { provider.disable } 39 | 40 | def retrieve 41 | provider.properties[:ensure] 42 | end 43 | end 44 | 45 | validate do 46 | raise ArgumentError, 'Invalid value for ensure. Valid values are enabled or disabled.' if self[:ensure].nil? && provider.properties[:ensure].nil? 47 | 48 | provider.validate if provider.respond_to?(:validate) 49 | end 50 | 51 | autorequire(:exec) do 52 | ['install_chocolatey_official'] 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/puppet/type/chocolateysource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/type' 4 | require 'pathname' 5 | 6 | Puppet::Type.newtype(:chocolateysource) do 7 | @doc = <<-EOT 8 | Allows managing sources for Chocolatey. A source can be a 9 | folder, a CIFS share, a NuGet Http OData feed, or a full 10 | Package Gallery. Learn more about sources at 11 | https://chocolatey.org/docs/how-to-host-feed 12 | 13 | EOT 14 | 15 | ensurable do 16 | desc 'Specifies state of resource' 17 | 18 | newvalue(:present) { provider.create } 19 | newvalue(:disabled) { provider.disable } 20 | newvalue(:absent) { provider.destroy } 21 | defaultto :present 22 | 23 | def retrieve 24 | provider.properties[:ensure] 25 | end 26 | end 27 | 28 | newparam(:name) do 29 | desc 'The name of the source. Used for uniqueness.' 30 | 31 | validate do |value| 32 | raise ArgumentError, 'A non-empty name must be specified.' if value.nil? || value.empty? 33 | end 34 | 35 | isnamevar 36 | 37 | munge do |value| 38 | value.downcase 39 | end 40 | 41 | def insync?(is) 42 | is.casecmp(should.downcase).zero? 43 | end 44 | end 45 | 46 | newproperty(:location) do 47 | desc "The location of the source repository. Can be a url pointing to 48 | an OData feed (like chocolatey/chocolatey_server), a CIFS (UNC) share, 49 | or a local folder. Required when `ensure => present` (the default for 50 | `ensure`)." 51 | 52 | validate do |value| 53 | raise ArgumentError, 'A non-empty location must be specified.' if value.nil? || value.empty? 54 | end 55 | 56 | def insync?(is) 57 | is.casecmp(should.downcase).zero? 58 | end 59 | end 60 | 61 | newproperty(:user) do 62 | desc "Optional user name for authenticated feeds. 63 | Requires at least Chocolatey v0.9.9.0. 64 | Defaults to `nil`. Specifying an empty value is the 65 | same as setting the value to nil or not specifying 66 | the property at all." 67 | 68 | def insync?(is) 69 | is.casecmp(should.downcase).zero? 70 | end 71 | 72 | defaultto '' 73 | end 74 | 75 | newparam(:password) do 76 | desc "Optional user password for authenticated feeds. 77 | Not ensurable. Value is not able to be checked 78 | with current value. If you need to update the password, 79 | update another setting as well. 80 | Requires at least Chocolatey v0.9.9.0. 81 | Defaults to `nil`. Specifying an empty value is the 82 | same as setting the value to nil or not specifying 83 | the property at all." 84 | 85 | defaultto '' 86 | end 87 | 88 | newproperty(:priority) do 89 | desc "Optional priority for explicit feed order when 90 | searching for packages across multiple feeds. 91 | The lower the number the higher the priority. 92 | Sources with a 0 priority are considered no priority 93 | and are added after other sources with a priority 94 | number. 95 | Requires at least Chocolatey v0.9.9.9. 96 | Defaults to 0." 97 | 98 | validate do |value| 99 | raise ArgumentError, 'A non-empty priority must be specified.' if value.nil? 100 | raise ArgumentError, 'An integer is necessary for priority. Specify 0 or remove for no priority.' unless resource.numeric?(value) 101 | end 102 | 103 | # There is a slight bug in the way that Puppet::Util::Execution.execute 104 | # handles parameters if you specify that the command to execute is sensitive. 105 | # When sensitive is not specified all parameters are cast to strings. If 106 | # Sensitive is specified then paramters retain their types. In this case it 107 | # means that if :priority is allowed to remain an integer it will cause a 108 | # failure later in the Puppet::Util::Execution.execute_windows method that 109 | # assumes all parameters in the `command` array it recieives will be strings. 110 | # This munge has no effect on the commandline that eventually gets generated. 111 | munge do |value| 112 | resource.munge_to_string(value) 113 | end 114 | 115 | defaultto('0') 116 | end 117 | 118 | newproperty(:bypass_proxy, boolean: true) do 119 | desc "Option to specify whether this source should 120 | explicitly bypass any explicitly or system 121 | configured proxies. 122 | Requires at least Chocolatey v0.10.4. 123 | Defaults to false." 124 | 125 | newvalues(:true, :false) 126 | defaultto(:false) 127 | 128 | munge do |value| 129 | resource.munge_boolean(value) 130 | end 131 | end 132 | 133 | newproperty(:admin_only, boolean: true) do 134 | desc "Option to specify whether this source should 135 | visible to Windows user accounts in the Administrators 136 | group only. 137 | 138 | Requires Chocolatey for Business (C4B) v1.12.2+ and at 139 | least Chocolatey v0.10.8 for the setting to be respected. 140 | Defaults to false." 141 | 142 | newvalues(:true, :false) 143 | defaultto(:false) 144 | 145 | munge do |value| 146 | resource.munge_boolean(value) 147 | end 148 | end 149 | 150 | newproperty(:allow_self_service, boolean: true) do 151 | desc "Option to specify whether this source should be 152 | allowed to be used with Chocolatey Self Service. 153 | 154 | Requires Chocolatey for Business (C4B) v1.10.0+ with the 155 | feature useBackgroundServiceWithSelfServiceSourcesOnly 156 | turned on in order to be respected. 157 | Also requires at least Chocolatey v0.10.4 for the setting 158 | to be enabled. 159 | Defaults to false." 160 | 161 | newvalues(:true, :false) 162 | defaultto(:false) 163 | 164 | munge do |value| 165 | resource.munge_boolean(value) 166 | end 167 | end 168 | 169 | validate do 170 | if (!self[:user].nil? && self[:user].strip != '' && (self[:password].nil? || self[:password] == '')) || 171 | ((self[:user].nil? || self[:user].strip == '') && !self[:password].nil? && self[:password] != '') 172 | raise ArgumentError, 'If specifying user/password, you must specify both values.' 173 | end 174 | 175 | provider.validate if provider.respond_to?(:validate) 176 | end 177 | 178 | autorequire(:exec) do 179 | ['install_chocolatey_official'] 180 | end 181 | 182 | def munge_boolean(value) 183 | case value 184 | when true, 'true', :true 185 | :true 186 | when false, 'false', :false 187 | :false 188 | else 189 | raise('munge_boolean only takes booleans') 190 | end 191 | end 192 | 193 | def munge_to_string(value) 194 | case value.class 195 | when String 196 | value 197 | else 198 | value.to_s 199 | end 200 | end 201 | 202 | def numeric?(value) 203 | # this is what stdlib does. Not sure if we want to emulate or not. 204 | # numeric = %r{^-?(?:(?:[1-9]\d*)|0)$} 205 | # if value.is_a? Integer or (value.is_a? String and value.match numeric) 206 | 207 | !Float(value).nil? 208 | rescue StandardError 209 | false 210 | end 211 | 212 | def set_sensitive_parameters(sensitive_parameters) 213 | parameter(:password)&.sensitive = true 214 | super(sensitive_parameters) 215 | end 216 | end 217 | -------------------------------------------------------------------------------- /lib/puppet_x/chocolatey/chocolatey_common.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | require Pathname.new(__FILE__).dirname + 'chocolatey_version' 5 | require Pathname.new(__FILE__).dirname + 'chocolatey_install' 6 | 7 | # Module used for general Chocolatey commands and constants 8 | module PuppetX::Chocolatey::ChocolateyCommon 9 | # First C# version of Chocolatey 10 | FIRST_COMPILED_CHOCO_VERSION = '0.9.9.0' unless defined? FIRST_COMPILED_CHOCO_VERSION 11 | # Specifes the minimum version that allows the `ignore-package-exit-codes` flag 12 | MINIMUM_SUPPORTED_CHOCO_VERSION_EXIT_CODES = '0.9.10.0' unless defined? MINIMUM_SUPPORTED_CHOCO_VERSION_EXIT_CODES 13 | # Specifies the minimum version that allows uninstalling with a source argument 14 | MINIMUM_SUPPORTED_CHOCO_UNINSTALL_SOURCE = '0.9.10.0' unless defined? MINIMUM_SUPPORTED_CHOCO_UNINSTALL_SOURCE 15 | # Specifies the minimum version that allows the '--no-progress' flag 16 | MINIMUM_SUPPORTED_CHOCO_VERSION_NO_PROGRESS = '0.10.4.0' unless defined? MINIMUM_SUPPORTED_CHOCO_VERSION_NO_PROGRESS 17 | 18 | def file_exists?(path) 19 | File.exist?(path) 20 | end 21 | module_function :file_exists? 22 | 23 | # Retrieves path of Chocolatey executable. 24 | # 25 | # @return [String] Path to Chocolatey executable 26 | def chocolatey_command 27 | if Puppet::Util::Platform.windows? 28 | # When attempting to find the choco command executable, the following 29 | # paths are checked: 30 | # - Start with the install_path. If choco is found with environment 31 | # variables through the registry or a check on the 32 | # ChocolateyInstall env var (first install of Choco may only have 33 | # this), then use that path. 34 | # - Next look to the most commonly used install location (ProgramData) 35 | # - Fall back to the older install location for older installations 36 | # - If all else fails, attempt to find Chocolatey in the default place 37 | # it installs 38 | choco_install_path = Facter.value('choco_install_path') || PuppetX::Chocolatey::ChocolateyInstall.install_path 39 | 40 | chocopath = (choco_install_path if choco_install_path && file_exists?("#{choco_install_path}\\choco.exe")) || 41 | ('C:\ProgramData\chocolatey' if file_exists?('C:\ProgramData\chocolatey\choco.exe')) || 42 | ('C:\Chocolatey' if file_exists?('C:\Chocolatey\choco.exe')) || 43 | "#{ENV.fetch('ALLUSERSPROFILE', nil)}\\chocolatey" 44 | 45 | chocopath += '\choco.exe' 46 | else 47 | chocopath = 'choco.exe' 48 | end 49 | 50 | chocopath 51 | end 52 | module_function :chocolatey_command 53 | 54 | # Sets the `ChocolateyInstall` environment variables to the current Chocolatey install path 55 | def set_env_chocolateyinstall 56 | ENV['ChocolateyInstall'] = Facter.value('choco_install_path') || PuppetX::Chocolatey::ChocolateyInstall.install_path 57 | end 58 | module_function :set_env_chocolateyinstall 59 | 60 | # Retrieves version of currently installed Chocolatey package. 61 | # 62 | # @return [String] Semver string of Chocolatey version 63 | def choco_version 64 | version_fact = Facter.value('chocolateyversion') 65 | @chocoversion ||= strip_beta_from_version(((version_fact == '0') ? nil : version_fact) || PuppetX::Chocolatey::ChocolateyVersion.version) 66 | @chocoversion 67 | end 68 | module_function :choco_version 69 | 70 | # Removes the beta section of the version string. 71 | # 72 | # @param [String] value Semver string 73 | # @return [String] Semver string with beta postfix removed 74 | def self.strip_beta_from_version(value) 75 | return nil if value.nil? 76 | 77 | value.split(%r{-})[0] 78 | end 79 | 80 | # Retrieves the path of the Chocolatey configuration file. 81 | # 82 | # @return [String] Path to config file 83 | def choco_config_file 84 | choco_install_path = Facter.value('choco_install_path') || PuppetX::Chocolatey::ChocolateyInstall.install_path 85 | choco_config = "#{choco_install_path}\\config\\chocolatey.config" 86 | 87 | # choco may be installed, but a config file doesn't exist until the 88 | # first run of choco - trigger that by checking the version 89 | _choco_run_ensure_config = choco_version 90 | 91 | return choco_config if file_exists?(choco_config) 92 | 93 | old_choco_config = "#{choco_install_path}\\chocolateyinstall\\chocolatey.config" 94 | 95 | return old_choco_config if file_exists?(old_choco_config) 96 | 97 | nil 98 | end 99 | module_function :choco_config_file 100 | 101 | # Clears global common constants. 102 | def clear_cached_values 103 | @chocoversion = nil 104 | @compiled_choco = nil 105 | end 106 | module_function :clear_cached_values 107 | end 108 | -------------------------------------------------------------------------------- /lib/puppet_x/chocolatey/chocolatey_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable Style/ClassAndModuleChildren 4 | 5 | # Puppet Extensions Module 6 | module PuppetX 7 | # Chocolatey module 8 | module Chocolatey 9 | # Class for installation of Chocolatey 10 | class ChocolateyInstall 11 | # Retrieves the path to the folder containing the Chocolatey instllation 12 | def self.install_path 13 | value = nil 14 | 15 | if Puppet::Util::Platform.windows? 16 | require 'win32/registry' 17 | 18 | begin 19 | hive = Win32::Registry::HKEY_LOCAL_MACHINE 20 | hive.open('SYSTEM\CurrentControlSet\Control\Session Manager\Environment', Win32::Registry::KEY_READ | 0x100) do |reg| 21 | value = reg['ChocolateyInstall'] 22 | end 23 | rescue Win32::Registry::Error 24 | value = nil 25 | end 26 | end 27 | 28 | # If machine level is not set, use process or user as the intended 29 | # location where Chocolatey would be installed. 30 | # Since it is technically possible that Chocolatey could exist on 31 | # non-Windows installations, we don't want to confine this 32 | # to just Windows. 33 | value = ENV.fetch('ChocolateyInstall', nil) if value.nil? 34 | 35 | value 36 | end 37 | 38 | # Retrieves the path to the temporary folder on the system 39 | # 40 | # @return [String] Path to temp folder 41 | def self.temp_dir 42 | return unless Puppet::Util::Platform.windows? 43 | 44 | require 'win32/registry' 45 | 46 | value = nil 47 | begin 48 | # looking at current user may likely fail because it's likely going to be LocalSystem 49 | hive = Win32::Registry::HKEY_CURRENT_USER 50 | hive.open('Environment', Win32::Registry::KEY_READ | 0x100) do |reg| 51 | value = reg['TEMP'] 52 | end 53 | rescue Win32::Registry::Error 54 | value = nil 55 | end 56 | 57 | if value.nil? 58 | begin 59 | hive = Win32::Registry::HKEY_LOCAL_MACHINE 60 | hive.open('SYSTEM\CurrentControlSet\Control\Session Manager\Environment', Win32::Registry::KEY_READ | 0x100) do |reg| 61 | value = reg['TEMP'] 62 | end 63 | rescue Win32::Registry::Error 64 | value = nil 65 | end 66 | end 67 | 68 | value 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/puppet_x/chocolatey/chocolatey_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pathname' 4 | require Pathname.new(__FILE__).dirname + 'chocolatey_install' 5 | 6 | # Module responsible for retrieving the version of the currently installed Chocolatey executable 7 | module PuppetX::Chocolatey::ChocolateyVersion 8 | # Used to remove additional message on older versions which do not print a single value 9 | OLD_CHOCO_MESSAGE = 'Please run chocolatey /? or chocolatey help - chocolatey v' unless defined? OLD_CHOCO_MESSAGE 10 | 11 | # Retrieves the version of the currently installed Chocolatey package 12 | # 13 | # @return [String] Semver string of Chocolatey package 14 | def self.version 15 | version = nil 16 | choco_path = "#{Facter.value('choco_install_path') || PuppetX::Chocolatey::ChocolateyInstall.install_path}\\choco.exe" 17 | if Puppet::Util::Platform.windows? && File.exist?(choco_path) 18 | begin 19 | # call `choco -v` 20 | # - new choco will output a single value e.g. `0.9.9` 21 | # - old choco is going to return the default output e.g. `Please run chocolatey /?` 22 | version = Puppet::Util::Execution.execute("#{choco_path} -v").gsub(OLD_CHOCO_MESSAGE, '') 23 | # - other messages, such as upgrade warnings or warnings about 24 | # installing the licensed extension once the license is installed 25 | # may show up when running this comamnd. Remove those as well 26 | version = version.split(%r{\r\n|\n|\r}).last.strip unless version.nil? 27 | rescue StandardError => e 28 | raise StandardError, "Unable to find Choco version: #{e}" 29 | end 30 | end 31 | 32 | version 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /locales/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is the project-specific configuration file for setting up 3 | # fast_gettext for your project. 4 | gettext: 5 | # This is used for the name of the .pot and .po files; they will be 6 | # called .pot? 7 | project_name: puppetlabs-chocolatey 8 | # This is used in comments in the .pot and .po files to indicate what 9 | # project the files belong to and should bea little more desctiptive than 10 | # 11 | package_name: puppetlabs-chocolatey 12 | # The locale that the default messages in the .pot file are in 13 | default_locale: en 14 | # The email used for sending bug reports. 15 | bugs_address: docs@puppet.com 16 | # The holder of the copyright. 17 | copyright_holder: Puppet, Inc. 18 | # This determines which comments in code should be eligible for translation. 19 | # Any comments that start with this string will be externalized. (Leave 20 | # empty to include all.) 21 | comments_tag: TRANSLATOR 22 | # Patterns for +Dir.glob+ used to find all files that might contain 23 | # translatable content, relative to the project root directory 24 | source_files: 25 | - './lib/**/*.rb' 26 | 27 | -------------------------------------------------------------------------------- /manifests/config.pp: -------------------------------------------------------------------------------- 1 | # @summary Handles configuration of Chocolatey 2 | # 3 | # @api private 4 | class chocolatey::config { 5 | assert_private() 6 | 7 | # this will require a second converge when choco is not 8 | # installed the first time through. This is on purpose 9 | # as we don't want to try to set these values for a 10 | # version less than 0.9.9 and we don't know what the 11 | # user may link to - it could be an older version of 12 | # Chocolatey 13 | 14 | $_choco_version = $chocolatey::chocolatey_version ? { 15 | undef => '0', 16 | default => $chocolatey::chocolatey_version 17 | } 18 | 19 | # lint:ignore:80chars 20 | if versioncmp($_choco_version, '0.9.9.0') >= 0 and versioncmp($_choco_version, '0.9.10.0') < 0 { 21 | $_choco_exe_path = "${chocolatey::choco_install_location}\\bin\\choco.exe" 22 | 23 | $_enable_autouninstaller = $chocolatey::enable_autouninstaller ? { 24 | false => 'disable', 25 | default => 'enable' 26 | } 27 | 28 | exec { "chocolatey_autouninstaller_${_enable_autouninstaller}": 29 | path => $facts['path'], 30 | command => "${_choco_exe_path} feature -r ${_enable_autouninstaller} -n autoUninstaller", 31 | unless => "cmd.exe /c ${_choco_exe_path} feature list -r | findstr /B /I /C:\"autoUninstaller - [${_enable_autouninstaller}d]\"", 32 | environment => ["ChocolateyInstall=${chocolatey::choco_install_location}"], 33 | } 34 | } 35 | # lint:endignore 36 | } 37 | -------------------------------------------------------------------------------- /manifests/init.pp: -------------------------------------------------------------------------------- 1 | # @summary Used for managing installation and configuration of Chocolatey itself. 2 | # 3 | # @author Rob Reynolds, Rich Siegel, and puppet-chocolatey contributors 4 | # 5 | # @example Default - This will by default ensure Chocolatey is installed and ready for use. 6 | # include chocolatey 7 | # 8 | # @example Override default install location 9 | # class {'chocolatey': 10 | # choco_install_location => 'D:\secured\choco', 11 | # } 12 | # 13 | # @example Use an internal Chocolatey.nupkg for installation 14 | # class {'chocolatey': 15 | # chocolatey_download_url => 'https://internalurl/to/chocolatey.nupkg', 16 | # use_7zip => false, 17 | # choco_install_timeout_seconds => 2700, 18 | # } 19 | # 20 | # @example Use a file chocolatey.0.9.9.9.nupkg for installation 21 | # class {'chocolatey': 22 | # chocolatey_download_url => 'file:///c:/location/of/chocolatey.0.9.9.9.nupkg', 23 | # use_7zip => false, 24 | # choco_install_timeout_seconds => 2700, 25 | # } 26 | # 27 | # @example Log chocolatey bootstrap installer script output 28 | # class {'chocolatey': 29 | # log_output => true, 30 | # } 31 | # 32 | # @example Disable autouninstaller (use when less than 0.9.9.8) 33 | # class {'chocolatey': 34 | # enable_autouninstaller => false, 35 | # } 36 | # 37 | # @param [String] choco_install_location Where Chocolatey install should be 38 | # located. This needs to be an absolute path starting with a drive letter 39 | # e.g. `c:\`. Defaults to the currently detected install location based on 40 | # the `ChocolateyInstall` environment variable, falls back to 41 | # `'C:\ProgramData\chocolatey'`. 42 | # 43 | # @param [Boolean] use_7zip Whether to use built-in shell or allow installer 44 | # to download 7zip to extract `chocolatey.nupkg` during installation. 45 | # Defaults to `false`. 46 | # 47 | # @param [String] seven_zip_download_url Specifies the source file for 7za.exe. 48 | # Supports all sources supported by Puppet's file resource. You should use 49 | # a 32bit binary for compatibility. 50 | # Defaults to 'https://chocolatey.org/7za.exe'. 51 | # 52 | # @param [Integer] choco_install_timeout_seconds How long in seconds should 53 | # be allowed for the install of Chocolatey (including .NET Framework 4 if 54 | # necessary). Defaults to `1500` (25 minutes). 55 | # 56 | # @param [String] chocolatey_download_url A url that will return 57 | # `chocolatey.nupkg`. This must be a url, but not necessarily an OData feed. 58 | # Any old url location will work. Defaults to 59 | # `'https://chocolatey.org/api/v2/package/chocolatey/'`. 60 | # 61 | # @param [Boolean] enable_autouninstaller [Deprecated] - Should auto 62 | # uninstaller be turned on? Auto uninstaller is what allows Chocolatey to 63 | # automatically manage the uninstall of software from Programs and Features 64 | # without necessarily requiring a `chocolateyUninstall.ps1` file in the 65 | # package. Defaults to `true`. Setting is ignored in Chocolatey v0.9.10+. 66 | # 67 | # @param [Boolean] log_output Log output from the installer. Defaults to 68 | # `false`. 69 | # 70 | # @param [String] chocolatey_version - **Informational** parameter to tell 71 | # Chocolatey what version to expect and to pre-load features with, falls 72 | # back to `$::chocolateyversion`. 73 | # 74 | # @param install_proxy Proxy server to use to use for installation of chocolatey itself or 75 | # `undef` to not use a proxy 76 | class chocolatey ( 77 | Stdlib::Windowspath $choco_install_location = $facts['choco_install_path'], 78 | Boolean $use_7zip = false, 79 | String[1] $seven_zip_download_url = 'https://chocolatey.org/7za.exe', 80 | Integer $choco_install_timeout_seconds = 1500, 81 | Stdlib::Filesource $chocolatey_download_url = 'https://chocolatey.org/api/v2/package/chocolatey/', 82 | Boolean $enable_autouninstaller = true, 83 | Boolean $log_output = false, 84 | String[1] $chocolatey_version = $facts['chocolateyversion'], 85 | Optional[String[1]] $install_proxy = undef, 86 | ) { 87 | class { 'chocolatey::install': } 88 | -> class { 'chocolatey::config': } 89 | 90 | contain 'chocolatey::install' 91 | contain 'chocolatey::config' 92 | } 93 | -------------------------------------------------------------------------------- /manifests/install.pp: -------------------------------------------------------------------------------- 1 | # @summary Handles installation of Chocolatey 2 | # 3 | # @api private 4 | class chocolatey::install { 5 | assert_private() 6 | 7 | $install_proxy = $chocolatey::install_proxy 8 | $_install_proxy = $install_proxy ? { 9 | undef => '$false', 10 | default => "'${install_proxy}'", 11 | } 12 | $_download_url = $chocolatey::chocolatey_download_url 13 | $seven_zip_download_url = $chocolatey::seven_zip_download_url 14 | $seven_zip_exe = "${facts['choco_temp_dir']}\\7za.exe" 15 | 16 | # lint:ignore:only_variable_string 17 | if "${_download_url}" =~ /^http(s)?:\/\/.*api\/v2\/package.*\/$/ and "${chocolatey::chocolatey_version}" =~ /\d+\./ { 18 | # Assume a nuget server source and we want to download a specific version instead the most current 19 | $download_url = "${_download_url}${chocolatey::chocolatey_version}" 20 | } else { 21 | $download_url = $_download_url 22 | } 23 | # lint:endignore 24 | 25 | if $chocolatey::use_7zip { 26 | $unzip_type = '7zip' 27 | file { $seven_zip_exe: 28 | ensure => file, 29 | source => $seven_zip_download_url, 30 | replace => false, 31 | mode => '0755', 32 | before => Exec['install_chocolatey_official'], 33 | } 34 | } else { 35 | $unzip_type = 'windows' 36 | } 37 | 38 | registry_value { 'ChocolateyInstall environment value': 39 | ensure => present, 40 | path => 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\ChocolateyInstall', 41 | type => 'string', 42 | data => $chocolatey::choco_install_location, 43 | } 44 | 45 | $install_parameters = { 46 | 'download_url' => $download_url, 47 | 'unzip_type' => $unzip_type, 48 | '_install_proxy' => $_install_proxy, 49 | 'seven_zip_exe' => $seven_zip_exe, 50 | } 51 | 52 | exec { 'install_chocolatey_official': 53 | command => epp('chocolatey/InstallChocolatey.ps1.epp', $install_parameters), 54 | creates => "${chocolatey::choco_install_location}\\bin\\choco.exe", 55 | provider => powershell, 56 | timeout => $chocolatey::choco_install_timeout_seconds, 57 | logoutput => $chocolatey::log_output, 58 | environment => ["ChocolateyInstall=${chocolatey::choco_install_location}"], 59 | require => Registry_value['ChocolateyInstall environment value'], 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppetlabs-chocolatey", 3 | "version": "8.0.2", 4 | "author": "puppetlabs", 5 | "summary": "Chocolatey package provider for Puppet", 6 | "license": "Apache-2.0", 7 | "source": "https://github.com/puppetlabs/puppetlabs-chocolatey", 8 | "project_page": "https://github.com/puppetlabs/puppetlabs-chocolatey", 9 | "issues_url": "https://github.com/puppetlabs/puppetlabs-chocolatey/issues", 10 | "dependencies": [ 11 | { 12 | "name": "puppetlabs/stdlib", 13 | "version_requirement": ">= 4.6.0 < 10.0.0" 14 | }, 15 | { 16 | "name": "puppetlabs/powershell", 17 | "version_requirement": ">= 1.0.1 < 7.0.0" 18 | }, 19 | { 20 | "name": "puppetlabs/registry", 21 | "version_requirement": ">= 1.0.0 < 6.0.0" 22 | }, 23 | { 24 | "name": "puppetlabs/ruby_task_helper", 25 | "version_requirement": ">= 0.4.0 < 1.0.0" 26 | } 27 | ], 28 | "operatingsystem_support": [ 29 | { 30 | "operatingsystem": "Windows", 31 | "operatingsystemrelease": [ 32 | "10", 33 | "2012", 34 | "2012 R2", 35 | "2016", 36 | "2019", 37 | "2022" 38 | ] 39 | } 40 | ], 41 | "requirements": [ 42 | { 43 | "name": "puppet", 44 | "version_requirement": ">= 7.0.0 < 9.0.0" 45 | } 46 | ], 47 | "description": "Chocolatey package provider for Puppet", 48 | "tags": [ 49 | "microsoft", 50 | "powershell", 51 | ".NET Framework", 52 | ".Net", 53 | "dot_net", 54 | "chocolatey", 55 | "package", 56 | "package manager", 57 | "chocolatey for business", 58 | "chocolatey professional" 59 | ], 60 | "pdk-version": "3.2.0", 61 | "template-url": "https://github.com/puppetlabs/pdk-templates.git#main", 62 | "template-ref": "tags/3.2.0.4-0-g5d17ec1" 63 | } 64 | -------------------------------------------------------------------------------- /pdk.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | ignore: [] 3 | -------------------------------------------------------------------------------- /provision.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | default: 3 | provisioner: abs 4 | images: 5 | - win-2016-x86_64 6 | vagrant: 7 | provisioner: vagrant 8 | images: 9 | - gusztavvargadr/windows-server 10 | release_checks: 11 | provisioner: abs 12 | images: 13 | - win-2012-x86_64 14 | - win-2012r2-x86_64 15 | - win-2016-core-x86_64 16 | - win-2019-core-x86_64 17 | - win-81-x86_64 18 | - win-10-pro-x86_64 19 | release_checks_7: 20 | provisioner: abs 21 | images: 22 | - win-2012r2-x86_64 23 | - win-2016-x86_64 24 | - win-2019-x86_64 25 | - win-10-pro-x86_64 26 | -------------------------------------------------------------------------------- /spec/acceptance/config_values_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'chocolateyconfig' do 6 | context 'create chocolateyconfig resource' do 7 | include_context 'backup and reset config' 8 | 9 | let(:pp_chocolateysource) do 10 | <<-MANIFEST 11 | chocolateyconfig {'hello123': 12 | ensure => present, 13 | value => 'this guy', 14 | } 15 | MANIFEST 16 | end 17 | 18 | it 'applies manifest, sets config' do 19 | idempotent_apply(pp_chocolateysource) 20 | run_shell(config_content_command) do |result| 21 | expect(get_xml_value("//config/add[@key='hello123']/@value", result.stdout).to_s).to match(%r{this guy}) 22 | end 23 | end 24 | end 25 | 26 | context 'add value to existing chocolateyconfig' do 27 | include_context 'backup and reset config' 28 | 29 | let(:pp_chocolateysource) do 30 | <<-MANIFEST 31 | chocolateyconfig {'proxy': 32 | ensure => present, 33 | value => 'https://somewhere', 34 | } 35 | MANIFEST 36 | end 37 | 38 | it 'applies manifest, sets config' do 39 | idempotent_apply(pp_chocolateysource) 40 | run_shell(config_content_command) do |result| 41 | expect(get_xml_value("//config/add[@key='proxy']/@value", result.stdout).to_s).to match(%r{https://somewhere}) 42 | end 43 | end 44 | end 45 | 46 | context 'change config value on existing chocolateyconfig' do 47 | include_context 'backup and reset config' 48 | 49 | let(:pp_chocolateysource) do 50 | <<-MANIFEST 51 | chocolateyconfig {'proxyUser': 52 | value => 'bob', 53 | } 54 | MANIFEST 55 | end 56 | 57 | let(:pp_chocolateysource_changed) do 58 | <<-MANIFEST 59 | chocolateyconfig {'proxyuser': 60 | value => 'tim', 61 | } 62 | MANIFEST 63 | end 64 | 65 | it 'applies manifest, sets up initial state' do 66 | idempotent_apply(pp_chocolateysource) 67 | run_shell(config_content_command) do |result| 68 | expect(get_xml_value("//config/add[@key='proxyUser']/@value", result.stdout).to_s).to match(%r{bob}) 69 | end 70 | end 71 | 72 | it 'applies manifest, sets config' do 73 | idempotent_apply(pp_chocolateysource_changed) 74 | run_shell(config_content_command) do |result| 75 | expect(get_xml_value("//config/add[@key='proxyUser']/@value", result.stdout).to_s).to match(%r{tim}) 76 | end 77 | end 78 | end 79 | 80 | context "create chocolateyconfig containing 'password' in name" do 81 | include_context 'backup and reset config' 82 | 83 | let(:config_value) do 84 | pp_chocolateysource_changed = <<-MANIFEST 85 | chocolateyconfig {'proxyPassword': 86 | value => 'secrect', 87 | } 88 | MANIFEST 89 | 90 | idempotent_apply(pp_chocolateysource_changed) 91 | result = run_shell(config_content_command) 92 | get_xml_value("//config/add[@key='proxyPassword']/@value", result.stdout).to_s 93 | end 94 | 95 | let(:pp_chocolateysource) do 96 | <<-MANIFEST 97 | chocolateyconfig {'proxyPassword': 98 | value => 'secrect2', 99 | } 100 | MANIFEST 101 | end 102 | 103 | it "applies manifest, doesn't change password field" do 104 | idempotent_apply(pp_chocolateysource) 105 | run_shell(config_content_command) do |result| 106 | expect(get_xml_value("//config/add[@key='proxyPassword']/@value", result.stdout).to_s).to eq(config_value) 107 | end 108 | end 109 | end 110 | 111 | context 'create chocolateyconfig with no value set' do 112 | include_context 'backup and reset config' 113 | 114 | let(:pp_chocolateysource) do 115 | <<-MANIFEST 116 | chocolateyconfig {'bob': 117 | ensure => present, 118 | } 119 | MANIFEST 120 | end 121 | 122 | it 'raises error' do 123 | apply_manifest(pp_chocolateysource, expect_failures: true) do |result| 124 | expect(result.exit_code).to eq(1) 125 | expect(result.stderr).to match(%r{Unless ensure => absent, value is required}) 126 | end 127 | end 128 | end 129 | 130 | context "remove value fom chocolateyconfig containing 'password' in name" do 131 | include_context 'backup and reset config' 132 | 133 | let(:pp_chocolateysource) do 134 | <<-MANIFEST 135 | chocolateyconfig {'proxyPassword': 136 | value => 'secret', 137 | } 138 | MANIFEST 139 | end 140 | 141 | let(:pp_chocolateysource_changed) do 142 | <<-MANIFEST 143 | chocolateyconfig {'proxyPassword': 144 | ensure => absent, 145 | } 146 | MANIFEST 147 | end 148 | 149 | it 'applies manifest, sets initial state' do 150 | idempotent_apply(pp_chocolateysource) 151 | run_shell(config_content_command) do |result| 152 | expect(get_xml_value("//config/add[@key='proxyPassword']/@value", result.stdout).to_s).to match(%r{.+}) 153 | end 154 | end 155 | 156 | it 'applies manifest, removes key from config' do 157 | idempotent_apply(pp_chocolateysource_changed) 158 | run_shell(config_content_command) do |result| 159 | expect(get_xml_value("//config/add[@key='proxyPassword']/@value", result.stdout).to_s).not_to match(%r{.+}) 160 | end 161 | end 162 | end 163 | 164 | context 'remove value from chocolateyconfig' do 165 | include_context 'backup and reset config' 166 | 167 | let(:pp_chocolateysource) do 168 | <<-MANIFEST 169 | chocolateyconfig {'amadeupvalue': 170 | ensure => present, 171 | value => '10', 172 | } 173 | MANIFEST 174 | end 175 | 176 | let(:pp_chocolateysource_changed) do 177 | <<-MANIFEST 178 | chocolateyconfig {'amadeupvalue': 179 | ensure => absent, 180 | } 181 | MANIFEST 182 | end 183 | 184 | it 'applies manifest, sets initial state' do 185 | idempotent_apply(pp_chocolateysource) 186 | run_shell(config_content_command) do |result| 187 | expect(get_xml_value("//config/add[@key='amadeupvalue']/@value", result.stdout).to_s).to match(%r{10}) 188 | end 189 | end 190 | 191 | it 'applies manifest, removes key from config' do 192 | idempotent_apply(pp_chocolateysource_changed) 193 | run_shell(config_content_command) do |result| 194 | expect(get_xml_value("//config/add[@key='amadeupvalue']/@value", result.stdout).to_s).not_to match(%r{.+}) 195 | end 196 | end 197 | end 198 | end 199 | -------------------------------------------------------------------------------- /spec/acceptance/features_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'chocolateyfeature resource' do 6 | context 'disable a disabled chocolateyfeature' do 7 | include_context 'backup and reset config' 8 | 9 | let(:pp_chocolateyfeature) do 10 | <<-MANIFEST 11 | chocolateyfeature {'failOnAutoUninstaller': 12 | ensure => disabled, 13 | } 14 | MANIFEST 15 | end 16 | 17 | it 'applies manifest, chocolateyfeature remains disabled' do 18 | idempotent_apply(pp_chocolateyfeature) 19 | run_shell(config_content_command) do |result| 20 | expect(get_xml_value("//features/feature[@name='failOnAutoUninstaller']/@enabled", result.stdout).to_s).to match(%r{false}) 21 | end 22 | end 23 | end 24 | 25 | context 'disable an enabled chocolateyfeature' do 26 | include_context 'backup and reset config' 27 | 28 | let(:pp_chocolateyfeature) do 29 | <<-MANIFEST 30 | chocolateyfeature { 'checksumFiles': 31 | ensure => enabled, 32 | } 33 | MANIFEST 34 | end 35 | 36 | let(:pp_chocolateyfeature_disabled) do 37 | <<-MANIFEST 38 | chocolateyfeature { 'checksumFiles': 39 | ensure => disabled, 40 | } 41 | MANIFEST 42 | end 43 | 44 | it 'applies manifest, enables chocolateyfeature' do 45 | idempotent_apply(pp_chocolateyfeature) 46 | run_shell(config_content_command) do |result| 47 | expect(get_xml_value("//features/feature[@name='checksumFiles']/@enabled", result.stdout).to_s).to match(%r{true}) 48 | end 49 | end 50 | 51 | it 'applies manifest, disables chocolateyfeature' do 52 | idempotent_apply(pp_chocolateyfeature_disabled) 53 | run_shell(config_content_command) do |result| 54 | expect(get_xml_value("//features/feature[@name='checksumFiles']/@enabled", result.stdout).to_s).to match(%r{false}) 55 | end 56 | end 57 | end 58 | 59 | context 'enable a disabled chocolateyfeature' do 60 | include_context 'backup and reset config' 61 | 62 | let(:pp_chocolateyfeature) do 63 | <<-MANIFEST 64 | chocolateyfeature {'failOnAutoUninstaller': 65 | ensure => disabled, 66 | } 67 | MANIFEST 68 | end 69 | 70 | let(:pp_chocolateyfeature_enabled) do 71 | <<-MANIFEST 72 | chocolateyfeature {'failOnAutoUninstaller': 73 | ensure => enabled, 74 | } 75 | MANIFEST 76 | end 77 | 78 | it 'applies manifest, disables chocolateyfeature' do 79 | idempotent_apply(pp_chocolateyfeature) 80 | run_shell(config_content_command) do |result| 81 | expect(get_xml_value("//features/feature[@name='failOnAutoUninstaller']/@enabled", result.stdout).to_s).to match(%r{false}) 82 | end 83 | end 84 | 85 | it 'applies manifest, enables chocolateyfeature' do 86 | idempotent_apply(pp_chocolateyfeature_enabled) 87 | run_shell(config_content_command) do |result| 88 | expect(get_xml_value("//features/feature[@name='failOnAutoUninstaller']/@enabled", result.stdout).to_s).to match(%r{true}) 89 | end 90 | end 91 | end 92 | 93 | context 'enable enabled chocolateyfeature' do 94 | include_context 'backup and reset config' 95 | 96 | let(:pp_chocolateyfeature) do 97 | <<-MANIFEST 98 | chocolateyfeature {'usePackageExitCodes': 99 | ensure => enabled, 100 | } 101 | MANIFEST 102 | end 103 | 104 | it 'applies manifest, chocolateyfeature remains enabled' do 105 | idempotent_apply(pp_chocolateyfeature) 106 | run_shell(config_content_command) do |result| 107 | expect(get_xml_value("//features/feature[@name='usePackageExitCodes']/@enabled", result.stdout).to_s).to match(%r{true}) 108 | end 109 | end 110 | end 111 | 112 | context 'enable non-existent chocolateyfeature' do 113 | include_context 'backup and reset config' 114 | 115 | let(:pp_chocolateyfeature) do 116 | <<-MANIFEST 117 | chocolateyfeature {'idontexistfeature123123': 118 | ensure => enabled, 119 | } 120 | MANIFEST 121 | end 122 | 123 | it 'raises error' do 124 | apply_manifest(pp_chocolateyfeature, expect_failures: true) do |result| 125 | # exit code of 0 is returned as resource is created... 126 | # expect(result.exit_code).to eq(1) 127 | expect(result.stderr).to match(%r{Feature 'idontexistfeature123123' not found}) 128 | end 129 | end 130 | end 131 | 132 | context 'remove chocolateyfeature' do 133 | include_context 'backup and reset config' 134 | 135 | let(:pp_chocolateyfeature) do 136 | <<-MANIFEST 137 | chocolateyfeature {'checksumFiles': 138 | ensure => absent, 139 | } 140 | MANIFEST 141 | end 142 | 143 | it 'raises error' do 144 | apply_manifest(pp_chocolateyfeature, expect_failures: true) do |result| 145 | expect(result.exit_code).to eq(1) 146 | expect(result.stderr).to match(%r{Error: Parameter ensure failed on Chocolateyfeature\[checksumFiles\]: Invalid value "absent"}) 147 | end 148 | end 149 | end 150 | end 151 | -------------------------------------------------------------------------------- /spec/acceptance/package_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | package_exe_path = %(C:\\'Program Files\\7-Zip\\7z.exe') 6 | software_uninstall_command = %(cmd.exe /C "C:\\Program Files\\7-Zip\\uninstall.exe" /S) 7 | package_name = '7zip.install' 8 | 9 | describe 'package resource' do 10 | let(:pp_chocolatey_package) do 11 | <<-MANIFEST 12 | package { "#{package_name}": 13 | ensure => present, 14 | provider => chocolatey, 15 | } 16 | MANIFEST 17 | end 18 | 19 | let(:pp_chocolatey_package_removed) do 20 | <<-MANIFEST 21 | package { "#{package_name}": 22 | ensure => absent, 23 | provider => chocolatey, 24 | } 25 | MANIFEST 26 | end 27 | 28 | before(:all) do 29 | result = run_shell("powershell.exe -EncodedCommand #{encode_command("Test-Path #{package_exe_path}")}") 30 | run_shell("powershell.exe -EncodedCommand #{encode_command(software_uninstall_command.to_s)}") if result.stdout.include?('True') 31 | end 32 | 33 | after(:all) do 34 | result = run_shell("powershell.exe -EncodedCommand #{encode_command("Test-Path #{package_exe_path}")}") 35 | run_shell("powershell.exe -EncodedCommand #{encode_command(software_uninstall_command.to_s)}") if result.stdout.include?('True') 36 | end 37 | 38 | context 'install package' do 39 | it 'checks package is not installed' do 40 | run_shell("powershell.exe -EncodedCommand #{encode_command("Test-Path #{package_exe_path}")}") do |result| 41 | expect(result.stdout).to match(%r{False}) 42 | end 43 | end 44 | 45 | it 'installs package' do 46 | apply_manifest(pp_chocolatey_package) do |result| 47 | expect(result.stdout).to match(%r{Notice: /Stage\[main\]/Main/Package\[#{package_name}\]/ensure: created}) 48 | end 49 | apply_manifest(pp_chocolatey_package, catch_changes: true) 50 | end 51 | end 52 | 53 | context 'remove package' do 54 | it 'checks package is installed' do 55 | run_shell("powershell.exe -EncodedCommand #{encode_command("Test-Path #{package_exe_path}")}") do |result| 56 | expect(result.stdout).to match(%r{True}) 57 | end 58 | end 59 | 60 | it 'uninstalls package' do 61 | apply_manifest(pp_chocolatey_package_removed) do |result| 62 | expect(result.stdout).to match(%r{Stage\[main\]/Main/Package\[#{package_name}\]/ensure: removed}) 63 | end 64 | apply_manifest(pp_chocolatey_package_removed, catch_changes: true) 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/acceptance/sources_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'chocolateysource resource' do 6 | context 'create resource' do 7 | include_context 'backup and reset config' 8 | 9 | let(:pp_remove) do 10 | <<-MANIFEST 11 | chocolateysource {'chocolatey': 12 | ensure => absent, 13 | } 14 | MANIFEST 15 | end 16 | 17 | let(:pp_chocolateysource) do 18 | <<-MANIFEST 19 | chocolateysource {'chocolatey': 20 | ensure => present, 21 | location => 'https://chocolatey.org/api/v2', 22 | priority => 2, 23 | user => 'bob', 24 | password => 'yes', 25 | bypass_proxy => true, 26 | allow_self_service => true, 27 | admin_only => true, 28 | } 29 | MANIFEST 30 | end 31 | 32 | it 'applies manifest, sets config' do 33 | apply_manifest(pp_remove) 34 | apply_manifest(pp_chocolateysource, debug: true) do |result| 35 | expect(result.stdout).to match(%r{Debug: Executing: '\[redacted\]'}) 36 | end 37 | run_shell(config_content_command, acceptable_exit_codes: [0]) do |result| 38 | expect(get_xml_value("//sources/source[@id='chocolatey']/@value", result.stdout).to_s).to match(%r{https://chocolatey.org/api/v2}) 39 | expect(get_xml_value("//sources/source[@id='chocolatey']/@priority", result.stdout).to_s).to match(%r{2}) 40 | expect(get_xml_value("//sources/source[@id='chocolatey']/@user", result.stdout).to_s).to match(%r{bob}) 41 | expect(get_xml_value("//sources/source[@id='chocolatey']/@password", result.stdout).to_s).to match(%r{.+}) 42 | expect(get_xml_value("//sources/source[@id='chocolatey']/@disabled", result.stdout).to_s).to match(%r{false}) 43 | expect(get_xml_value("//sources/source[@id='chocolatey']/@bypassProxy", result.stdout).to_s).to match(%r{true}) 44 | expect(get_xml_value("//sources/source[@id='chocolatey']/@selfService", result.stdout).to_s).to match(%r{true}) 45 | expect(get_xml_value("//sources/source[@id='chocolatey']/@adminOnly", result.stdout).to_s).to match(%r{true}) 46 | end 47 | end 48 | end 49 | 50 | context 'change existing resource' do 51 | include_context 'backup and reset config' 52 | 53 | let(:pp_chocolateysource) do 54 | <<-MANIFEST 55 | chocolateysource {'chocolatey': 56 | ensure => present, 57 | location => 'https://chocolatey.org/api/v2', 58 | priority => 2, 59 | user => 'bob', 60 | password => 'yes', 61 | bypass_proxy => true, 62 | allow_self_service => true, 63 | admin_only => true, 64 | } 65 | MANIFEST 66 | end 67 | 68 | let(:pp_chocolateysource_changed) do 69 | <<-MANIFEST 70 | chocolateysource {'chocolatey': 71 | ensure => present, 72 | location => 'c:\\packages', 73 | priority => 5, 74 | user => 'doot', 75 | password => 'password123', 76 | bypass_proxy => false, 77 | allow_self_service => false, 78 | admin_only => false, 79 | } 80 | MANIFEST 81 | end 82 | 83 | it 'applies manifests' do 84 | idempotent_apply(pp_chocolateysource) 85 | idempotent_apply(pp_chocolateysource_changed) 86 | end 87 | 88 | it 'sets changed config' do 89 | run_shell(config_content_command) do |result| 90 | expect(get_xml_value("//sources/source[@id='chocolatey']/@value", result.stdout).to_s).to match(%r{c:\\packages}) 91 | expect(get_xml_value("//sources/source[@id='chocolatey']/@priority", result.stdout).to_s).to match(%r{5}) 92 | expect(get_xml_value("//sources/source[@id='chocolatey']/@user", result.stdout).to_s).to match(%r{doot}) 93 | expect(get_xml_value("//sources/source[@id='chocolatey']/@password", result.stdout).to_s).to match(%r{.+}) 94 | expect(get_xml_value("//sources/source[@id='chocolatey']/@disabled", result.stdout).to_s).to match(%r{false}) 95 | expect(get_xml_value("//sources/source[@id='chocolatey']/@bypassProxy", result.stdout).to_s).to match(%r{false}) 96 | expect(get_xml_value("//sources/source[@id='chocolatey']/@selfService", result.stdout).to_s).to match(%r{false}) 97 | expect(get_xml_value("//sources/source[@id='chocolatey']/@adminOnly", result.stdout).to_s).to match(%r{false}) 98 | end 99 | end 100 | end 101 | 102 | context 'remove values from an existing resource' do 103 | include_context 'backup and reset config' 104 | 105 | let(:pp_chocolateysource) do 106 | <<-MANIFEST 107 | chocolateysource {'chocolatey': 108 | ensure => present, 109 | location => 'https://chocolatey.org/api/v2', 110 | priority => 2, 111 | user => 'bob', 112 | password => 'yes', 113 | bypass_proxy => true, 114 | allow_self_service => true, 115 | admin_only => true, 116 | } 117 | MANIFEST 118 | end 119 | 120 | let(:pp_chocolateysource_remove) do 121 | <<-MANIFEST 122 | chocolateysource {'chocolatey': 123 | ensure => present, 124 | location => 'https://chocolatey.org/api/v2', 125 | } 126 | MANIFEST 127 | end 128 | 129 | it 'applies manifest, sets config' do 130 | idempotent_apply(pp_chocolateysource) 131 | run_shell(config_content_command) do |result| 132 | expect(get_xml_value("//sources/source[@id='chocolatey']/@value", result.stdout).to_s).to match(%r{https://chocolatey.org/api/v2}) 133 | expect(get_xml_value("//sources/source[@id='chocolatey']/@priority", result.stdout).to_s).to match(%r{2}) 134 | expect(get_xml_value("//sources/source[@id='chocolatey']/@user", result.stdout).to_s).to match(%r{bob}) 135 | expect(get_xml_value("//sources/source[@id='chocolatey']/@password", result.stdout).to_s).to match(%r{.+}) 136 | expect(get_xml_value("//sources/source[@id='chocolatey']/@disabled", result.stdout).to_s).to match(%r{false}) 137 | expect(get_xml_value("//sources/source[@id='chocolatey']/@bypassProxy", result.stdout).to_s).to match(%r{true}) 138 | expect(get_xml_value("//sources/source[@id='chocolatey']/@selfService", result.stdout).to_s).to match(%r{true}) 139 | expect(get_xml_value("//sources/source[@id='chocolatey']/@adminOnly", result.stdout).to_s).to match(%r{true}) 140 | end 141 | end 142 | 143 | it 'applies manifest, unsets config attributes' do 144 | idempotent_apply(pp_chocolateysource_remove) 145 | run_shell(config_content_command) do |result| 146 | expect(get_xml_value("//sources/source[@id='chocolatey']/@value", result.stdout).to_s).to match(%r{https://chocolatey.org/api/v2}) 147 | expect(get_xml_value("//sources/source[@id='chocolatey']/@priority", result.stdout).to_s).to match(%r{0}) 148 | expect(get_xml_value("//sources/source[@id='chocolatey']/@user", result.stdout).to_s).to match(%r{}) 149 | expect(get_xml_value("//sources/source[@id='chocolatey']/@password", result.stdout).to_s).to match(%r{}) 150 | expect(get_xml_value("//sources/source[@id='chocolatey']/@disabled", result.stdout).to_s).to match(%r{}) 151 | expect(get_xml_value("//sources/source[@id='chocolatey']/@bypassProxy", result.stdout).to_s).to match(%r{false}) 152 | expect(get_xml_value("//sources/source[@id='chocolatey']/@selfService", result.stdout).to_s).to match(%r{false}) 153 | expect(get_xml_value("//sources/source[@id='chocolatey']/@adminOnly", result.stdout).to_s).to match(%r{false}) 154 | end 155 | end 156 | end 157 | 158 | context 'specify only location' do 159 | include_context 'backup and reset config' 160 | 161 | let(:pp_chocolateysource) do 162 | <<-MANIFEST 163 | chocolateysource {'test': 164 | location => 'c:\\packages', 165 | } 166 | MANIFEST 167 | end 168 | 169 | it 'applies manifest, sets config' do 170 | idempotent_apply(pp_chocolateysource) 171 | run_shell(config_content_command) do |result| 172 | expect(get_xml_value("//sources/source[@id='test']/@value", result.stdout).to_s).to match(%r{c:\\packages}) 173 | expect(get_xml_value("//sources/source[@id='test']/@disabled", result.stdout).to_s).to match(%r{false}) 174 | end 175 | end 176 | end 177 | 178 | context 'disable resource' do 179 | include_context 'backup and reset config' 180 | 181 | let(:pp_chocolateysource) do 182 | <<-MANIFEST 183 | chocolateysource {'chocolatey': 184 | ensure => disabled, 185 | } 186 | MANIFEST 187 | end 188 | 189 | it 'applies manifest, sets config' do 190 | idempotent_apply(pp_chocolateysource) 191 | run_shell(config_content_command) do |result| 192 | expect(get_xml_value("//sources/source[@id='chocolatey']/@disabled", result.stdout).to_s).to match(%r{true}) 193 | end 194 | end 195 | end 196 | 197 | context 'exceptions' do 198 | include_context 'backup and reset config' 199 | 200 | context 'when without location set' do 201 | let(:pp_chocolateysource) do 202 | <<-MANIFEST 203 | chocolateysource {'chocolatey': 204 | ensure => present, 205 | } 206 | MANIFEST 207 | end 208 | 209 | it 'raises an error' do 210 | apply_manifest(pp_chocolateysource, expect_failures: true) do |result| 211 | expect(result.exit_code).to eq(1) 212 | expect(result.stderr).to match(%r{Error: Validation of Chocolateysource\[chocolatey\] failed: A non-empty location}) 213 | end 214 | end 215 | end 216 | 217 | context 'when invalid ensure' do 218 | let(:pp_chocolateysource) do 219 | <<-MANIFEST 220 | chocolateysource {'test': 221 | ensure => sad, 222 | location => 'c:\\packages', 223 | } 224 | MANIFEST 225 | end 226 | 227 | it 'raises an error' do 228 | apply_manifest(pp_chocolateysource, expect_failures: true) do |result| 229 | expect(result.exit_code).to eq(1) 230 | expect(result.stderr).to match(%r{Error: Parameter ensure failed on Chocolateysource\[test\]: Invalid value "sad"}) 231 | end 232 | end 233 | end 234 | 235 | context 'when password set and user not set' do 236 | let(:pp_chocolateysource) do 237 | <<-MANIFEST 238 | chocolateysource {'chocolatey': 239 | ensure => present, 240 | location => 'https://chocolatey.org/api/v2', 241 | password => 'test', 242 | } 243 | MANIFEST 244 | end 245 | 246 | it 'raises an error' do 247 | apply_manifest(pp_chocolateysource, expect_failures: true) do |result| 248 | expect(result.exit_code).to eq(1) 249 | expect(result.stderr).to match(%r{Error: Validation of Chocolateysource\[chocolatey\] failed: If specifying user/password, you must specify both values}) 250 | end 251 | end 252 | end 253 | 254 | context 'when user set and password not set' do 255 | let(:pp_chocolateysource) do 256 | <<-MANIFEST 257 | chocolateysource {'chocolatey': 258 | ensure => present, 259 | location => 'https://chocolatey.org/api/v2', 260 | user => 'tim', 261 | } 262 | MANIFEST 263 | end 264 | 265 | it 'raises an error' do 266 | apply_manifest(pp_chocolateysource, expect_failures: true) do |result| 267 | expect(result.exit_code).to eq(1) 268 | expect(result.stderr).to match(%r{Error: Validation of Chocolateysource\[chocolatey\] failed: If specifying user/password, you must specify both values}) 269 | end 270 | end 271 | end 272 | end 273 | end 274 | -------------------------------------------------------------------------------- /spec/classes/config_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'chocolatey' do 6 | let(:facts) do 7 | { 8 | path: 'C:\something' 9 | } 10 | end 11 | 12 | context 'contains config.pp' do 13 | context 'with older choco installed' do 14 | let(:facts) do 15 | super().merge(chocolateyversion: '0.9.8.33', 16 | choco_install_path: 'C:\ProgramData\chocolatey') 17 | end 18 | 19 | [true, false].each do |param_value| 20 | feature_enable = 'enable' 21 | feature_enable = 'disable' unless param_value 22 | 23 | context "enable_autouninstaller => #{param_value}" do 24 | let(:params) { { enable_autouninstaller: param_value } } 25 | 26 | it { is_expected.not_to contain_exec("chocolatey_autouninstaller_#{feature_enable}") } 27 | 28 | it { 29 | expect(subject).not_to contain_exec("chocolatey_autouninstaller_#{feature_enable}").with_command("C:\\ProgramData\\chocolatey\\bin\\choco.exe feature -r #{feature_enable} -n autoUninstaller") # rubocop:disable Layout/LineLength 30 | } 31 | end 32 | end 33 | end 34 | 35 | context 'without choco installed' do 36 | let(:facts) do 37 | super().merge(chocolateyversion: '0', 38 | choco_install_path: 'C:\ProgramData\chocolatey') 39 | end 40 | 41 | [true, false].each do |param_value| 42 | feature_enable = 'enable' 43 | feature_enable = 'disable' unless param_value 44 | 45 | context "enable_autouninstaller => #{param_value}" do 46 | let(:params) { { enable_autouninstaller: param_value } } 47 | 48 | it { is_expected.not_to contain_exec("chocolatey_autouninstaller_#{feature_enable}") } 49 | 50 | it { 51 | expect(subject).not_to contain_exec("chocolatey_autouninstaller_#{feature_enable}").with_command("C:\\ProgramData\\chocolatey\\bin\\choco.exe feature -r #{feature_enable} -n autoUninstaller") # rubocop:disable Layout/LineLength 52 | } 53 | end 54 | end 55 | end 56 | 57 | context 'with choco.exe installed' do 58 | let(:facts) do 59 | super().merge(chocolateyversion: '0.9.9.8', 60 | choco_install_path: 'C:\ProgramData\chocolatey') 61 | end 62 | 63 | [true, false].each do |param_value| 64 | feature_enable = 'enable' 65 | feature_enable = 'disable' unless param_value 66 | 67 | context "enable_autouninstaller => #{param_value}" do 68 | let(:params) { { enable_autouninstaller: param_value } } 69 | 70 | it { is_expected.to contain_exec("chocolatey_autouninstaller_#{feature_enable}") } 71 | 72 | it { 73 | expect(subject).to contain_exec("chocolatey_autouninstaller_#{feature_enable}").with_command("C:\\ProgramData\\chocolatey\\bin\\choco.exe feature -r #{feature_enable} -n autoUninstaller") 74 | } 75 | end 76 | end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/classes/init_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'chocolatey' do 6 | let(:facts) do 7 | { 8 | chocolateyversion: '0.9.9.8', 9 | choco_install_path: 'C:\ProgramData\chocolatey', 10 | path: 'C:\something' 11 | } 12 | end 13 | 14 | [{}].each do |params| 15 | context params.to_s do 16 | let(:params) { params } 17 | 18 | it 'compiles successfully' do 19 | catalogue 20 | end 21 | 22 | # it { is_expected.to compile } 23 | # it { is_expected.to compile.with_all_deps } 24 | it { is_expected.to contain_class('chocolatey') } 25 | it { is_expected.to contain_class('chocolatey::install') } 26 | it { is_expected.to contain_class('chocolatey::config') } 27 | end 28 | end 29 | 30 | context 'accepts install_proxy parameter' do 31 | let(:params) do 32 | { 33 | install_proxy: 'http://proxy.megacorp.com:3128' 34 | } 35 | end 36 | 37 | it 'compiles successfully' do 38 | catalogue 39 | end 40 | end 41 | 42 | context 'chocolatey_download_url =>' do 43 | ['https://chocolatey.org/api/v2/package/chocolatey/', 'http://location', 'file:///c:/somwhere/chocolatey.nupkg', '\\\\ciflocation\\share'].each do |param_value| 44 | context param_value.to_s do 45 | let(:params) do 46 | { 47 | chocolatey_download_url: param_value 48 | } 49 | end 50 | 51 | it 'compiles successfully' do 52 | catalogue 53 | end 54 | end 55 | end 56 | 57 | invalid_url_values = ['bob', '4', ''] 58 | not_a_string_values = [false, 3] 59 | 60 | invalid_url_values.each do |param_value| 61 | context "#{param_value} (invalid scenario)" do 62 | let(:params) do 63 | { 64 | chocolatey_download_url: param_value 65 | } 66 | end 67 | 68 | let(:error_message) { %r{Stdlib::Filesource} } 69 | 70 | it { 71 | expect { catalogue }.to raise_error(Puppet::Error, error_message) 72 | } 73 | end 74 | end 75 | 76 | not_a_string_values.each do |param_value| 77 | context "#{param_value} (invalid scenario)" do 78 | let(:params) do 79 | { 80 | chocolatey_download_url: param_value 81 | } 82 | end 83 | 84 | let(:error_message) { %r{Stdlib::Filesource} } 85 | 86 | it { 87 | expect { catalogue }.to raise_error(Puppet::Error, error_message) 88 | } 89 | end 90 | end 91 | end 92 | 93 | context 'choco_install_location =>' do 94 | ['C:\\ProgramData\\chocolatey', 'D:\\somewhere'].each do |param_value| 95 | context param_value.to_s do 96 | let(:params) do 97 | { 98 | choco_install_location: param_value 99 | } 100 | end 101 | 102 | it 'compiles successfully' do 103 | catalogue 104 | end 105 | end 106 | end 107 | 108 | [1, false].each do |param_value| 109 | context "#{param_value} (invalid scenario)" do 110 | let(:params) do 111 | { 112 | choco_install_location: param_value 113 | } 114 | end 115 | 116 | let(:error_message) { %r{Stdlib::Windowspath} } 117 | 118 | it { 119 | expect { catalogue }.to raise_error(Puppet::Error, error_message) 120 | } 121 | end 122 | end 123 | 124 | ['https://somewhere', '\\\\overhere', ''].each do |param_value| 125 | context "#{param_value} (invalid scenario)" do 126 | let(:params) do 127 | { 128 | choco_install_location: param_value 129 | } 130 | end 131 | 132 | let(:error_message) { %r{Stdlib::Windowspath} } 133 | 134 | it { 135 | expect { catalogue }.to raise_error(Puppet::Error, error_message) 136 | } 137 | end 138 | end 139 | end 140 | 141 | context 'choco_install_timeout_seconds =>' do 142 | [1500, 8000].each do |param_value| 143 | context param_value.to_s do 144 | let(:params) do 145 | { 146 | choco_install_timeout_seconds: param_value 147 | } 148 | end 149 | 150 | it 'compiles successfully' do 151 | catalogue 152 | end 153 | end 154 | end 155 | 156 | ['string', false, '', '1', '30'].each do |param_value| 157 | context "#{param_value} (invalid scenario)" do 158 | let(:params) do 159 | { 160 | choco_install_timeout_seconds: param_value 161 | } 162 | end 163 | 164 | let(:error_message) { %r{expects an Integer value} } 165 | 166 | it { 167 | expect { catalogue }.to raise_error(Puppet::Error, error_message) 168 | } 169 | end 170 | end 171 | end 172 | 173 | ['use_7zip', 'enable_autouninstaller'].each do |boolean_param| 174 | context "#{boolean_param} =>" do 175 | [true, false].each do |param_value| 176 | context param_value.to_s do 177 | let(:params) do 178 | { 179 | boolean_param.to_sym => param_value 180 | } 181 | end 182 | 183 | it 'compiles successfully' do 184 | catalogue 185 | end 186 | end 187 | end 188 | 189 | ['true', 'false', 'bob', 3, '4', ''].each do |param_value| 190 | context "#{param_value} (invalid scenario)" do 191 | let(:params) do 192 | { 193 | boolean_param.to_sym => param_value 194 | } 195 | end 196 | 197 | let(:error_message) { %r{expects a Boolean value} } 198 | 199 | it { 200 | expect { catalogue }.to raise_error(Puppet::Error, error_message) 201 | } 202 | end 203 | end 204 | end 205 | end 206 | end 207 | -------------------------------------------------------------------------------- /spec/classes/install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'chocolatey' do 6 | let(:facts) do 7 | { 8 | chocolateyversion: '0.9.9.8', 9 | choco_install_path: 'C:\ProgramData\chocolatey', 10 | choco_temp_dir: 'C:\Temp', 11 | path: 'C:\something' 12 | } 13 | end 14 | 15 | context 'contains install.pp' do 16 | ['c:\local_folder', 'C:\\ProgramData\\chocolatey'].each do |param_value| 17 | context "choco_install_location => #{param_value}" do 18 | let(:params) { { choco_install_location: param_value } } 19 | 20 | it { is_expected.to contain_exec('install_chocolatey_official').with_creates("#{param_value}\\bin\\choco.exe") } 21 | end 22 | end 23 | 24 | [1500, 35].each do |param_value| 25 | context "choco_install_timeout_seconds => #{param_value}" do 26 | let(:params) { { choco_install_timeout_seconds: param_value } } 27 | 28 | it { is_expected.to contain_exec('install_chocolatey_official').with_timeout(param_value.to_s) } 29 | end 30 | end 31 | 32 | context 'use_7zip => false' do 33 | let(:params) { { use_7zip: false } } 34 | 35 | it { 36 | expect(subject).not_to contain_file('C:\Temp\7za.exe') 37 | } 38 | end 39 | 40 | context 'use_7zip => true' do 41 | context 'seven_zip_download_url default' do 42 | let(:params) { { use_7zip: true } } 43 | 44 | it { is_expected.to contain_file('C:\Temp\7za.exe').with_source('https://chocolatey.org/7za.exe') } 45 | end 46 | 47 | context "seven_zip_download_url => 'https://packages.organization.net/7za.exe'" do 48 | let(:params) do 49 | { 50 | use_7zip: true, 51 | seven_zip_download_url: 'https://packages.organization.net/7za.exe' 52 | } 53 | end 54 | 55 | it { is_expected.to contain_file('C:\Temp\7za.exe').with_source('https://packages.organization.net/7za.exe') } 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/default_facts.yml: -------------------------------------------------------------------------------- 1 | # Use default_module_facts.yml for module specific facts. 2 | # 3 | # Facts specified here will override the values provided by rspec-puppet-facts. 4 | --- 5 | networking: 6 | ip: "172.16.254.254" 7 | ip6: "FE80:0000:0000:0000:AAAA:AAAA:AAAA" 8 | mac: "AA:AA:AA:AA:AA:AA" 9 | is_pe: false 10 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.configure do |c| 4 | c.mock_with :rspec 5 | end 6 | 7 | require 'puppetlabs_spec_helper/module_spec_helper' 8 | require 'rspec-puppet-facts' 9 | 10 | require 'spec_helper_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_local.rb')) 11 | 12 | include RspecPuppetFacts 13 | 14 | default_facts = { 15 | puppetversion: Puppet.version, 16 | facterversion: Facter.version, 17 | } 18 | 19 | default_fact_files = [ 20 | File.expand_path(File.join(File.dirname(__FILE__), 'default_facts.yml')), 21 | File.expand_path(File.join(File.dirname(__FILE__), 'default_module_facts.yml')), 22 | ] 23 | 24 | default_fact_files.each do |f| 25 | next unless File.exist?(f) && File.readable?(f) && File.size?(f) 26 | 27 | begin 28 | require 'deep_merge' 29 | default_facts.deep_merge!(YAML.safe_load(File.read(f), permitted_classes: [], permitted_symbols: [], aliases: true)) 30 | rescue StandardError => e 31 | RSpec.configuration.reporter.message "WARNING: Unable to load #{f}: #{e}" 32 | end 33 | end 34 | 35 | # read default_facts and merge them over what is provided by facterdb 36 | default_facts.each do |fact, value| 37 | add_custom_fact fact, value, merge_facts: true 38 | end 39 | 40 | RSpec.configure do |c| 41 | c.default_facts = default_facts 42 | c.before :each do 43 | # set to strictest setting for testing 44 | # by default Puppet runs at warning level 45 | Puppet.settings[:strict] = :warning 46 | Puppet.settings[:strict_variables] = true 47 | end 48 | c.filter_run_excluding(bolt: true) unless ENV['GEM_BOLT'] 49 | c.after(:suite) do 50 | RSpec::Puppet::Coverage.report!(0) 51 | end 52 | 53 | # Filter backtrace noise 54 | backtrace_exclusion_patterns = [ 55 | %r{spec_helper}, 56 | %r{gems}, 57 | ] 58 | 59 | if c.respond_to?(:backtrace_exclusion_patterns) 60 | c.backtrace_exclusion_patterns = backtrace_exclusion_patterns 61 | elsif c.respond_to?(:backtrace_clean_patterns) 62 | c.backtrace_clean_patterns = backtrace_exclusion_patterns 63 | end 64 | end 65 | 66 | # Ensures that a module is defined 67 | # @param module_name Name of the module 68 | def ensure_module_defined(module_name) 69 | module_name.split('::').reduce(Object) do |last_module, next_module| 70 | last_module.const_set(next_module, Module.new) unless last_module.const_defined?(next_module, false) 71 | last_module.const_get(next_module, false) 72 | end 73 | end 74 | 75 | # 'spec_overrides' from sync.yml will appear below this line 76 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet_litmus' 4 | require 'spec_helper_acceptance_local' if File.file?(File.join(File.dirname(__FILE__), 'spec_helper_acceptance_local.rb')) 5 | 6 | PuppetLitmus.configure! 7 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance_local.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'net/http' 4 | require 'nokogiri' 5 | require 'singleton' 6 | 7 | class LitmusHelper 8 | include Singleton 9 | include PuppetLitmus 10 | end 11 | 12 | Dir['./spec/support/**/*.rb'].sort.each { |f| require f } 13 | 14 | CHOCOLATEY_LATEST_INFO_URL = 'https://artifactory.delivery.puppetlabs.net/artifactory/api/nuget/choco-pipeline-tests/Packages()?$filter=((Id%20eq%20%27chocolatey%27)%20and%20(not%20IsPrerelease))%20and%20IsLatestVersion' 15 | 16 | def encode_command(cmd) 17 | cmd = cmd.chars.to_a.join("\x00").chomp 18 | cmd << "\x00" unless cmd[-1].eql? "\x00" 19 | # use strict_encode because linefeeds are not correctly handled in our model 20 | Base64.strict_encode64(cmd).chomp 21 | end 22 | 23 | def install_chocolatey 24 | chocolatey_pp = <<-MANIFEST 25 | include chocolatey 26 | MANIFEST 27 | 28 | LitmusHelper.instance.apply_manifest(chocolatey_pp, catch_failures: true) 29 | end 30 | 31 | def config_file_location 32 | 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config' 33 | end 34 | 35 | def backup_config 36 | bolt_run_script(File.expand_path('spec/support/scripts/backup_config.ps1', Dir.pwd)) 37 | end 38 | 39 | def reset_config 40 | bolt_run_script(File.expand_path('spec/support/scripts/reset_config.ps1', Dir.pwd)) 41 | end 42 | 43 | def get_xml_value(xpath, file_text) 44 | doc = Nokogiri::XML(file_text) 45 | 46 | doc.xpath(xpath) 47 | end 48 | 49 | def config_content_command 50 | "cmd.exe /c \"type #{config_file_location}\"" 51 | end 52 | 53 | RSpec.configure do |c| 54 | c.include_context 'backup and reset config', include_shared: true 55 | c.before(:suite) { install_chocolatey } 56 | end 57 | -------------------------------------------------------------------------------- /spec/spec_helper_local.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # require 'pry' if Bundler.rubygems.find_name('pry').any? 4 | # require 'puppetlabs_spec_helper/module_spec_helper' 5 | # require 'rake' 6 | # require 'fileutils' 7 | 8 | RSpec.configure do |_c| 9 | # set the environment variable before files are loaded, otherwise it is too late 10 | ENV['ChocolateyInstall'] = 'c:\blah' 11 | 12 | begin 13 | # rubocop:disable RSpec/AnyInstance 14 | Win32::Registry.any_instance.stubs(:[]).with('Bind') 15 | Win32::Registry.any_instance.stubs(:[]).with('Domain') 16 | Win32::Registry.any_instance.stubs(:[]).with('ChocolateyInstall').raises(Win32::Registry::Error.new(2), 'file not found yo') 17 | # rubocop:enable RSpec/AnyInstance 18 | rescue StandardError 19 | # ignore errors thrown while setting up mocks 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/support/backup_and_reset_config_context.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.shared_context 'backup and reset config' do 4 | before(:all) { backup_config } # rubocop:disable RSpec/BeforeAfterAll 5 | after(:all) { reset_config } # rubocop:disable RSpec/BeforeAfterAll 6 | end 7 | -------------------------------------------------------------------------------- /spec/support/scripts/backup_config.ps1: -------------------------------------------------------------------------------- 1 | if (Test-Path 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config') { 2 | Copy-Item -Path 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config' -Destination 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config.bkp' 3 | } 4 | -------------------------------------------------------------------------------- /spec/support/scripts/reset_config.ps1: -------------------------------------------------------------------------------- 1 | if (Test-Path 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config.bkp' ) { 2 | Move-Item -Path 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config.bkp' -Destination 'c:\\ProgramData\\chocolatey\\config\\chocolatey.config' -force 3 | } -------------------------------------------------------------------------------- /spec/tasks/chocolatey_outdated_task_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require_relative '../../tasks/outdated' 5 | 6 | describe ChocolateyOutdatedTask do 7 | subject { described_class.new.task } 8 | 9 | before(:each) do 10 | sucess_status = double 11 | allow(sucess_status).to receive(:==).with(0).and_return(true) 12 | allow(sucess_status).to receive_messages(exited?: true, exitstatus: 0) 13 | allow(Open3).to receive(:capture2).with('choco', 'outdated', '--no-color', '--limit-output').and_return([<<~OUTPUT, sucess_status]) 14 | puppet-bolt|3.20.0|3.21.0|false 15 | OUTPUT 16 | end 17 | 18 | it { is_expected.to eq({ status: [{ package: 'puppet-bolt', version: '3.20.0', available_version: '3.21.0', pinned: false }] }) } 19 | end 20 | -------------------------------------------------------------------------------- /spec/tasks/chocolatey_pin_task_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require_relative '../../tasks/pin' 5 | 6 | describe ChocolateyPinTask do 7 | subject { described_class.new.task(action: action, package: package, version: version) } 8 | 9 | let(:package) { nil } 10 | let(:version) { nil } 11 | 12 | let(:sucess_status) do 13 | res = double 14 | allow(res).to receive(:==).with(0).and_return(true) 15 | allow(res).to receive_messages(exited?: true, exitstatus: 0) 16 | res 17 | end 18 | 19 | context 'when action=list' do 20 | let(:action) { 'list' } 21 | 22 | before(:each) do 23 | allow(Open3).to receive(:capture2).with('choco', 'pin', 'list', '--no-color', '--limit-output').and_return([<<~OUTPUT, sucess_status]) 24 | puppet-bolt|3.20.0 25 | OUTPUT 26 | end 27 | 28 | it { is_expected.to eq({ status: [{ package: 'puppet-bolt', version: '3.20.0' }] }) } 29 | end 30 | 31 | context 'when action=add' do 32 | let(:action) { 'add' } 33 | let(:package) { 'puppet-bolt' } 34 | 35 | context 'without version' do 36 | before(:each) { allow(Open3).to receive(:capture2).with('choco', 'pin', 'add', '--no-color', '--limit-output', '--name', 'puppet-bolt').and_return(['', sucess_status]) } 37 | 38 | it { is_expected.to eq(nil) } 39 | end 40 | 41 | context 'with version' do 42 | let(:version) { '3.21.0' } 43 | 44 | before(:each) { allow(Open3).to receive(:capture2).with('choco', 'pin', 'add', '--no-color', '--limit-output', '--name', 'puppet-bolt', '--version', '3.21.0').and_return(['', sucess_status]) } 45 | 46 | it { is_expected.to eq(nil) } 47 | end 48 | end 49 | 50 | context 'when action=remove' do 51 | let(:action) { 'remove' } 52 | let(:package) { 'puppet-bolt' } 53 | 54 | context 'without version' do 55 | before(:each) { allow(Open3).to receive(:capture2).with('choco', 'pin', 'remove', '--no-color', '--limit-output', '--name', 'puppet-bolt').and_return(['', sucess_status]) } 56 | 57 | it { is_expected.to eq(nil) } 58 | end 59 | 60 | context 'with version' do 61 | let(:version) { '3.21.0' } 62 | 63 | before(:each) do 64 | allow(Open3).to receive(:capture2).with('choco', 'pin', 'remove', '--no-color', '--limit-output', '--name', 'puppet-bolt', '--version', '3.21.0').and_return(['', sucess_status]) 65 | end 66 | 67 | it { is_expected.to eq(nil) } 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /spec/tasks/chocolatey_status_task_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require_relative '../../tasks/status' 5 | 6 | describe ChocolateyStatusTask do 7 | subject { described_class.new.task } 8 | 9 | before(:each) do 10 | sucess_status = double 11 | allow(sucess_status).to receive(:==).with(0).and_return(true) 12 | allow(sucess_status).to receive_messages(exited?: true, exitstatus: 0) 13 | allow(Open3).to receive(:capture2).with('choco', 'list', '--local-only', '--no-color', '--limit-output').and_return([<<~OUTPUT, sucess_status]) 14 | chocolatey|0.11.3 15 | puppet-bolt|3.20.0 16 | OUTPUT 17 | end 18 | 19 | it { is_expected.to eq({ status: [{ package: 'chocolatey', version: '0.11.3' }, { package: 'puppet-bolt', version: '3.20.0' }] }) } 20 | end 21 | -------------------------------------------------------------------------------- /spec/tasks/chocolatey_task_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require_relative '../../tasks/init' 5 | 6 | describe ChocolateyTask do 7 | subject { described_class.new.task(action: action, package: package, version: version) } 8 | 9 | let(:package) { 'puppet-bolt' } 10 | let(:version) { nil } 11 | 12 | let(:sucess_status) do 13 | res = double 14 | allow(res).to receive(:==).with(0).and_return(true) 15 | allow(res).to receive_messages(exited?: true, exitstatus: 0) 16 | res 17 | end 18 | 19 | context 'when action=install' do 20 | let(:action) { 'install' } 21 | 22 | context 'without version' do 23 | before(:each) do 24 | allow(Open3).to receive(:capture2).with('choco', 'install', 'puppet-bolt', '--yes', '--no-color', '--no-progress').and_return(['', sucess_status]) 25 | end 26 | 27 | it { is_expected.to eq(nil) } 28 | end 29 | 30 | context 'with version' do 31 | let(:version) { '3.21.0' } 32 | 33 | before(:each) do 34 | allow(Open3).to receive(:capture2).with('choco', 'install', 'puppet-bolt', '--yes', '--no-color', '--no-progress', '--version', '3.21.0').and_return(['', sucess_status]) 35 | end 36 | 37 | it { is_expected.to eq(nil) } 38 | end 39 | end 40 | 41 | context 'when action=upgrade' do 42 | let(:action) { 'upgrade' } 43 | 44 | context 'without version' do 45 | before(:each) do 46 | allow(Open3).to receive(:capture2).with('choco', 'upgrade', 'puppet-bolt', '--yes', '--no-color', '--no-progress').and_return(['', sucess_status]) 47 | end 48 | 49 | it { is_expected.to eq(nil) } 50 | end 51 | 52 | context 'with version' do 53 | let(:version) { '3.21.0' } 54 | 55 | before(:each) do 56 | allow(Open3).to receive(:capture2).with('choco', 'upgrade', 'puppet-bolt', '--yes', '--no-color', '--no-progress', '--version', '3.21.0').and_return(['', sucess_status]) 57 | end 58 | 59 | it { is_expected.to eq(nil) } 60 | end 61 | end 62 | 63 | context 'when action=uninstall' do 64 | let(:action) { 'uninstall' } 65 | 66 | context 'without version' do 67 | before(:each) do 68 | allow(Open3).to receive(:capture2).with('choco', 'uninstall', 'puppet-bolt', '--yes', '--no-color', '--no-progress').and_return(['', sucess_status]) 69 | end 70 | 71 | it { is_expected.to eq(nil) } 72 | end 73 | 74 | context 'with version' do 75 | let(:version) { '3.21.0' } 76 | 77 | before(:each) do 78 | allow(Open3).to receive(:capture2).with('choco', 'uninstall', 'puppet-bolt', '--yes', '--no-color', '--no-progress', '--version', '3.21.0').and_return(['', sucess_status]) 79 | end 80 | 81 | it { is_expected.to eq(nil) } 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/unit/facter/choco_install_path_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'facter' 5 | require 'puppet_x/chocolatey/chocolatey_install' 6 | 7 | describe 'choco_install_path fact' do 8 | subject(:fact) { Facter.fact(:choco_install_path) } 9 | 10 | let(:fact_value) { subject.value } 11 | 12 | before(:each) do 13 | skip 'Not on Windows platform' unless Puppet::Util::Platform.windows? 14 | Facter.clear 15 | Facter.clear_messages 16 | end 17 | 18 | after(:each) do 19 | Facter.clear 20 | Facter.clear_messages 21 | end 22 | 23 | context 'on Windows' do 24 | it 'returns the output of PuppetX::Chocolatey::ChocolateyInstall.install_path' do 25 | expect(PuppetX::Chocolatey::ChocolateyInstall).to receive(:install_path).and_return('C:\somewhere') 26 | 27 | expect(fact_value).to eq('C:\somewhere') 28 | end 29 | 30 | it 'returns the default path when PuppetX::Chocolatey::ChocolateyInstall.install_path is nil' do 31 | expect(PuppetX::Chocolatey::ChocolateyInstall).to receive(:install_path).and_return(nil) 32 | 33 | expect(fact_value).to eq('C:\ProgramData\chocolatey') 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/unit/facter/choco_temp_dir_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'facter' 5 | require 'puppet_x/chocolatey/chocolatey_install' 6 | 7 | describe 'choco_temp_dir fact' do 8 | subject(:fact) { Facter.fact(:choco_temp_dir) } 9 | 10 | let(:fact_value) { subject.value } 11 | 12 | before(:each) do 13 | skip 'Not on Windows platform' unless Puppet::Util::Platform.windows? 14 | Facter.clear 15 | Facter.clear_messages 16 | end 17 | 18 | after(:each) do 19 | Facter.clear 20 | Facter.clear_messages 21 | end 22 | 23 | it 'returns the TEMP directory' do 24 | allow(PuppetX::Chocolatey::ChocolateyInstall).to receive(:temp_dir).and_return('waffles') 25 | 26 | expect(fact_value).to eq('waffles') 27 | end 28 | 29 | it 'returns the default path when PuppetX::Chocolatey::ChocolateyInstall.install_path is nil' do 30 | expect(PuppetX::Chocolatey::ChocolateyInstall).to receieve(:temp_dir).and_return(nil) 31 | 32 | expect(fact_value).to eq(ENV.fetch('TEMP', nil)) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/unit/facter/chocolateyversion_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'facter' 5 | require 'puppet_x/chocolatey/chocolatey_version' 6 | 7 | describe 'chocolateyversion fact' do 8 | subject(:fact) { Facter.fact(:chocolateyversion) } 9 | 10 | let(:fact_value) { subject.value } 11 | 12 | before(:each) do 13 | skip 'Not on Windows platform' unless Puppet::Util::Platform.windows? 14 | Facter.clear 15 | Facter.clear_messages 16 | end 17 | 18 | after(:each) do 19 | Facter.clear 20 | Facter.clear_messages 21 | end 22 | 23 | context 'on Windows' do 24 | it 'returns the output of PuppetX::Chocolatey::ChocolateyVersion.version' do 25 | allow(PuppetX::Chocolatey::ChocolateyVersion).to receive(:version).and_return('1.2.3') 26 | 27 | expect(fact_value).to eq('1.2.3') 28 | end 29 | 30 | it 'returns the default of 0 when PuppetX::Chocolatey::ChocolateyVersion.version is nil' do 31 | allow(PuppetX::Chocolatey::ChocolateyVersion).to receive(:version).and_return(nil) 32 | 33 | expect(fact_value).to eq('0') 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/chocolateyfeature/windows_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'stringio' 5 | require 'puppet/type/chocolateyfeature' 6 | require 'puppet/provider/chocolateyfeature/windows' 7 | require 'rexml/document' 8 | 9 | provider = Puppet::Type.type(:chocolateyfeature).provider(:windows) 10 | describe provider do 11 | let(:name) { 'allowglobalconfirmation' } 12 | let(:resource) { Puppet::Type.type(:chocolateyfeature).new(provider: :windows, name: name, ensure: 'enabled') } 13 | let(:choco_config) { 'c:\choco.config' } 14 | let(:choco_install_path) { 'c:\dude\bin\choco.exe' } 15 | let(:choco_config_contents) do 16 | <<~'EOT' 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | EOT 58 | end 59 | 60 | let(:minimum_supported_version) { '0.9.9.0' } 61 | let(:provider_class) { subject.class } 62 | let(:provider) { subject.class.new(resource) } 63 | 64 | before(:each) do 65 | allow(PuppetX::Chocolatey::ChocolateyInstall).to receive(:install_path).and_return('c:\dude') 66 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_version).and_return(minimum_supported_version) 67 | 68 | provider = provider_class.new(resource) 69 | resource.provider = provider 70 | 71 | # Stub all file and config tests 72 | allow(provider_class).to receive(:healthcheck) 73 | end 74 | 75 | context 'verify provider' do 76 | it 'is an instance of Puppet::Type::Chocolateyfeature::ProviderWindows' do 77 | expect(provider).to be_an_instance_of Puppet::Type::Chocolateyfeature::ProviderWindows 78 | end 79 | 80 | it 'has a enable method' do 81 | expect(provider).to respond_to(:enable) 82 | end 83 | 84 | it 'has an exists? method' do 85 | expect(provider).to respond_to(:exists?) 86 | end 87 | 88 | it 'has a disable method' do 89 | expect(provider).to respond_to(:disable) 90 | end 91 | 92 | it 'has a properties method' do 93 | expect(provider).to respond_to(:properties) 94 | end 95 | 96 | it 'has a query method' do 97 | expect(provider).to respond_to(:query) 98 | end 99 | 100 | it 'has the standard feautures method' do 101 | expect(provider).to respond_to(:features) 102 | end 103 | 104 | it 'returns nil feature when element is nil' do 105 | expect(provider.features).to eq([]) 106 | end 107 | end 108 | 109 | context 'self.read_choco_features' do 110 | before(:each) do 111 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:set_env_chocolateyinstall) 112 | end 113 | 114 | it 'errors when the config file location is null' do 115 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_config_file).and_return(nil) 116 | 117 | expect { 118 | provider_class.read_choco_features 119 | }.to raise_error(Puppet::ResourceError, %r{Config file not found for Chocolatey}) 120 | end 121 | 122 | it 'errors when the config file is not found' do 123 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_config_file).and_return(choco_config) 124 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with(choco_config).and_return(false) 125 | 126 | expect { 127 | provider_class.read_choco_features 128 | }.to raise_error(Puppet::ResourceError, %r{was unable to locate config file at}) 129 | end 130 | 131 | context 'when getting features from the config file' do 132 | features = [] 133 | 134 | before :each do 135 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_config_file).and_return(choco_config) 136 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with(choco_config).and_return(true) 137 | allow(File).to receive(:read).with(choco_config).and_return(choco_config_contents) 138 | 139 | features = provider_class.read_choco_features 140 | end 141 | 142 | it 'matches the count of features in the config' do 143 | expect(features.count).to eq(14) 144 | end 145 | 146 | it 'contains xml elements' do 147 | expect(features[0]).to be_an_instance_of(REXML::Element) 148 | end 149 | end 150 | end 151 | 152 | context 'self.get_choco_feature' do 153 | let(:element) { REXML::Element.new('feature') } 154 | 155 | element_name = 'default' 156 | element_enabled = 'true' 157 | 158 | before :each do 159 | element.add_attributes('name' => element_name, 'enabled' => element_enabled) 160 | end 161 | 162 | it 'returns nil feature when element is nil' do 163 | expect(provider_class.get_choco_feature(nil)).to eq({}) 164 | end 165 | 166 | it 'converts an element to a feature' do 167 | feature = provider_class.get_choco_feature(element) 168 | 169 | expect(feature[:name]).to eq(element_name) 170 | expect(feature[:ensure]).to eq(:enabled) 171 | end 172 | 173 | it 'when feature is disabled' do 174 | element.delete_attribute('enabled') 175 | element.add_attribute('enabled', 'false') 176 | 177 | feature = provider_class.get_choco_feature(element) 178 | expect(feature[:ensure]).to eq(:disabled) 179 | end 180 | end 181 | end 182 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/chocolateyconfig_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/type/chocolateyconfig' 5 | 6 | describe Puppet::Type.type(:chocolateyconfig) do 7 | let(:resource) { Puppet::Type.type(:chocolateyconfig).new(name: 'config', ensure: :absent) } 8 | let(:provider) { Puppet::Provider.new(resource) } 9 | let(:catalog) { Puppet::Resource::Catalog.new } 10 | let(:minimum_supported_version) { '0.9.10.0' } 11 | 12 | before :each do 13 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_version).and_return(minimum_supported_version) 14 | 15 | resource.provider = provider 16 | end 17 | 18 | it 'is an instance of Puppet::Type::Chocolateyconfig' do 19 | expect(resource).to be_an_instance_of(Puppet::Type::Chocolateyconfig) 20 | end 21 | 22 | it 'parameter :name should be the name var' do 23 | expect(resource.parameters[:name]).to be_isnamevar 24 | end 25 | 26 | # string values 27 | ['name', 'value'].each do |param| 28 | context "parameter :#{param}" do 29 | let(:param_symbol) { param.to_sym } 30 | 31 | it 'does not allow nil' do 32 | expect { 33 | resource[param_symbol] = nil 34 | }.to raise_error(Puppet::Error, %r{Got nil value for #{param}}) 35 | end 36 | 37 | it 'does not allow empty' do 38 | expect { 39 | resource[param_symbol] = '' 40 | }.to raise_error(Puppet::Error, %r{A non-empty #{param} must}) 41 | end 42 | 43 | it 'accepts any string value' do 44 | resource[param_symbol] = 'value' 45 | resource[param_symbol] = 'c:/thisstring-location/value/somefile.txt' 46 | resource[param_symbol] = 'c:\\thisstring-location\\value\\somefile.txt' 47 | end 48 | end 49 | end 50 | 51 | context 'param :ensure' do 52 | it "accepts 'present'" do 53 | resource[:ensure] = 'present' 54 | end 55 | 56 | it 'accepts present' do 57 | resource[:ensure] = :present 58 | end 59 | 60 | it 'accepts absent' do 61 | resource[:ensure] = :absent 62 | end 63 | 64 | it 'rejects any other value' do 65 | expect { 66 | resource[:ensure] = :whenever 67 | }.to raise_error(Puppet::Error, %r{Invalid value :whenever. Valid values are}) 68 | end 69 | end 70 | 71 | it 'autorequires Exec[install_chocolatey_official] when in the catalog' do 72 | exec = Puppet::Type.type(:exec).new(name: 'install_chocolatey_official', path: 'nope') 73 | catalog.add_resource resource 74 | catalog.add_resource exec 75 | 76 | reqs = resource.autorequire 77 | expect(reqs.count).to eq(1) 78 | expect(reqs[0].source).to eq(exec) 79 | expect(reqs[0].target).to eq(resource) 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/chocolateyfeature_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/type/chocolateyfeature' 5 | 6 | describe Puppet::Type.type(:chocolateyfeature) do 7 | let(:resource) { Puppet::Type.type(:chocolateyfeature).new(name: 'chocolateyfeature', ensure: 'enabled') } 8 | let(:provider) { Puppet::Provider.new(resource) } 9 | let(:catalog) { Puppet::Resource::Catalog.new } 10 | let(:minimum_supported_version) { '0.9.9.0' } 11 | 12 | before :each do 13 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_version).and_return(minimum_supported_version) 14 | 15 | resource.provider = provider 16 | resource[:ensure] = 'enabled' 17 | end 18 | 19 | it 'is an instance of Puppet::Type::Chocolateyfeature' do 20 | expect(resource).to be_an_instance_of(Puppet::Type::Chocolateyfeature) 21 | end 22 | 23 | it 'parameter :name should be the name var' do 24 | expect(resource.parameters[:name]).to be_isnamevar 25 | end 26 | 27 | context 'parameter :name' do 28 | let(:param_symbol) { :name } 29 | 30 | it 'accepts any string value' do 31 | resource[param_symbol] = 'value' 32 | resource[param_symbol] = 'c:/thisstring-location/value/somefile.txt' 33 | resource[param_symbol] = 'c:\\thisstring-location\\value\\somefile.txt' 34 | end 35 | end 36 | 37 | context 'param :ensure' do 38 | it "accepts 'enabled'" do 39 | resource[:ensure] = 'enabled' 40 | end 41 | 42 | it 'accepts enabled' do 43 | resource[:ensure] = :enabled 44 | end 45 | 46 | it "accepts 'disabled'" do 47 | resource[:ensure] = 'disabled' 48 | end 49 | 50 | it 'accepts :disabled' do 51 | resource[:ensure] = :disabled 52 | end 53 | 54 | it 'rejects any other value' do 55 | expect { 56 | resource[:ensure] = :whenever 57 | }.to raise_error(Puppet::Error, %r{Invalid value :whenever. Valid values are}) 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/chocolateysource_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/type/chocolateysource' 5 | 6 | describe Puppet::Type.type(:chocolateysource) do 7 | let(:resource) { Puppet::Type.type(:chocolateysource).new(name: 'source', location: 'c:\packages') } 8 | let(:provider) { Puppet::Provider.new(resource) } 9 | let(:catalog) { Puppet::Resource::Catalog.new } 10 | let(:minimum_supported_version) { '0.9.9.0' } 11 | 12 | before :each do 13 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:choco_version).and_return(minimum_supported_version) 14 | 15 | resource.provider = provider 16 | end 17 | 18 | it 'is an instance of Puppet::Type::Chocolateysource' do 19 | expect(resource).to be_an_instance_of(Puppet::Type::Chocolateysource) 20 | end 21 | 22 | it 'parameter :name should be the name var' do 23 | expect(resource.parameters[:name]).to be_isnamevar 24 | end 25 | 26 | # string values 27 | ['name', 'location', 'user', 'password'].each do |param| 28 | context "parameter :#{param}" do 29 | let(:param_symbol) { param.to_sym } 30 | 31 | it 'accepts any string value' do 32 | resource[param_symbol] = 'value' 33 | resource[param_symbol] = 'c:/thisstring-location/value/somefile.txt' 34 | resource[param_symbol] = 'c:\\thisstring-location\\value\\somefile.txt' 35 | end 36 | end 37 | end 38 | 39 | # boolean values 40 | ['bypass_proxy', 'admin_only', 'allow_self_service'].each do |param| 41 | context "parameter :#{param}" do 42 | let(:param_symbol) { param.to_sym } 43 | 44 | it 'accepts any valid boolean value' do 45 | resource[param_symbol] = true 46 | resource[param_symbol] = 'true' 47 | resource[param_symbol] = false 48 | resource[param_symbol] = 'false' 49 | end 50 | end 51 | end 52 | 53 | # numeric values 54 | ['priority'].each do |param| 55 | context "parameter :#{param}" do 56 | let(:param_symbol) { param.to_sym } 57 | 58 | it 'accepts any numeric value' do 59 | resource[param_symbol] = 0 60 | resource[param_symbol] = 10 61 | end 62 | 63 | it 'accepts any string that represents a numeric value' do 64 | resource[param_symbol] = '1' 65 | resource[param_symbol] = '0' 66 | end 67 | 68 | it 'does not accept other string values' do 69 | expect { 70 | resource[param_symbol] = 'value' 71 | }.to raise_error(Puppet::Error, %r{An integer is necessary for #{param}}) 72 | end 73 | 74 | it 'does not accept symbol values' do 75 | expect { 76 | resource[param_symbol] = :whenever 77 | }.to raise_error(Puppet::Error, %r{An integer is necessary for #{param}}) 78 | end 79 | end 80 | end 81 | 82 | context 'param :ensure' do 83 | it "accepts 'present'" do 84 | resource[:ensure] = 'present' 85 | end 86 | 87 | it 'accepts present' do 88 | resource[:ensure] = :present 89 | end 90 | 91 | it 'accepts :disabled' do 92 | resource[:ensure] = :disabled 93 | end 94 | 95 | it 'accepts absent' do 96 | resource[:ensure] = :absent 97 | end 98 | 99 | it 'rejects any other value' do 100 | expect { 101 | resource[:ensure] = :whenever 102 | }.to raise_error(Puppet::Error, %r{Invalid value :whenever. Valid values are}) 103 | end 104 | end 105 | 106 | it 'autorequires Exec[install_chocolatey_official] when in the catalog' do 107 | exec = Puppet::Type.type(:exec).new(name: 'install_chocolatey_official', path: 'nope') 108 | catalog.add_resource resource 109 | catalog.add_resource exec 110 | 111 | reqs = resource.autorequire 112 | expect(reqs.count).to eq(1) 113 | expect(reqs[0].source).to eq(exec) 114 | expect(reqs[0].target).to eq(resource) 115 | end 116 | 117 | describe '.validate' do 118 | it 'passes when both user/password are empty' do 119 | resource.validate 120 | end 121 | 122 | it 'passes when both user/password have a value' do 123 | resource[:user] = 'tim' 124 | resource[:password] = 'tim' 125 | 126 | resource.validate 127 | end 128 | 129 | it 'fails when user has a value but password does not' do 130 | resource[:user] = 'tim' 131 | 132 | expect { 133 | resource.validate 134 | }.to raise_error(ArgumentError, %r{you must specify both values}) 135 | end 136 | 137 | it 'fails when password has a value but user does not' do 138 | resource[:password] = 'tim' 139 | 140 | expect { 141 | resource.validate 142 | }.to raise_error(ArgumentError, %r{you must specify both values}) 143 | end 144 | end 145 | end 146 | -------------------------------------------------------------------------------- /spec/unit/puppet_x/chocolatey/chocolatey_common_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet_x/chocolatey/chocolatey_install' 5 | require 'puppet_x/chocolatey/chocolatey_common' 6 | 7 | describe 'Chocolatey Common' do 8 | let(:first_compiled_choco_version) { '0.9.9.0' } 9 | let(:newer_choco_version) { '0.9.10.0' } 10 | let(:last_posh_choco_version) { '0.9.8.33' } 11 | 12 | before :each do 13 | allow(PuppetX::Chocolatey::ChocolateyCommon).to receive(:set_env_chocolateyinstall) 14 | end 15 | 16 | describe '.chocolatey_command' do 17 | before :each do 18 | skip 'Not on Windows platform' unless Puppet.features.microsoft_windows? 19 | end 20 | 21 | it 'finds chocolatey install location based on PuppetX::Chocolatey::ChocolateyInstall' do 22 | expect(Facter).to receive(:value).with('choco_install_path').and_return('c:\dude') 23 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('c:\dude\choco.exe').and_return(true) 24 | 25 | PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command.should == 'c:\dude\choco.exe' 26 | end 27 | 28 | it 'finds chocolatey install location based on default location' do 29 | expect(Facter).to receive(:value).with('choco_install_path').and_return('c:\dude') 30 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('c:\dude\choco.exe').and_return(false) 31 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('C:\ProgramData\chocolatey\choco.exe').and_return(false) 32 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('C:\Chocolatey\choco.exe').and_return(false) 33 | 34 | PuppetX::Chocolatey::ChocolateyCommon.chocolatey_command.should == "#{ENV.fetch('ALLUSERSPROFILE', nil)}\\chocolatey\\choco.exe" 35 | end 36 | end 37 | 38 | describe '.choco_version' do 39 | it 'returns PuppetX::Chocolatey::ChocolateyVersion.version' do 40 | expected = '0.9.9.0.1' 41 | expect(Facter).to receive(:value).with('chocolateyversion').and_return(expected) 42 | PuppetX::Chocolatey::ChocolateyCommon.clear_cached_values 43 | 44 | expect(PuppetX::Chocolatey::ChocolateyCommon.choco_version).to eq expected 45 | end 46 | end 47 | 48 | describe '.choco_config_file' do 49 | let(:choco_install_loc) { 'c:\dude' } 50 | 51 | before(:each) do 52 | expected_version = '0.9.9.0.1' 53 | expect(Facter).to receive(:value).with('choco_install_path').and_return(choco_install_loc).once 54 | expect(Facter).to receive(:value).with('chocolateyversion').and_return(expected_version).once 55 | end 56 | 57 | it 'returns the normal config file location when found' do 58 | expected = 'c:\dude\config\chocolatey.config' 59 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with(expected).and_return(true) 60 | 61 | expect(PuppetX::Chocolatey::ChocolateyCommon.choco_config_file).to eq expected 62 | end 63 | 64 | it 'returns the old config file location for older installs' do 65 | expected = 'c:\dude\chocolateyinstall\chocolatey.config' 66 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('c:\dude\config\chocolatey.config').and_return(false) 67 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with(expected).and_return(true) 68 | 69 | expect(PuppetX::Chocolatey::ChocolateyCommon.choco_config_file).to eq expected 70 | end 71 | 72 | it 'returns nil when the config cannot be found' do 73 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('c:\dude\config\chocolatey.config').and_return(false) 74 | expect(PuppetX::Chocolatey::ChocolateyCommon).to receive(:file_exists?).with('c:\dude\chocolateyinstall\chocolatey.config').and_return(false) 75 | 76 | expect(PuppetX::Chocolatey::ChocolateyCommon.choco_config_file).to be_nil 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /spec/unit/puppet_x/chocolatey/chocolatey_install_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # rubocop:disable RSpec/AnyInstance 4 | 5 | require 'spec_helper' 6 | require 'puppet_x/chocolatey/chocolatey_install' 7 | 8 | describe 'Chocolatey Install Location' do 9 | context 'using normal install path' do 10 | after :each do 11 | # setting the values back 12 | ENV['ChocolateyInstall'] = 'c:\blah' 13 | end 14 | 15 | context 'on Windows' do 16 | before :each do 17 | skip 'Not on Windows platform' unless Puppet::Util::Platform.windows? 18 | end 19 | 20 | it 'returns install path from registry if it exists' do 21 | expected_value = 'C:\somewhere' 22 | allow_any_instance_of(Win32::Registry).to receive(:[]).with('ChocolateyInstall').and_return(expected_value) 23 | 24 | expect(PuppetX::Chocolatey::ChocolateyInstall.install_path).to eq expected_value 25 | end 26 | 27 | it 'returns the environment variable ChocolateyInstall if it exists' do 28 | allow_any_instance_of(Win32::Registry).to receive(:[]).with('ChocolateyInstall').and_raise(Win32::Registry::Error.new(2), 'file not found yo') 29 | 30 | # this is a placeholder, it is already set in spec_helper 31 | ENV['ChocolateyInstall'] = 'c:\blah' 32 | 33 | expect(PuppetX::Chocolatey::ChocolateyInstall.install_path).to eq 'c:\blah' 34 | end 35 | 36 | it 'returns nil if the environment variable does not exist' do 37 | allow_any_instance_of(Win32::Registry).to receive(:[]).with('ChocolateyInstall').and_raise(Win32::Registry::Error.new(2), 'file not found yo') 38 | ENV['ChocolateyInstall'] = nil 39 | 40 | expect(PuppetX::Chocolatey::ChocolateyInstall.install_path).to be_nil 41 | end 42 | end 43 | 44 | context 'on Linux' do 45 | before :each do 46 | skip 'Not on Linux platform' unless Puppet.features.posix? 47 | end 48 | 49 | it 'returns the environment variable ChocolateyInstall if it exists' do 50 | # this is a placeholder, it is already set in spec_helper 51 | ENV['ChocolateyInstall'] = 'c:\blah' 52 | 53 | expect(PuppetX::Chocolatey::ChocolateyInstall.install_path).to eq 'c:\blah' 54 | end 55 | 56 | it 'returns nil if the ChocolateyInstall variable does not exist' do 57 | ENV['ChocolateyInstall'] = nil 58 | 59 | expect(PuppetX::Chocolatey::ChocolateyInstall.install_path).to be_nil 60 | end 61 | end 62 | end 63 | 64 | context 'using temp directory' do 65 | before :each do 66 | skip 'Not on Windows platform' unless Puppet::Util::Platform.windows? 67 | end 68 | 69 | it 'returns the TEMP path from registry if it exists' do 70 | expected_value = 'C:\somewhere' 71 | allow_any_instance_of(Win32::Registry).to receive(:[]).with('TEMP').and_return(expected_value) 72 | 73 | expect(PuppetX::Chocolatey::ChocolateyInstall.temp_dir).to eq expected_value 74 | end 75 | 76 | it 'returns nil path from registry if it does not exist' do 77 | allow_any_instance_of(Win32::Registry).to receive(:[]).with('TEMP').and_raise(Win32::Registry::Error.new(2), 'file not found yo') 78 | 79 | expect(PuppetX::Chocolatey::ChocolateyInstall.temp_dir).to be_nil 80 | end 81 | end 82 | end 83 | 84 | # rubocop:enable RSpec/AnyInstance 85 | -------------------------------------------------------------------------------- /spec/unit/puppet_x/chocolatey/chocolatey_version_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet_x/chocolatey/chocolatey_version' 5 | 6 | describe 'Chocolatey Version' do 7 | context 'on Windows' do 8 | before :each do 9 | skip 'Not on Windows platform' unless Puppet::Util::Platform.windows? 10 | end 11 | 12 | context 'when Chocolatey is installed' do 13 | let(:expected_value) { '1.2.3' } 14 | 15 | before :each do 16 | expect(Facter).to receive(:value).with('choco_install_path').and_return('c:\dude') 17 | allow(File).to receive(:exist?).with('c:\dude\choco.exe').and_return(true) 18 | end 19 | 20 | it 'returns the value from running choco -v' do 21 | expect(Puppet::Util::Execution).to receive(:execute).and_return(expected_value) 22 | 23 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to eq expected_value 24 | end 25 | 26 | it 'handles cleaning up spaces' do 27 | expect(Puppet::Util::Execution).to receive(:execute).and_return(' ' + expected_value + ' ') 28 | 29 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to eq expected_value 30 | end 31 | 32 | it 'handles older versions of choco' do 33 | expect(Puppet::Util::Execution).to receive(:execute).and_return('Please run chocolatey /? or chocolatey help - chocolatey v' + expected_value) 34 | 35 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to eq expected_value 36 | end 37 | 38 | it 'handles other messages that return with version call' do 39 | expect(Puppet::Util::Execution).to receive(:execute).and_return("Error setting some value.\nPlease set this value yourself\r\nsound good?\r" + expected_value) 40 | 41 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to eq expected_value 42 | end 43 | 44 | it 'handles a trailing line break' do 45 | expect(Puppet::Util::Execution).to receive(:execute).and_return(expected_value + "\r\n") 46 | 47 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to eq expected_value 48 | end 49 | 50 | it 'handles 0.9.8.33 of choco' do 51 | expect(Puppet::Util::Execution).to receive(:execute).and_return('!!ATTENTION!! 52 | The next version of Chocolatey (v0.9.9) will require -y to perform 53 | behaviors that change state without prompting for confirmation. Start 54 | using it now in your automated scripts. 55 | 56 | For details on the all new Chocolatey, visit http://bit.ly/new_choco 57 | Please run chocolatey /? or chocolatey help - chocolatey v' + expected_value) 58 | 59 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to eq expected_value 60 | end 61 | end 62 | 63 | context 'When Chocolatey is not installed' do 64 | it 'returns nil' do 65 | expect(Facter).to receive(:value).with('choco_install_path').and_return(nil) 66 | expect(PuppetX::Chocolatey::ChocolateyInstall).to receive(:install_path).and_return(nil) 67 | expect(File).to receive(:exist?).with('\choco.exe').and_return(false) 68 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to be_nil 69 | end 70 | end 71 | end 72 | 73 | context 'on Linux' do 74 | it 'returns nil on a non-windows system' do 75 | skip 'Not on Linux platform' unless Puppet.features.posix? 76 | expect(PuppetX::Chocolatey::ChocolateyVersion.version).to be_nil 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /tasks/init.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Manage a package", 3 | "files": [ 4 | "ruby_task_helper/files/task_helper.rb" 5 | ], 6 | "input_method": "stdin", 7 | "parameters": { 8 | "action": { 9 | "description": "Action to perform", 10 | "type": "Enum[install,upgrade,uninstall]" 11 | }, 12 | "package": { 13 | "description": "Package to manipulate", 14 | "type": "String[1]" 15 | }, 16 | "version": { 17 | "description": "Use a specific version", 18 | "type": "Optional[String[1]]" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tasks/init.rb: -------------------------------------------------------------------------------- 1 | #!/opt/puppetlabs/puppet/bin/ruby 2 | # frozen_string_literal: true 3 | 4 | require 'open3' 5 | require 'puppet' 6 | 7 | require_relative '../../ruby_task_helper/files/task_helper' 8 | 9 | # Manage installing, upgrading and uninstalling packages 10 | class ChocolateyTask < TaskHelper 11 | def task(action: nil, package: nil, version: nil) 12 | command = [ 13 | 'choco', 14 | action, 15 | package, 16 | '--yes', 17 | '--no-color', 18 | '--no-progress', 19 | ] 20 | 21 | if version 22 | command += [ 23 | '--version', 24 | version, 25 | ] 26 | end 27 | 28 | output, status = Open3.capture2(*command) 29 | 30 | raise TaskHelper::Error.new('choco did not exited normally', "chocolatey/#{action}-error", output) unless status.exited? 31 | raise TaskHelper::Error.new("choco exited with error code #{status.exitstatus}", "chocolatey/#{action}-error", output) if status != 0 32 | 33 | nil 34 | end 35 | end 36 | 37 | ChocolateyTask.run if __FILE__ == $PROGRAM_NAME 38 | -------------------------------------------------------------------------------- /tasks/outdated.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "List outdated packages", 3 | "files": [ 4 | "ruby_task_helper/files/task_helper.rb" 5 | ], 6 | "input_method": "stdin" 7 | } 8 | -------------------------------------------------------------------------------- /tasks/outdated.rb: -------------------------------------------------------------------------------- 1 | #!/opt/puppetlabs/puppet/bin/ruby 2 | # frozen_string_literal: true 3 | 4 | require 'open3' 5 | require 'puppet' 6 | 7 | require_relative '../../ruby_task_helper/files/task_helper' 8 | 9 | # Retun a list of packages with pending updates 10 | class ChocolateyOutdatedTask < TaskHelper 11 | def task(**_kwargs) 12 | output, status = Open3.capture2('choco', 'outdated', '--no-color', '--limit-output') 13 | 14 | raise TaskHelper::Error.new('choco did not exited normally', 'chocolatey/outdated-error', output) unless status.exited? 15 | raise TaskHelper::Error.new("choco exited with error code #{status.exitstatus}", 'chocolatey/outdated-error', output) unless [0, 2].include?(status.exitstatus) 16 | 17 | result = [] 18 | output.split("\n").each do |line| 19 | parts = line.split('|') 20 | 21 | next unless parts.count == 4 22 | 23 | result << { 24 | package: parts[0], 25 | version: parts[1], 26 | available_version: parts[2], 27 | pinned: parts[3] == 'true' 28 | } 29 | end 30 | 31 | { status: result } 32 | end 33 | end 34 | 35 | ChocolateyOutdatedTask.run if __FILE__ == $PROGRAM_NAME 36 | -------------------------------------------------------------------------------- /tasks/pin.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Manage package pinning", 3 | "files": [ 4 | "ruby_task_helper/files/task_helper.rb" 5 | ], 6 | "input_method": "stdin", 7 | "parameters": { 8 | "action": { 9 | "description": "Action to perform", 10 | "type": "Enum[list,add,remove]" 11 | }, 12 | "package": { 13 | "description": "Package to manipulate", 14 | "type": "Optional[String[1]]" 15 | }, 16 | "version": { 17 | "description": "Use a specific version", 18 | "type": "Optional[String[1]]" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tasks/pin.rb: -------------------------------------------------------------------------------- 1 | #!/opt/puppetlabs/puppet/bin/ruby 2 | # frozen_string_literal: true 3 | 4 | require 'open3' 5 | require 'puppet' 6 | 7 | require_relative '../../ruby_task_helper/files/task_helper' 8 | 9 | # Manage installing, upgrading and uninstalling packages 10 | class ChocolateyPinTask < TaskHelper 11 | def task(action: nil, package: nil, version: nil) 12 | command = [ 13 | 'choco', 14 | 'pin', 15 | action, 16 | '--no-color', 17 | '--limit-output', 18 | ] 19 | 20 | if package 21 | command += [ 22 | '--name', 23 | package, 24 | ] 25 | end 26 | 27 | if version 28 | command += [ 29 | '--version', 30 | version, 31 | ] 32 | end 33 | 34 | output, status = Open3.capture2(*command) 35 | 36 | raise TaskHelper::Error.new('choco did not exited normally', "chocolatey/pin-#{action}-error", output) unless status.exited? 37 | raise TaskHelper::Error.new("choco exited with error code #{status.exitstatus}", "chocolatey/pin-#{action}-error", output) if status != 0 38 | 39 | if action == 'list' 40 | result = [] 41 | 42 | output.split("\n").each do |line| 43 | parts = line.split('|') 44 | 45 | next unless parts.count == 2 46 | 47 | result << { 48 | package: parts[0], 49 | version: parts[1] 50 | } 51 | end 52 | 53 | return { status: result } 54 | end 55 | 56 | nil 57 | end 58 | end 59 | 60 | ChocolateyPinTask.run if __FILE__ == $PROGRAM_NAME 61 | -------------------------------------------------------------------------------- /tasks/status.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "List currently installed packages", 3 | "files": [ 4 | "ruby_task_helper/files/task_helper.rb" 5 | ], 6 | "input_method": "stdin" 7 | } 8 | -------------------------------------------------------------------------------- /tasks/status.rb: -------------------------------------------------------------------------------- 1 | #!/opt/puppetlabs/puppet/bin/ruby 2 | # frozen_string_literal: true 3 | 4 | require_relative '../../ruby_task_helper/files/task_helper' 5 | 6 | require 'open3' 7 | require 'puppet' 8 | 9 | # Return a list of installed packages 10 | class ChocolateyStatusTask < TaskHelper 11 | def task(**_kwargs) 12 | output, status = Open3.capture2('choco', 'list', '--local-only', '--no-color', '--limit-output') 13 | 14 | raise TaskHelper::Error.new('choco did not exited normally', 'chocolatey/status-error', output) unless status.exited? 15 | raise TaskHelper::Error.new("choco exited with error code #{status.exitstatus}", 'chocolatey/status-error', output) if status != 0 16 | 17 | result = [] 18 | 19 | output.split("\n").each do |line| 20 | parts = line.split('|') 21 | 22 | next unless parts.count == 2 23 | 24 | result << { 25 | package: parts[0], 26 | version: parts[1] 27 | } 28 | end 29 | 30 | { status: result } 31 | end 32 | end 33 | 34 | ChocolateyStatusTask.run if __FILE__ == $PROGRAM_NAME 35 | -------------------------------------------------------------------------------- /templates/InstallChocolatey.ps1.epp: -------------------------------------------------------------------------------- 1 | # ============================================================================== 2 | # Copyright 2011 - Present RealDimensions Software, LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use 5 | # this file except in compliance with the License. You may obtain a copy of the 6 | # License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software distributed 11 | # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 12 | # CONDITIONS OF ANY KIND, either express or implied. See the License for the 13 | # specific language governing permissions and limitations under the License. 14 | # ============================================================================== 15 | 16 | $ErrorActionPreference = 'Stop' 17 | 18 | # For some reason try/catch wrapping only ensures 19 | # that none of this script runs at all 20 | # https://tickets.puppetlabs.com/browse/MODULES-2634 21 | #try { 22 | 23 | # variables 24 | $url = '<%= $download_url %>' 25 | $unzipMethod = '<%= $unzip_type %>' 26 | $install_proxy = <%= $_install_proxy %> 27 | $7zaExe = '<%= $seven_zip_exe %>' 28 | if ($env:TEMP -eq $null) { 29 | $env:TEMP = Join-Path $env:SystemDrive 'temp' 30 | } 31 | $chocTempDir = Join-Path $env:TEMP "chocolatey" 32 | $tempDir = Join-Path $chocTempDir "chocInstall" 33 | if (![System.IO.Directory]::Exists($tempDir)) {[System.IO.Directory]::CreateDirectory($tempDir)} 34 | $file = Join-Path $tempDir "chocolatey.zip" 35 | $chocErrorLog = Join-Path $tempDir "chocError.log" 36 | 37 | # PowerShell v2/3 caches the output stream. Then it throws errors due 38 | # to the FileStream not being what is expected. Fixes "The OS handle's 39 | # position is not what FileStream expected. Do not use a handle 40 | # simultaneously in one FileStream and in Win32 code or another 41 | # FileStream." 42 | 43 | # This only works with the ConsoleHost (PowerShell InternalHost) 44 | function Fix-PowerShellOutputRedirectionBug { 45 | try{ 46 | # http://www.leeholmes.com/blog/2008/07/30/workaround-the-os-handles-position-is-not-what-filestream-expected/ plus comments 47 | $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField" 48 | $objectRef = $host.GetType().GetField("externalHostRef", $bindingFlags).GetValue($host) 49 | $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetProperty" 50 | $consoleHost = $objectRef.GetType().GetProperty("Value", $bindingFlags).GetValue($objectRef, @()) 51 | [void] $consoleHost.GetType().GetProperty("IsStandardOutputRedirected", $bindingFlags).GetValue($consoleHost, @()) 52 | $bindingFlags = [Reflection.BindingFlags] "Instance,NonPublic,GetField" 53 | $field = $consoleHost.GetType().GetField("standardOutputWriter", $bindingFlags) 54 | $field.SetValue($consoleHost, [Console]::Out) 55 | [void] $consoleHost.GetType().GetProperty("IsStandardErrorRedirected", $bindingFlags).GetValue($consoleHost, @()) 56 | $field2 = $consoleHost.GetType().GetField("standardErrorWriter", $bindingFlags) 57 | $field2.SetValue($consoleHost, [Console]::Error) 58 | } catch { 59 | Write-Output "Unable to apply redirection fix. Error: $_" 60 | } 61 | } 62 | 63 | Fix-PowerShellOutputRedirectionBug 64 | 65 | # This should help when certain organizations have issues installing Chocolatey 66 | # Attempt to set highest encryption available for SecurityProtocol. 67 | # PowerShell will not set this by default (until maybe .NET 4.6.x). This 68 | # will typically produce a message for PowerShell v2 (just an info 69 | # message though) 70 | try { 71 | # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) 72 | # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 will not 73 | # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is 74 | # installed (.NET 4.5 is an in-place upgrade). 75 | [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 76 | } catch { 77 | Write-Output "Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to do one or more of the following: (1) upgrade to .NET Framework 4.5 and PowerShell v3 and/or (2) specify internal Chocolatey package location (see https://forge.puppet.com/puppetlabs/chocolatey#manage-chocolatey-installation)." 78 | } 79 | 80 | function Download-File { 81 | param ( 82 | [string]$url, 83 | [string]$file 84 | ) 85 | Write-Output "Downloading $url to $file" 86 | $downloader = new-object System.Net.WebClient 87 | 88 | if ($install_proxy) { 89 | [System.Net.WebRequest]::DefaultWebProxy = New-Object System.Net.WebProxy($install_proxy,$true) 90 | } 91 | 92 | $downloader.Proxy.Credentials=[System.Net.CredentialCache]::DefaultNetworkCredentials; 93 | $downloader.DownloadFile($url, $file) 94 | } 95 | 96 | # download the package 97 | Download-File $url $file 98 | 99 | if ($unzipMethod -eq '7zip') { 100 | # unzip the package 101 | Write-Output "Extracting $file to $tempDir..." 102 | Start-Process "$7zaExe" -ArgumentList "x -o`"$tempDir`" -y `"$file`"" -Wait -NoNewWindow 103 | Remove-Item -Path "$7zaExe" -Force 104 | } else { 105 | if ($PSVersionTable.PSVersion.Major -lt 5) { 106 | $shellApplication = new-object -com shell.application 107 | $zipPackage = $shellApplication.NameSpace($file) 108 | $destinationFolder = $shellApplication.NameSpace($tempDir) 109 | $destinationFolder.CopyHere($zipPackage.Items(),0x10) 110 | } else { 111 | Expand-Archive -Path "$file" -DestinationPath "$tempDir" -Force | Out-Null 112 | } 113 | } 114 | 115 | # call chocolatey install 116 | Write-Output "Installing chocolatey on this machine" 117 | $toolsFolder = Join-Path $tempDir "tools" 118 | $chocInstallPS1 = Join-Path $toolsFolder "chocolateyInstall.ps1" 119 | 120 | if ($PSVersionTable.PSVersion.Major -gt 2) { 121 | & $chocInstallPS1 122 | } else { 123 | $output = Invoke-Expression $chocInstallPS1 124 | $output 125 | Write-Output "Any errors that occurred during install or upgrade are logged here: $chocoErrorLog" 126 | $error | out-file $chocErrorLog 127 | } 128 | 129 | Write-Output 'Ensuring chocolatey commands are on the path' 130 | $chocInstallVariableName = "ChocolateyInstall" 131 | $chocoPath = [Environment]::GetEnvironmentVariable($chocInstallVariableName, [System.EnvironmentVariableTarget]::User) 132 | if ($chocoPath -eq $null -or $chocoPath -eq '') { 133 | $chocoPath = 'C:\ProgramData\Chocolatey' 134 | } 135 | 136 | $chocoBinPath = Join-Path $chocoPath 'bin' 137 | 138 | if ($($env:Path).ToLower().Contains($($chocoBinPath).ToLower()) -eq $false) { 139 | $env:Path = [Environment]::GetEnvironmentVariable('Path',[System.EnvironmentVariableTarget]::Machine); 140 | } 141 | 142 | Write-Output 'Ensuring chocolatey.nupkg is in the lib folder' 143 | $chocoPkgDir = Join-Path $chocoPath 'lib\chocolatey' 144 | $nupkg = Join-Path $chocoPkgDir 'chocolatey.nupkg' 145 | if (![System.IO.Directory]::Exists($chocoPkgDir)) { [System.IO.Directory]::CreateDirectory($chocoPkgDir); } 146 | Copy-Item "$file" "$nupkg" -Force -ErrorAction SilentlyContinue 147 | 148 | #} 149 | #catch 150 | #{ 151 | # Write-Host "$($_.Exception.Message)" 152 | # exit 1 153 | #} 154 | --------------------------------------------------------------------------------