├── .adr-dir
├── .dependency_decisions.yml
├── .fixtures.yml
├── .github
├── ISSUE_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── dependabot.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ ├── nightly.yml
│ ├── release.yml
│ ├── release_prep.yml
│ ├── sync_private.yml
│ ├── workflow-restarter-test.yml
│ └── workflow-restarter.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── .rubocop_todo.yml
├── CHANGELOG.md
├── CODEOWNERS
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── NOTICE
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── docs
├── adr
│ └── 0001-restart-pdk-nightly-on-acceptance-test-failures.md
├── customizing_module_config.md
├── pdk-workflow.png
├── pdk.ditamap
├── pdk.md
├── pdk_building_module_packages.md
├── pdk_converting_modules.md
├── pdk_creating_modules.md
├── pdk_install.md
├── pdk_known_issues.md
├── pdk_overview.md
├── pdk_reference.md
├── pdk_release_notes.md
├── pdk_testing.md
├── pdk_troubleshooting.md
├── pdk_updating_modules.md
├── pdk_upgrading.md
└── release_notes_pdk.md
├── exe
└── pdk
├── ext
├── PuppetDevelopmentKit
│ ├── PuppetDevelopmentKit.psd1.erb
│ └── PuppetDevelopmentKit.psm1
├── PuppetDevelopmentKitBeta
│ ├── PuppetDevelopmentKitBeta.psd1.erb
│ └── PuppetDevelopmentKitBeta.psm1.erb
├── build_defaults.yml
└── rubocop.rb
├── lib
├── pdk.rb
└── pdk
│ ├── answer_file.rb
│ ├── bolt.rb
│ ├── cli.rb
│ ├── cli
│ ├── build.rb
│ ├── bundle.rb
│ ├── console.rb
│ ├── convert.rb
│ ├── env.rb
│ ├── errors.rb
│ ├── exec.rb
│ ├── exec
│ │ ├── command.rb
│ │ └── interactive_command.rb
│ ├── exec_group.rb
│ ├── get.rb
│ ├── get
│ │ └── config.rb
│ ├── new.rb
│ ├── new
│ │ ├── class.rb
│ │ ├── defined_type.rb
│ │ ├── fact.rb
│ │ ├── function.rb
│ │ ├── module.rb
│ │ ├── provider.rb
│ │ ├── task.rb
│ │ ├── test.rb
│ │ └── transport.rb
│ ├── release.rb
│ ├── release
│ │ ├── prep.rb
│ │ └── publish.rb
│ ├── remove.rb
│ ├── remove
│ │ └── config.rb
│ ├── set.rb
│ ├── set
│ │ └── config.rb
│ ├── test.rb
│ ├── test
│ │ └── unit.rb
│ ├── update.rb
│ ├── util.rb
│ ├── util
│ │ ├── command_redirector.rb
│ │ ├── interview.rb
│ │ ├── option_normalizer.rb
│ │ ├── option_validator.rb
│ │ ├── spinner.rb
│ │ └── update_manager_printer.rb
│ └── validate.rb
│ ├── config.rb
│ ├── config
│ ├── errors.rb
│ ├── ini_file.rb
│ ├── ini_file_setting.rb
│ ├── json.rb
│ ├── json_schema_namespace.rb
│ ├── json_schema_setting.rb
│ ├── json_with_schema.rb
│ ├── namespace.rb
│ ├── setting.rb
│ ├── task_schema.json
│ ├── validator.rb
│ ├── yaml.rb
│ └── yaml_with_schema.rb
│ ├── context.rb
│ ├── context
│ ├── control_repo.rb
│ ├── module.rb
│ └── none.rb
│ ├── control_repo.rb
│ ├── generate.rb
│ ├── generate
│ ├── defined_type.rb
│ ├── fact.rb
│ ├── function.rb
│ ├── module.rb
│ ├── provider.rb
│ ├── puppet_class.rb
│ ├── puppet_object.rb
│ ├── task.rb
│ └── transport.rb
│ ├── logger.rb
│ ├── module.rb
│ ├── module
│ ├── convert.rb
│ ├── metadata.rb
│ ├── release.rb
│ ├── update.rb
│ └── update_manager.rb
│ ├── report.rb
│ ├── report
│ └── event.rb
│ ├── template.rb
│ ├── template
│ ├── fetcher.rb
│ ├── fetcher
│ │ ├── git.rb
│ │ └── local.rb
│ ├── renderer.rb
│ ├── renderer
│ │ ├── v1.rb
│ │ └── v1
│ │ │ ├── legacy_template_dir.rb
│ │ │ ├── renderer.rb
│ │ │ └── template_file.rb
│ └── template_dir.rb
│ ├── tests
│ └── unit.rb
│ ├── util.rb
│ ├── util
│ ├── bundler.rb
│ ├── changelog_generator.rb
│ ├── env.rb
│ ├── filesystem.rb
│ ├── git.rb
│ ├── json_finder.rb
│ ├── puppet_strings.rb
│ ├── puppet_version.rb
│ ├── ruby_version.rb
│ ├── template_uri.rb
│ ├── vendored_file.rb
│ ├── version.rb
│ ├── windows.rb
│ └── windows
│ │ ├── api_types.rb
│ │ ├── file.rb
│ │ ├── process.rb
│ │ └── string.rb
│ ├── validate.rb
│ ├── validate
│ ├── control_repo
│ │ ├── control_repo_validator_group.rb
│ │ └── environment_conf_validator.rb
│ ├── external_command_validator.rb
│ ├── internal_ruby_validator.rb
│ ├── invokable_validator.rb
│ ├── metadata
│ │ ├── metadata_json_lint_validator.rb
│ │ ├── metadata_syntax_validator.rb
│ │ └── metadata_validator_group.rb
│ ├── puppet
│ │ ├── puppet_epp_validator.rb
│ │ ├── puppet_lint_validator.rb
│ │ ├── puppet_plan_syntax_validator.rb
│ │ ├── puppet_syntax_validator.rb
│ │ └── puppet_validator_group.rb
│ ├── ruby
│ │ ├── ruby_rubocop_validator.rb
│ │ └── ruby_validator_group.rb
│ ├── tasks
│ │ ├── tasks_metadata_lint_validator.rb
│ │ ├── tasks_name_validator.rb
│ │ └── tasks_validator_group.rb
│ ├── validator.rb
│ ├── validator_group.rb
│ └── yaml
│ │ ├── yaml_syntax_validator.rb
│ │ └── yaml_validator_group.rb
│ └── version.rb
├── package-testing
├── .gitignore
├── Gemfile
├── Rakefile
├── config
│ └── options.rb
├── lib
│ ├── helper.rb
│ ├── pdk
│ │ └── pdk_helper.rb
│ └── testenv.rb
└── spec
│ ├── package
│ ├── add_gem_to_module_spec.rb
│ ├── airgapped_usage_spec.rb
│ ├── pdk_configuration_spec.rb
│ ├── pdk_help_spec.rb
│ ├── support
│ │ ├── install_pdk.rb
│ │ ├── serverspec_monkeypatch.rb
│ │ └── spec_utils.rb
│ ├── unit_test_a_new_module_spec.rb
│ ├── unprivileged_user_spec.rb
│ ├── update_module_spec.rb
│ ├── validate_a_new_module_spec.rb
│ └── version_selection_spec.rb
│ └── spec_helper_package.rb
├── pdk.gemspec
├── rakelib
├── command_spec.rake
└── test_pdk_as_library.rake
├── reference
└── validate
│ └── REFERENCE.md
└── spec
├── acceptance
├── build_spec.rb
├── bundle_spec.rb
├── convert_spec.rb
├── get_config_spec.rb
├── get_spec.rb
├── new_class_spec.rb
├── new_defined_type_spec.rb
├── new_fact_spec.rb
├── new_function_spec.rb
├── new_module_spec.rb
├── new_provider_spec.rb
├── new_test_spec.rb
├── new_transport_spec.rb
├── remove_config_spec.rb
├── remove_spec.rb
├── report_spec.rb
├── set_config_spec.rb
├── set_spec.rb
├── support
│ ├── contain_valid_junit_xml.rb
│ ├── have_junit_testcase.rb
│ ├── have_junit_testsuite.rb
│ ├── have_no_output.rb
│ ├── have_xpath.rb
│ ├── in_a_new_module.rb
│ ├── it_requires_running_in_a_module.rb
│ ├── with_a_fake_answer_file.rb
│ └── with_a_fake_tty.rb
├── template_ref_spec.rb
├── test_unit_spec.rb
├── update_spec.rb
├── validate_all_spec.rb
├── validate_metadata_spec.rb
├── validate_puppet_spec.rb
├── validate_ruby_spec.rb
├── validate_tasks_spec.rb
└── version_changer_spec.rb
├── fixtures
├── JUnit.xsd
├── control_repo
│ ├── Puppetfile
│ ├── data
│ │ └── .gitkeep
│ ├── environment.conf
│ └── site
│ │ ├── profile
│ │ └── .gitkeep
│ │ └── role
│ │ └── .gitkeep
├── module_gemfile
├── module_gemfile_lockfile
├── module_root
│ └── lib
│ │ └── facter
│ │ └── .gitkeep
├── pe_versions.json
└── puppet_module
│ ├── manifests
│ └── .gitkeep
│ └── metadata.json
├── spec_helper.rb
├── spec_helper_acceptance.rb
├── support
├── exit_with_status.rb
├── file_based_namespaces.rb
├── hash_with_defaults_including.rb
├── it_accepts_epp_targets.rb
├── it_accepts_metadata_json_targets.rb
├── it_accepts_pp_targets.rb
├── mock_configuration.rb
├── packaged_install.rb
├── run_outside_module.rb
├── valid_in_context.rb
└── validators.rb
└── unit
├── pdk
├── bolt_spec.rb
├── cli
│ ├── build_spec.rb
│ ├── bundle_spec.rb
│ ├── console_spec.rb
│ ├── convert_spec.rb
│ ├── env_spec.rb
│ ├── errors_spec.rb
│ ├── exec
│ │ ├── command_spec.rb
│ │ └── interactive_command_spec.rb
│ ├── exec_spec.rb
│ ├── new
│ │ ├── class_spec.rb
│ │ ├── defined_type_spec.rb
│ │ ├── module_spec.rb
│ │ ├── task_spec.rb
│ │ └── test_spec.rb
│ ├── new_spec.rb
│ ├── release
│ │ ├── prep_spec.rb
│ │ └── publish_spec.rb
│ ├── release_spec.rb
│ ├── remove
│ │ └── config_spec.rb
│ ├── set
│ │ └── config_spec.rb
│ ├── test
│ │ └── unit_spec.rb
│ ├── test_spec.rb
│ ├── update_spec.rb
│ ├── util
│ │ ├── command_redirector_spec.rb
│ │ ├── interview_spec.rb
│ │ ├── option_normalizer_spec.rb
│ │ ├── option_validator_spec.rb
│ │ └── update_manager_printer_spec.rb
│ ├── util_spec.rb
│ └── validate_spec.rb
├── cli_spec.rb
├── config
│ ├── ini_file_setting_spec.rb
│ ├── ini_file_spec.rb
│ ├── json_schema_namespace_spec.rb
│ ├── json_schema_setting_spec.rb
│ ├── json_spec.rb
│ ├── json_with_schema_spec.rb
│ ├── namespace_spec.rb
│ ├── schema_files_spec.rb
│ ├── setting_spec.rb
│ ├── yaml_spec.rb
│ └── yaml_with_schema_spec.rb
├── config_spec.rb
├── context
│ ├── control_repo_spec.rb
│ ├── module_spec.rb
│ └── none_spec.rb
├── context_spec.rb
├── control_repo_spec.rb
├── generate
│ ├── defined_type_spec.rb
│ ├── module_spec.rb
│ ├── provider_spec.rb
│ ├── puppet_class_spec.rb
│ ├── puppet_object_spec.rb
│ ├── task_spec.rb
│ └── transport_spec.rb
├── logger_spec.rb
├── module
│ ├── convert_spec.rb
│ ├── metadata_spec.rb
│ ├── release_spec.rb
│ ├── update_manager_spec.rb
│ └── update_spec.rb
├── report
│ └── event_spec.rb
├── report_spec.rb
├── template
│ ├── fetcher
│ │ ├── git_spec.rb
│ │ └── local_spec.rb
│ ├── fetcher_spec.rb
│ ├── renderer
│ │ ├── v1
│ │ │ ├── renderer_spec.rb
│ │ │ └── template_file_spec.rb
│ │ └── v1_spec.rb
│ ├── renderer_spec.rb
│ └── template_dir_spec.rb
├── template_spec.rb
├── test
│ └── unit_spec.rb
├── util
│ ├── bundler_spec.rb
│ ├── changelog_generator_spec.rb
│ ├── env_spec.rb
│ ├── filesystem_spec.rb
│ ├── git_spec.rb
│ ├── puppet_strings_spec.rb
│ ├── puppet_version_spec.rb
│ ├── ruby_version_spec.rb
│ ├── template_uri_spec.rb
│ ├── vendored_file_spec.rb
│ └── version_spec.rb
├── util_spec.rb
├── validate
│ ├── control_repo
│ │ ├── control_repo_validator_group_spec.rb
│ │ └── environment_conf_validator_spec.rb
│ ├── external_command_validator_spec.rb
│ ├── internal_ruby_validator_spec.rb
│ ├── invokable_validator_spec.rb
│ ├── metadata
│ │ ├── metadata_json_lint_validator_spec.rb
│ │ ├── metadata_syntax_validator_spec.rb
│ │ └── metadata_validator_group_spec.rb
│ ├── puppet
│ │ ├── puppet_epp_validator_spec.rb
│ │ ├── puppet_lint_validator_spec.rb
│ │ ├── puppet_plan_syntax_validator_spec.rb
│ │ ├── puppet_syntax_validator_spec.rb
│ │ └── puppet_validator_group_spec.rb
│ ├── ruby
│ │ ├── ruby_rubocop_validator_spec.rb
│ │ └── ruby_validator_group_spec.rb
│ ├── tasks
│ │ ├── tasks_metadata_lint_validator_spec.rb
│ │ ├── tasks_name_validator_spec.rb
│ │ └── tasks_validator_group_spec.rb
│ ├── validator_group_spec.rb
│ ├── validator_spec.rb
│ └── yaml
│ │ ├── yaml_syntax_validator_spec.rb
│ │ └── yaml_validator_group_spec.rb
├── validate_spec.rb
└── version_spec.rb
└── pdk_spec.rb
/.adr-dir:
--------------------------------------------------------------------------------
1 | docs/adr
2 |
--------------------------------------------------------------------------------
/.fixtures.yml:
--------------------------------------------------------------------------------
1 | fixtures:
2 | repositories:
3 | provision: 'https://github.com/puppetlabs/provision.git'
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | labels: needs-triage
3 | ---
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve the PDK
4 | title: ''
5 | labels: bug, needs-triage
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | The simplest sequence of steps you have discovered which triggers the bug.
15 |
16 | **Expected behavior**
17 | A clear and concise description of what you expected to happen.
18 |
19 | **Additional context**
20 | - Your PDK installation method (native packages or via Rubygems)
21 | - (If via Rubygems, please include your Ruby version `ruby -v`)
22 | - Your PDK version (`pdk --version`)
23 | - Your operating system / platform
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: 'Feature requests should be raised as a ''Feature Request'' Discussion here:
4 | https://github.com/puppetlabs/pdk/discussions/categories/feature-request '
5 | title: ''
6 | labels: ''
7 | assignees: ''
8 |
9 | ---
10 |
11 | **Feature requests should be opened as a ['Feature Request' Discussion](https://github.com/puppetlabs/pdk/discussions/categories/feature-request) in the [Discussions](https://github.com/puppetlabs/pdk/discussions) area.**
12 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # raise PRs for gem updates
4 | - package-ecosystem: bundler
5 | directory: "/"
6 | schedule:
7 | interval: daily
8 | time: "13:00"
9 | open-pull-requests-limit: 10
10 |
11 | # Maintain dependencies for GitHub Actions
12 | - package-ecosystem: github-actions
13 | directory: "/"
14 | schedule:
15 | interval: daily
16 | time: "13:00"
17 | open-pull-requests-limit: 10
18 |
--------------------------------------------------------------------------------
/.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.
16 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: "ci"
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 | pull_request:
8 | branches:
9 | - "main"
10 | workflow_dispatch:
11 |
12 | env:
13 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
14 |
15 | jobs:
16 | spec:
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | ruby_version:
21 | - "3.1"
22 | include:
23 | - puppet_gem_version: "~> 8.0"
24 | ruby_version: "3.1"
25 | name: "spec (ruby ${{ matrix.ruby_version }})"
26 | uses: "puppetlabs/cat-github-actions/.github/workflows/gem_ci.yml@main"
27 | secrets: inherit
28 | with:
29 | rake_task: 'spec:coverage'
30 | ruby_version: ${{ matrix.ruby_version }}
31 | puppet_gem_version: ${{ matrix.puppet_gem_version }}
32 | # This line enables CI shellcheck (reviewdog) to be run on the repository
33 | run_shellcheck: true
34 |
35 | acceptance:
36 | needs: "spec"
37 | strategy:
38 | fail-fast: false
39 | matrix:
40 | os:
41 | - "ubuntu-latest"
42 | - "windows-2019"
43 | ruby_version:
44 | - "3.1"
45 | include:
46 | - puppet_gem_version: "~> 8.0"
47 | ruby_version: "3.1"
48 | name: "acceptance (ruby ${{ matrix.ruby_version }} | ${{ matrix.os }})"
49 | uses: "puppetlabs/cat-github-actions/.github/workflows/gem_acceptance.yml@main"
50 | secrets: inherit
51 | with:
52 | ruby_version: ${{ matrix.ruby_version }}
53 | puppet_version: ${{ matrix.puppet_gem_version }}
54 | rake_task: 'acceptance:local'
55 | runs_on: ${{ matrix.os }}
56 |
--------------------------------------------------------------------------------
/.github/workflows/nightly.yml:
--------------------------------------------------------------------------------
1 | name: "nightly"
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 | workflow_dispatch:
7 |
8 | jobs:
9 |
10 | spec:
11 | strategy:
12 | fail-fast: false
13 | matrix:
14 | ruby_version:
15 | - "3.1"
16 | include:
17 | - puppet_gem_version: "~> 8.0"
18 | ruby_version: "3.1"
19 | name: "spec (ruby ${{ matrix.ruby_version }})"
20 | uses: "puppetlabs/cat-github-actions/.github/workflows/gem_ci.yml@main"
21 | secrets: "inherit"
22 | with:
23 | rake_task: 'spec:coverage'
24 | ruby_version: ${{ matrix.ruby_version }}
25 | puppet_gem_version: ${{ matrix.puppet_gem_version }}
26 |
27 | acceptance:
28 | needs: "spec"
29 | strategy:
30 | fail-fast: false
31 | matrix:
32 | os:
33 | - "ubuntu-latest"
34 | - "windows-2019"
35 | ruby_version:
36 | - "3.1"
37 | include:
38 | - puppet_gem_version: "~> 8.0"
39 | ruby_version: "3.1"
40 | name: "acceptance (ruby ${{ matrix.ruby_version }} | ${{ matrix.os }})"
41 | uses: "puppetlabs/cat-github-actions/.github/workflows/gem_acceptance.yml@main"
42 | secrets: "inherit"
43 | with:
44 | ruby_version: ${{ matrix.ruby_version }}
45 | puppet_version: ${{ matrix.puppet_gem_version }}
46 | rake_task: 'acceptance:local'
47 | runs_on: ${{ matrix.os }}
48 |
49 | on-failure-workflow-restarter-proxy:
50 | # (1) run this job after the "acceptance" job and...
51 | needs: [acceptance]
52 | # (2) continue ONLY IF "acceptance" fails
53 | if: always() && needs.acceptance.result == 'failure'
54 | runs-on: ubuntu-latest
55 | steps:
56 | # (3) checkout this repository in order to "see" the following custom action
57 | - name: Checkout repository
58 | uses: actions/checkout@v4
59 |
60 | # (4) "use" the custom action to retrigger the failed "acceptance job" above
61 | # NOTE: pass the SOURCE_GITHUB_TOKEN to the custom action because (a) it must have
62 | # this to trigger the reusable workflow that restarts the failed job; and
63 | # (b) custom actions do not have access to the calling workflow's secrets
64 | - name: Trigger reusable workflow
65 | uses: "puppetlabs/cat-github-actions/.github/actions/workflow-restarter-proxy@main"
66 | env:
67 | SOURCE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68 | with:
69 | repository: ${{ github.repository }}
70 | run_id: ${{ github.run_id }}
71 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: "release"
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | target:
7 | description: "The target for the release. This can be a commit sha or a branch."
8 | required: false
9 | default: "main"
10 |
11 | jobs:
12 | release:
13 | uses: "puppetlabs/cat-github-actions/.github/workflows/gem_release.yml@main"
14 | with:
15 | target: "${{ github.event.inputs.target }}"
16 | secrets: "inherit"
17 |
--------------------------------------------------------------------------------
/.github/workflows/release_prep.yml:
--------------------------------------------------------------------------------
1 | name: "release prep"
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | target:
7 | description: "The target for the release. This can be a commit sha or a branch."
8 | required: false
9 | default: "main"
10 | version:
11 | description: "Version of gem to be released."
12 | required: true
13 |
14 | jobs:
15 | release_prep:
16 | uses: "puppetlabs/cat-github-actions/.github/workflows/gem_release_prep.yml@main"
17 | with:
18 | target: "${{ github.event.inputs.target }}"
19 | version: "${{ github.events.inputs.version }}"
20 | secrets: "inherit"
21 |
--------------------------------------------------------------------------------
/.github/workflows/workflow-restarter-test.yml:
--------------------------------------------------------------------------------
1 | name: Workflow Restarter TEST
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | fail:
7 | description: >
8 | For (acceptance, unit) jobs:
9 | 'true' = (fail, succeed) and
10 | 'false' = (succeed, fail)
11 | required: true
12 | default: 'true'
13 | env:
14 | SOURCE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15 |
16 | jobs:
17 | unit:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Check outcome
21 | run: |
22 | if [ "${{ github.event.inputs.fail }}" = "true" ]; then
23 | echo "'unit' job succeeded"
24 | exit 0
25 | else
26 | echo "'unit' job failed"
27 | exit 1
28 | fi
29 | acceptance:
30 | runs-on: ubuntu-latest
31 | steps:
32 | - name: Check outcome
33 | run: |
34 | if [ "${{ github.event.inputs.fail }}" = "true" ]; then
35 | echo "'acceptance' job failed"
36 | exit 1
37 | else
38 | echo "'acceptance' job succeeded"
39 | exit 0
40 | fi
41 |
42 | on-failure-workflow-restarter-proxy:
43 | # (1) run this job after the "acceptance" job and...
44 | needs: [acceptance, unit]
45 | # (2) continue ONLY IF "acceptance" fails
46 | if: always() && needs.acceptance.result == 'failure' || needs.unit.result == 'failure'
47 | runs-on: ubuntu-latest
48 | steps:
49 | # (3) checkout this repository in order to "see" the following custom action
50 | - name: Checkout repository
51 | uses: actions/checkout@v4
52 |
53 | # (4) "use" the custom action to retrigger the failed "acceptance job" above
54 | # NOTE: pass the SOURCE_GITHUB_TOKEN to the custom action because (a) it must have
55 | # this to trigger the reusable workflow that restarts the failed job; and
56 | # (b) custom actions do not have access to the calling workflow's secrets
57 | - name: Trigger reusable workflow
58 | uses: "puppetlabs/cat-github-actions/.github/actions/workflow-restarter-proxy@main"
59 | env:
60 | SOURCE_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
61 | with:
62 | repository: ${{ github.repository }}
63 | run_id: ${{ github.run_id }}
64 |
--------------------------------------------------------------------------------
/.github/workflows/workflow-restarter.yml:
--------------------------------------------------------------------------------
1 | # target-repo/.github/workflows/call-reusable-workflow.yml
2 | name: Workflow Restarter
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | repo:
7 | description: "GitHub repository name."
8 | required: true
9 | type: string
10 | run_id:
11 | description: "The ID of the workflow run to rerun."
12 | required: true
13 | type: string
14 | retries:
15 | description: "The number of times to retry the workflow run."
16 | required: false
17 | type: string
18 | default: "3"
19 |
20 | jobs:
21 | call-reusable-workflow:
22 | uses: "puppetlabs/cat-github-actions/.github/workflows/workflow-restarter.yml@main"
23 | with:
24 | repo: ${{ inputs.repo }}
25 | run_id: ${{ inputs.run_id }}
26 | retries: ${{ inputs.retries }}
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | *.swp
4 | /.config
5 | /coverage/
6 | /inventory.yaml
7 | /InstalledFiles
8 | /pkg/
9 | /spec/fixtures/
10 | /spec/reports/
11 | /spec/examples.txt
12 | /test/tmp/
13 | /test/version_tmp/
14 | /tmp/
15 |
16 | # Used by dotenv library to load environment variables.
17 | # .env
18 |
19 | ## Specific to RubyMotion:
20 | .dat*
21 | .repl_history
22 | build/
23 | *.bridgesupport
24 | build-iPhoneOS/
25 | build-iPhoneSimulator/
26 |
27 | ## Specific to RubyMotion (use of CocoaPods):
28 | #
29 | # We recommend against adding the Pods directory to your .gitignore. However
30 | # you should judge for yourself, the pros and cons are mentioned at:
31 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
32 | #
33 | # vendor/Pods/
34 |
35 | ## Documentation cache and generated files:
36 | /.yardoc/
37 | /_yardoc/
38 | /doc/
39 | /rdoc/
40 |
41 | ## Environment normalization:
42 | /.bundle/
43 | /vendor/bundle
44 | /lib/bundler/man/
45 |
46 | # for a library or gem, you might want to ignore these files since the code is
47 | # intended to run in multiple environments; otherwise, check them in:
48 | # Gemfile.lock
49 | .ruby-version
50 | # .ruby-gemset
51 |
52 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
53 | .rvmrc
54 |
55 | # bundler defaults
56 | /.bundle/
57 | /.yardoc
58 | /Gemfile.lock
59 | /_yardoc/
60 | /coverage/
61 | /doc/
62 | /pkg/
63 | /spec/reports/
64 | /tmp/
65 | /vendor/gems/
66 | Gemfile.local
67 |
68 | # ignore bundler generated binstubs, but keep setup/console scripts
69 | /bin/*
70 | !/bin/setup
71 | !/bin/console
72 |
73 | # beaker output directories
74 | /log/
75 | /repo-config/
76 | /acceptance_hosts.yml
77 | /junit/
78 | /archive/
79 | /sut-files.tgz
80 |
81 | # Puppet packaging files
82 | /ext/packaging/
83 |
84 | # generated artifacts from rake tasks
85 | /output
86 | modules
87 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Setting ownership to the tooling team
2 | * @puppetlabs/devx
3 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in pdk.gemspec
4 | gemspec
5 |
6 | group :development do
7 | gem 'ruby-prof'
8 | gem 'yard'
9 |
10 | gem 'fuubar'
11 | gem 'pry'
12 | gem 'pry-stack_explorer'
13 | end
14 |
15 | group :test do
16 | gem 'parallel'
17 | gem 'parallel_tests'
18 | gem 'rake'
19 | gem 'rexml'
20 | gem 'rspec', '~> 3.0'
21 | gem 'rubocop', '~> 1.70.0', require: false
22 | gem 'rubocop-performance', '~> 1.22.1', require: false
23 | gem 'rubocop-rspec', '~> 3.1.0', require: false
24 | gem 'simplecov-console'
25 |
26 | # Temporary exclusion required as these versions are currently broken for us
27 | gem 'rubocop-factory_bot', '!= 2.26.0', require: false
28 | gem 'rubocop-rspec_rails', '!= 2.29.0', require: false
29 | end
30 |
31 | group :acceptance do
32 | gem 'minitar-cli'
33 | gem 'rspec-xsd'
34 | gem 'serverspec'
35 | end
36 |
37 | group :acceptance_ci do
38 | gem 'puppetlabs_spec_helper', '~> 7.0', require: false
39 | end
40 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Puppet Development Kit
2 |
3 | Copyright (C) 2017 Puppet, Inc.
4 |
5 | Puppet Labs can be contacted at: info@puppetlabs.com
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.
18 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'bundler/setup'
4 | require 'pdk'
5 |
6 | # You can add fixtures and/or initialization code here to make experimenting
7 | # with your gem easier. You can also use a different console, if you like.
8 |
9 | require 'pry'
10 | Pry.start
11 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
8 | # Do any other automated setup that you need to do here
9 |
--------------------------------------------------------------------------------
/docs/pdk-workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/puppetlabs/pdk/9c9acab177d14a94f113f81b75220cb215133f3a/docs/pdk-workflow.png
--------------------------------------------------------------------------------
/docs/pdk.ditamap:
--------------------------------------------------------------------------------
1 |
2 |
3 |
28 |
--------------------------------------------------------------------------------
/docs/pdk_building_module_packages.md:
--------------------------------------------------------------------------------
1 | # Building module packages
2 |
3 | Before you can upload and publish your module to the Forge, build an uploadable
4 | module package.
5 |
6 | The `pdk build` command performs a series of checks on your module and builds a
7 | `tar.gz` package so that you can upload your module to the Forge. To learn more
8 | about publishing your module to the Forge, see the documentation about
9 | [publishing your
10 | module](https://www.puppet.com/docs/puppet/latest/modules_publishing.html).
11 |
12 | When you run the `pdk build` command, PDK checks your module metadata, looks for
13 | any symlinks, and excludes from the package any files listed in the `.gitignore`
14 | or `.pdkignore` files. If PDK finds any issues with metadata or symlinks, it
15 | prompts you to fix these issues.
16 |
17 | By default, the `.pdkignore` file contains a list of commonly ignored files,
18 | such as temporary files. This file is located in the module's main directory. To
19 | add or remove files to this list, define them in the module's `.sync.yml` file
20 | and run `pdk update` on your module.
21 |
22 | PDK prompts you for confirmation before building the package. It writes module
23 | packages to the module's `pkg` directory, but you can specify a different
24 | directory if you prefer. PDK names module packages with the convention
25 | `forgeusername-modulename-version.tar.gz`.
26 |
27 | ## Build a module
28 |
29 | Build a module package with the `pdk build` command so that you can upload your
30 | module to the Forge.
31 |
32 | 1. From the command line, change into the module's directory with `cd
33 | `
34 |
35 | 2. Run `pdk build` and respond to any prompts.
36 |
37 | To change the behavior of the build command, add option flags to the
38 | command. For example, to create the package to a custom location, run `pdk
39 | build --target-dir=` . For a complete list of command options and
40 | usage information, see the PDK [command reference](pdk_reference.md).
41 |
42 |
43 | **Result:**
44 |
45 | PDK builds a package with the naming convention
46 | `forgeusername-modulename-version.tar.gz` to the `pkg` directory of the module.
47 |
48 | ### What to do next:
49 |
50 | You can now upload your module to the Forge.
51 |
52 |
--------------------------------------------------------------------------------
/docs/pdk_release_notes.md:
--------------------------------------------------------------------------------
1 | # Release notes
2 |
3 | These release notes contain important information about Puppet Development Kit
4 | (PDK).
5 |
6 | This release incorporates new features, enhancements, and resolved issues from
7 | all previous releases. If you're upgrading from an earlier version of PDK,check
8 | the release notes for any interim versions for details about additional
9 | improvements in this release over your current release. Update your PDK
10 | installation with every new release.
11 |
12 |
--------------------------------------------------------------------------------
/docs/pdk_troubleshooting.md:
--------------------------------------------------------------------------------
1 | # PDK troubleshooting
2 |
3 | If you are encountering trouble with PDK, check for these common issues.
4 |
5 | ## PDK not in ZShell PATH on Mac OS X
6 |
7 | With ZShell on Mac OS X, PDK is not automatically added to the PATH. To fix
8 | this, add the PATH by adding the line `eval (/usr/libexec/path_helper -s)` to
9 | the ZShell resource file (`~/.zshrc`).
10 |
11 | ## PDK failing to pull from custom git server
12 |
13 | If a `fatal: unable to access...SSL certificate problem: self signed certificate` error occurs during PDK usage, it indicates that the PDK is trying to download a module from a source that isn't trusted. For example, given a `.fixtures.yml` that references an untrusted self-hosted git repository server `git.self.hosted`
14 |
15 | ```yaml
16 | # .fixtures.yml
17 | ---
18 | fixtures:
19 | forge_modules:
20 | nginx: "puppet/nginx"
21 | repositories:
22 | mymodule: 'https://git.self.hosted/companyxyz/mymodule.git'
23 | ```
24 |
25 | then running `pdk test unit` will throw an error something like:
26 |
27 | ```bash
28 | root@seattle:~/modules/tester# pdk test unit
29 | pdk (INFO): Using Ruby 3.2.2
30 | pdk (INFO): Using Puppet 8.1.0
31 | [✖] Preparing to run the unit tests.
32 | [✔] Cleaning up after running unit tests.
33 | pdk (ERROR): The spec_prep rake task failed with the following error(s):
34 |
35 | Cloning into 'spec/fixtures/modules/mymodule'...
36 | fatal: unable to access 'https://git.self.hosted/companyxyz/mymodule.git/': SSL certificate problem: self signed certificate
37 | # terminated with exception (report_on_exception is true):
38 | ...
39 | ...
40 | ```
41 |
42 | To resolve this issue, first create a backup of the existing PDK certificates file, which lives `/opt/puppetlabs/pdk/ssl/cert.pem` on linux machines and `C:\Program Files\Puppet Labs\DevelopmentKit\ssl\cert.pem` on windows.
43 |
44 | Then do the following
45 |
46 | * Obtain the chain of trust certificates from the self-hosted server. For example, `printf '' | openssl s_client -connect git.self.hosted:443 -showcerts` will return metadata including the trust certificates for the `git.self.hosted:443` server.
47 | * Append the trust certificates to the end of the `cert.pem` ensuring the pdk "trusts" the new self-hosted git server.
48 |
49 | **NOTE:** There is a known issue [Upgrading the pdk over-writes the pdk's cert.pem](pdk_known_issues.md#upgrading-the-pdk-over-writes-the-pdks-certpem). Therefore, any custom certificates will need to be added again after upgrading the pdk.
50 |
--------------------------------------------------------------------------------
/docs/pdk_updating_modules.md:
--------------------------------------------------------------------------------
1 | # Updating modules with changes to the template
2 |
3 | To keep your module's configuration current with changes to either the PDK
4 | default template or your own custom template, use the `pdk update` command.
5 |
6 | The `pdk update` function updates your module based on the template you used
7 | when you created or converted your module. If there have been any changes to
8 | that template PDK updates your module to incorporate them.
9 |
10 | If you used a custom template, you can update whenever you know there is a
11 | change in your template. If you didn't specify any custom template, you created
12 | or converted your module using the default PDK template and can update when new
13 | versions of PDK release.
14 |
15 | When you run the `update` command, PDK displays a summary of the files that will
16 | change during converstion and prompts you to either continue or cancel the
17 | update. Either way, PDK generates a detailed change report, `update_report.txt`,
18 | in the top directory of the module. This report is replaced by an updated
19 | version every time you run the `update` command.
20 |
21 | You can check for template changes by running `update` with the `--noop` option,
22 | which runs the command in "no operation" mode. This option shows what changes
23 | would be made, but doesn't actually make them.
24 |
25 | **Important:** The default PDK template URL changed in PDK version 1.3.0. If you
26 | created your module with a PDK version earlier than 1.3.0, update your PDK
27 | version and run `pdk convert` on your old module to bring it up to date.
28 |
29 | **Related information**
30 | - [Converting modules](pdk_converting_modules.md)
31 |
32 |
--------------------------------------------------------------------------------
/exe/pdk:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require 'pdk/cli'
4 |
5 | begin
6 | PDK::CLI.run(ARGV)
7 | rescue Interrupt
8 | warn "\nAborted!"
9 | exit 1
10 | end
11 |
--------------------------------------------------------------------------------
/ext/PuppetDevelopmentKit/PuppetDevelopmentKit.psd1.erb:
--------------------------------------------------------------------------------
1 | @{
2 | ModuleToProcess = 'PuppetDevelopmentKit.psm1'
3 | ModuleVersion = '<%= Gem::Version.new(PDK::VERSION).release %>'
4 | GUID = 'bfe70e90-1802-4f6b-b4a0-f627d53f593f'
5 | Author = "Puppet, Inc"
6 | CompanyName = "Puppet, Inc"
7 | Copyright = '(c) <%= Time.new.year %> Puppet, Inc. All rights reserved'
8 | FunctionsToExport = @('pdk')
9 | CmdletsToExport = @()
10 | VariablesToExport = @()
11 | AliasesToExport = @()
12 | PrivateData = @{
13 | PSData = @{
14 | # Tags = @()
15 | LicenseUri = 'https://github.com/puppetlabs/pdk/blob/main/LICENSE'
16 | ProjectUri = 'https://github.com/puppetlabs/pdk'
17 | # IconUri = ''
18 | ReleaseNotes = 'https://github.com/puppetlabs/pdk/blob/main/CHANGELOG.md'
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ext/PuppetDevelopmentKit/PuppetDevelopmentKit.psm1:
--------------------------------------------------------------------------------
1 | $fso = New-Object -ComObject Scripting.FileSystemObject
2 |
3 | $env:DEVKIT_BASEDIR = (Get-ItemProperty -Path "HKLM:\Software\Puppet Labs\DevelopmentKit").RememberedInstallDir64
4 | # Windows API GetShortPathName requires inline C#, so use COM instead
5 | $env:DEVKIT_BASEDIR = $fso.GetFolder($env:DEVKIT_BASEDIR).ShortPath
6 | $env:RUBY_DIR = "$($env:DEVKIT_BASEDIR)\private\ruby\2.4.5"
7 | $env:SSL_CERT_FILE = "$($env:DEVKIT_BASEDIR)\ssl\cert.pem"
8 | $env:SSL_CERT_DIR = "$($env:DEVKIT_BASEDIR)\ssl\certs"
9 |
10 | function pdk {
11 | if ($Host.Name -eq 'Windows PowerShell ISE Host') {
12 | Write-Error ("The Puppet Development Kit cannot be run in the Windows PowerShell ISE.`n" + `
13 | "Open a new Windows PowerShell Console, or 'Start-Process PowerShell', and use PDK within this new console.`n" + `
14 | "For more information see https://puppet.com/docs/pdk/latest/pdk_known_issues.html and https://devblogs.microsoft.com/powershell/console-application-non-support-in-the-ise.")
15 | return
16 | }
17 | if ($env:ConEmuANSI -eq 'ON') {
18 | &$env:RUBY_DIR\bin\ruby -S -- $env:RUBY_DIR\bin\pdk $args
19 | } else {
20 | &$env:DEVKIT_BASEDIR\private\tools\bin\ansicon.exe $env:RUBY_DIR\bin\ruby -S -- $env:RUBY_DIR\bin\pdk $args
21 | }
22 | }
23 |
24 | Export-ModuleMember -Function pdk -Variable *
25 |
--------------------------------------------------------------------------------
/ext/PuppetDevelopmentKitBeta/PuppetDevelopmentKitBeta.psd1.erb:
--------------------------------------------------------------------------------
1 | @{
2 | ModuleToProcess = 'PuppetDevelopmentKitBeta.psm1'
3 | ModuleVersion = '0.0.1-beta'
4 | GUID = '8b45f6ef-3990-4a86-a2e1-9f66a9ba55f6'
5 | Author = "Puppet, Inc"
6 | CompanyName = "Puppet, Inc"
7 | Copyright = '(c) <%= Time.new.year %> Puppet, Inc. All rights reserved'
8 | FunctionsToExport = @('PENDING')
9 | CmdletsToExport = @()
10 | VariablesToExport = @()
11 | AliasesToExport = @()
12 | PrivateData = @{
13 | PSData = @{
14 | # Tags = @()
15 | LicenseUri = 'https://github.com/puppetlabs/pdk/blob/main/LICENSE'
16 | ProjectUri = 'https://github.com/puppetlabs/pdk'
17 | IconUri = 'https://cdn.rawgit.com/puppetlabs/puppet-chocolatey-packages/main/icons/puppet.png'
18 | ReleaseNotes = 'https://github.com/puppetlabs/pdk/blob/main/CHANGELOG.md'
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ext/build_defaults.yml:
--------------------------------------------------------------------------------
1 | ---
2 | packaging_url: 'git://github.com/puppetlabs/packaging.git --branch=main'
3 | packaging_repo: 'packaging'
4 | packager: 'puppetlabs'
5 |
--------------------------------------------------------------------------------
/lib/pdk.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | autoload :AnswerFile, 'pdk/answer_file'
3 | autoload :Bolt, 'pdk/bolt'
4 | autoload :Config, 'pdk/config'
5 | autoload :Context, 'pdk/context'
6 | autoload :ControlRepo, 'pdk/control_repo'
7 | autoload :Generate, 'pdk/generate'
8 | autoload :Logger, 'pdk/logger'
9 | autoload :Module, 'pdk/module'
10 | autoload :Report, 'pdk/report'
11 | autoload :Template, 'pdk/template'
12 | autoload :TEMPLATE_REF, 'pdk/version'
13 | autoload :Util, 'pdk/util'
14 | autoload :Validate, 'pdk/validate'
15 | autoload :VERSION, 'pdk/version'
16 |
17 | # TODO: Refactor backend code to not raise CLI errors or use CLI util
18 | # methods.
19 | module CLI
20 | autoload :ExitWithError, 'pdk/cli/errors'
21 | autoload :FatalError, 'pdk/cli/errors'
22 | autoload :Util, 'pdk/cli/util'
23 | autoload :Exec, 'pdk/cli/exec'
24 | autoload :ExecGroup, 'pdk/cli/exec_group'
25 | end
26 |
27 | module Test
28 | autoload :Unit, 'pdk/tests/unit'
29 | end
30 |
31 | def self.logger
32 | @logger ||= PDK::Logger.new
33 | end
34 |
35 | def self.config
36 | return @config unless @config.nil?
37 |
38 | options = {}
39 | options['user.module_defaults.path'] = PDK::Util::Env['PDK_ANSWER_FILE'] unless PDK::Util::Env['PDK_ANSWER_FILE'].nil?
40 | @config = PDK::Config.new(options)
41 | end
42 |
43 | def self.context
44 | @context ||= PDK::Context.create(Dir.pwd)
45 | end
46 |
47 | def self.available_feature_flags
48 | @available_feature_flags ||= ['controlrepo'].freeze
49 | end
50 |
51 | def self.requested_feature_flags
52 | @requested_feature_flags ||= (PDK::Util::Env['PDK_FEATURE_FLAGS'] || '').split(',').map(&:strip)
53 | end
54 |
55 | def self.feature_flag?(flagname)
56 | return false unless available_feature_flags.include?(flagname)
57 |
58 | requested_feature_flags.include?(flagname)
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/pdk/answer_file.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class AnswerFile
5 | # Determine the default path to the answer file.
6 | #
7 | # @return [String] The path on disk to the default answer file.
8 | def self.default_answer_file_path
9 | PDK::Util::Filesystem.expand_path(File.join(PDK::Util.cachedir, 'answers.json'))
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/pdk/bolt.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Bolt
5 | # Returns true or false depending on if any of the common files and directories in
6 | # a Bolt Project are found in the specified directory. If a directory is not specified,
7 | # the current working directory is used.
8 | #
9 | # @see https://puppet.com/docs/bolt/latest/bolt_project_directories.html
10 | #
11 | # @return [boolean] True if any bolt specific files or directories are present
12 | #
13 | def bolt_project_root?(path = Dir.pwd)
14 | return true if File.basename(path) == 'Boltdir' && PDK::Util::Filesystem.directory?(path)
15 |
16 | PDK::Util::Filesystem.file?(File.join(path, 'bolt.yaml'))
17 | end
18 | module_function :bolt_project_root?
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/pdk/cli/bundle.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @bundle_cmd = @base_cmd.define_command do
4 | name 'bundle'
5 | usage 'bundle [bundler_options]'
6 | summary 'Command pass-through to bundler'
7 | description <<~EOF
8 | For advanced users, pdk bundle runs arbitrary commands in the bundler environment that pdk manages.
9 | Careless use of this command can lead to errors that pdk can't help recover from.
10 | EOF
11 | skip_option_parsing
12 |
13 | run do |_opts, args, _cmd|
14 | require 'pdk/util/bundler'
15 |
16 | PDK::CLI::Util.ensure_in_module!(
17 | message: '`pdk bundle` can only be run from inside a valid module directory.'
18 | )
19 |
20 | PDK::CLI::Util.validate_puppet_version_opts({})
21 |
22 | screen_view_name = ['bundle']
23 | screen_view_name << args[0] if args.size >= 1
24 | screen_view_name << args[1] if args.size >= 2 && args[0] == 'exec'
25 |
26 | # Ensure that the correct Ruby is activated before running command.
27 | puppet_env = PDK::CLI::Util.puppet_from_opts_or_env({})
28 | PDK::Util::RubyVersion.use(puppet_env[:ruby_version])
29 |
30 | gemfile_env = PDK::Util::Bundler::BundleHelper.gemfile_env(puppet_env[:gemset])
31 |
32 | require 'pdk/cli/exec'
33 | require 'pdk/cli/exec/interactive_command'
34 |
35 | command = PDK::CLI::Exec::InteractiveCommand.new(PDK::CLI::Exec.bundle_bin, *args).tap do |c|
36 | c.context = :pwd
37 | c.update_environment(gemfile_env)
38 | end
39 |
40 | result = command.execute!
41 |
42 | exit result[:exit_code]
43 | end
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/pdk/cli/convert.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @convert_cmd = @base_cmd.define_command do
4 | name 'convert'
5 | usage 'convert [options]'
6 | summary 'Convert an existing module to be compatible with the PDK.'
7 |
8 | PDK::CLI.template_url_option(self)
9 | PDK::CLI.template_ref_option(self)
10 | PDK::CLI.skip_interview_option(self)
11 | PDK::CLI.full_interview_option(self)
12 | flag nil, :noop, 'Do not convert the module, just output what would be done.'
13 | flag nil, :force, 'Convert the module automatically, with no prompts.'
14 | flag nil, :'add-tests', 'Add any missing tests while converting the module.'
15 | flag nil, :'default-template', 'Convert the module to use the default PDK template.'
16 |
17 | run do |opts, _args, _cmd|
18 | # Write the context information to the debug log
19 | PDK.context.to_debug_log
20 |
21 | unless PDK.context.is_a?(PDK::Context::Module) || PDK.context.is_a?(PDK::Context::ControlRepo)
22 | raise PDK::CLI::ExitWithError, '`pdk convert` can only be run from inside a valid module directory.'
23 | end
24 |
25 | raise PDK::CLI::ExitWithError, 'You can not specify --noop and --force when converting a module' if opts[:noop] && opts[:force]
26 |
27 | if opts[:'default-template']
28 | raise PDK::CLI::ExitWithError, 'You can not specify --template-url and --default-template.' if opts[:'template-url']
29 |
30 | opts[:'template-url'] = PDK::Util::TemplateURI.default_template_addressable_uri.to_s
31 | PDK.config.set(['user', 'module_defaults', 'template-url'], nil)
32 | end
33 |
34 | PDK::CLI::Util.validate_template_opts(opts)
35 |
36 | if opts[:'skip-interview'] && opts[:'full-interview']
37 | PDK.logger.info 'Ignoring --full-interview and continuing with --skip-interview.'
38 | opts[:'full-interview'] = false
39 | end
40 |
41 | if opts[:force] && opts[:'full-interview']
42 | PDK.logger.info 'Ignoring --full-interview and continuing with --force.'
43 | opts[:'full-interview'] = false
44 | end
45 |
46 | PDK::Module::Convert.invoke(PDK.context.root_path, opts)
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/lib/pdk/cli/env.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @env_cmd = @base_cmd.define_command do
4 | name 'env'
5 | usage 'env'
6 | summary '(Experimental) Output environment variables for specific Puppet context'
7 | description <<~EOF
8 | [experimental] Aids in setting a CLI context for a specified version of Puppet by outputting export commands for necessary environment variables.
9 | EOF
10 |
11 | PDK::CLI.puppet_version_options(self)
12 | PDK::CLI.puppet_dev_option(self)
13 |
14 | run do |opts, _args, _cmd|
15 | require 'pdk/util'
16 | require 'pdk/util/ruby_version'
17 |
18 | PDK::CLI::Util.validate_puppet_version_opts(opts)
19 |
20 | # Ensure that the correct Ruby is activated before running command.
21 | puppet_env = PDK::CLI::Util.puppet_from_opts_or_env(opts)
22 | PDK::Util::RubyVersion.use(puppet_env[:ruby_version])
23 |
24 | resolved_env = {
25 | 'PDK_RESOLVED_PUPPET_VERSION' => puppet_env[:gemset][:puppet],
26 | 'PDK_RESOLVED_RUBY_VERSION' => puppet_env[:ruby_version]
27 | }
28 |
29 | resolved_env['GEM_HOME'] = PDK::Util::RubyVersion.gem_home
30 | gem_path = PDK::Util::RubyVersion.gem_path
31 | resolved_env['GEM_PATH'] = gem_path.empty? ? resolved_env['GEM_HOME'] : gem_path
32 |
33 | # Make sure invocation of Ruby prefers our private installation.
34 | package_binpath = PDK::Util.package_install? ? File.join(PDK::Util.pdk_package_basedir, 'bin') : nil
35 |
36 | resolved_env['PATH'] = [
37 | PDK::Util::RubyVersion.bin_path,
38 | File.join(resolved_env['GEM_HOME'], 'bin'),
39 | PDK::Util::RubyVersion.gem_paths_raw.map { |gem_path_raw| File.join(gem_path_raw, 'bin') },
40 | package_binpath,
41 | PDK::Util::Env['PATH']
42 | ].compact.flatten.join(File::PATH_SEPARATOR)
43 |
44 | resolved_env.each do |var, val|
45 | puts "export #{var}=\"#{val}\""
46 | end
47 | exit 0
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/pdk/cli/errors.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module CLI
5 | class FatalError < StandardError
6 | attr_reader :exit_code
7 |
8 | def initialize(msg = 'An unexpected error has occurred. Try running the command again with --debug', opts = {})
9 | @exit_code = opts.fetch(:exit_code, 1)
10 | super(msg)
11 | end
12 | end
13 |
14 | class ExitWithError < StandardError
15 | attr_reader :exit_code, :log_level
16 |
17 | def initialize(msg, opts = {})
18 | @exit_code = opts.fetch(:exit_code, 1)
19 | @log_level = opts.fetch(:log_level, :error)
20 | super(msg)
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/pdk/cli/get.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @get_cmd = @base_cmd.define_command do
4 | name 'get'
5 | usage 'get [subcommand] [options]'
6 | summary 'Retrieve information about the PDK or current project.'
7 | default_subcommand 'help'
8 |
9 | run do |_opts, args, _cmd|
10 | if args == ['help']
11 | PDK::CLI.run(['get', '--help'])
12 | exit 0
13 | end
14 |
15 | PDK::CLI.run(['get', 'help']) if args.empty?
16 | end
17 | end
18 | @get_cmd.add_command Cri::Command.new_basic_help
19 | end
20 | end
21 |
22 | require 'pdk/cli/get/config'
23 |
--------------------------------------------------------------------------------
/lib/pdk/cli/get/config.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @get_config_cmd = @get_cmd.define_command do
4 | name 'config'
5 | usage 'config [name]'
6 | summary 'Retrieve the configuration for . If not specified, retrieve all configuration settings'
7 |
8 | run do |_opts, args, _cmd|
9 | item_name = args[0]
10 | resolved_config = PDK.config.resolve(item_name)
11 | # If the user wanted to know a setting but it doesn't exist, raise an error
12 | if resolved_config.empty? && !item_name.nil?
13 | PDK.logger.error(format("Configuration item '%{name}' does not exist", name: item_name))
14 | exit 1
15 | end
16 | # If the user requested a setting and it's the only one resolved, then just output the value
17 | if resolved_config.count == 1 && resolved_config.keys[0] == item_name
18 | puts format('%{value}', value: resolved_config.values[0])
19 | exit 0
20 | end
21 | # Otherwise just output everything
22 | resolved_config.keys.sort.each { |key| puts format('%{name}=%{value}', name: key, value: resolved_config[key]) }
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_cmd = @base_cmd.define_command do
4 | name 'new'
5 | usage 'new [options]'
6 | summary 'create a new module, etc.'
7 | description 'Creates a new using relevant options.'
8 | default_subcommand 'help'
9 | end
10 |
11 | @new_cmd.add_command Cri::Command.new_basic_help
12 | end
13 | end
14 |
15 | require 'pdk/cli/new/class'
16 | require 'pdk/cli/new/defined_type'
17 | require 'pdk/cli/new/module'
18 | require 'pdk/cli/new/provider'
19 | require 'pdk/cli/new/task'
20 | require 'pdk/cli/new/test'
21 | require 'pdk/cli/new/transport'
22 | require 'pdk/cli/new/fact'
23 | require 'pdk/cli/new/function'
24 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/class.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_class_cmd = @new_cmd.define_command do
4 | name 'class'
5 | usage 'class [options] '
6 | summary 'Create a new class named using given options'
7 |
8 | run do |opts, args, _cmd|
9 | require 'pdk/generate/puppet_class'
10 |
11 | PDK::CLI::Util.ensure_in_module!(
12 | message: 'Classes can only be created from inside a valid module directory.',
13 | log_level: :info
14 | )
15 |
16 | class_name = args[0]
17 |
18 | if class_name.nil? || class_name.empty?
19 | puts command.help
20 | exit 1
21 | end
22 |
23 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid class name", name: class_name) unless Util::OptionValidator.valid_class_name?(class_name)
24 |
25 | updates = PDK::Generate::PuppetClass.new(PDK.context, class_name, opts).run
26 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
27 | end
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/defined_type.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_define_cmd = @new_cmd.define_command do
4 | name 'defined_type'
5 | usage 'defined_type [options] '
6 | summary 'Create a new defined type named using given options'
7 |
8 | run do |opts, args, _cmd|
9 | PDK::CLI::Util.ensure_in_module!(
10 | message: 'Defined types can only be created from inside a valid module directory.',
11 | log_level: :info
12 | )
13 |
14 | defined_type_name = args[0]
15 |
16 | if defined_type_name.nil? || defined_type_name.empty?
17 | puts command.help
18 | exit 1
19 | end
20 |
21 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid defined type name", name: defined_type_name) unless Util::OptionValidator.valid_defined_type_name?(defined_type_name)
22 |
23 | require 'pdk/generate/defined_type'
24 |
25 | updates = PDK::Generate::DefinedType.new(PDK.context, defined_type_name, opts).run
26 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
27 | end
28 | end
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/fact.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_fact_cmd = @new_cmd.define_command do
4 | name 'fact'
5 | usage 'fact [options] '
6 | summary 'Create a new custom fact named using given options'
7 |
8 | run do |opts, args, _cmd|
9 | PDK::CLI::Util.ensure_in_module!
10 |
11 | fact_name = args[0]
12 |
13 | if fact_name.nil? || fact_name.empty?
14 | puts command.help
15 | exit 1
16 | end
17 |
18 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid fact name", name: fact_name) unless Util::OptionValidator.valid_fact_name?(fact_name)
19 |
20 | require 'pdk/generate/fact'
21 |
22 | updates = PDK::Generate::Fact.new(PDK.context, fact_name, opts).run
23 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/function.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_function_cmd = @new_cmd.define_command do
4 | name 'function'
5 | usage 'function [options] '
6 | summary 'Create a new function named using given options'
7 | option :t, :type, 'The function type, (native or v4)', argument: :required, default: 'native'
8 |
9 | run do |opts, args, _cmd|
10 | PDK::CLI::Util.ensure_in_module!
11 |
12 | function_name = args[0]
13 |
14 | if function_name.nil? || function_name.empty?
15 | puts command.help
16 | exit 1
17 | end
18 |
19 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid function name", name: function_name) unless Util::OptionValidator.valid_function_name?(function_name)
20 |
21 | require 'pdk/generate/function'
22 | updates = PDK::Generate::Function.new(PDK.context, function_name, opts).run
23 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/module.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_module_cmd = @new_cmd.define_command do
4 | name 'module'
5 | usage 'module [options] [module_name] [target_dir]'
6 | summary 'Create a new module named [module_name] using given options'
7 |
8 | PDK::CLI.template_url_option(self)
9 | PDK::CLI.template_ref_option(self)
10 | PDK::CLI.skip_interview_option(self)
11 | PDK::CLI.full_interview_option(self)
12 |
13 | option nil, 'license', 'Specifies the license this module is written under. ' \
14 | "This should be a identifier from https://spdx.org/licenses/. Common values are 'Apache-2.0', 'MIT', or 'proprietary'.", argument: :required
15 | option nil, 'skip-bundle-install', 'Do not automatically run `bundle install` after creating the module.', hidden: true
16 |
17 | run do |opts, args, _cmd|
18 | require 'pdk/generate/module'
19 |
20 | module_name = args[0]
21 | target_dir = args[1]
22 |
23 | PDK::CLI::Util.validate_template_opts(opts)
24 |
25 | if opts[:'skip-interview'] && opts[:'full-interview']
26 | PDK.logger.info 'Ignoring --full-interview and continuing with --skip-interview.'
27 | opts[:'full-interview'] = false
28 | end
29 |
30 | if module_name.nil? || module_name.empty?
31 | if opts[:'skip-interview']
32 | raise PDK::CLI::ExitWithError,
33 | 'You must specify a module name on the command line when running ' \
34 | 'with --skip-interview.'
35 | end
36 | else
37 | module_name_parts = module_name.split('-', 2)
38 | if module_name_parts.size > 1
39 | opts[:username] = module_name_parts[0]
40 | opts[:module_name] = module_name_parts[1]
41 | else
42 | opts[:module_name] = module_name
43 | end
44 | opts[:target_dir] = target_dir.nil? ? opts[:module_name] : target_dir
45 | end
46 |
47 | PDK.logger.info(format('Creating new module: %{modname}', modname: module_name))
48 | PDK::Generate::Module.invoke(opts)
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/provider.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_provider_cmd = @new_cmd.define_command do
4 | name 'provider'
5 | usage 'provider [options] '
6 | summary '[experimental] Create a new ruby provider named using given options'
7 |
8 | run do |opts, args, _cmd|
9 | PDK::CLI::Util.ensure_in_module!
10 |
11 | provider_name = args[0]
12 |
13 | if provider_name.nil? || provider_name.empty?
14 | puts command.help
15 | exit 1
16 | end
17 |
18 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid provider name", name: provider_name) unless Util::OptionValidator.valid_provider_name?(provider_name)
19 |
20 | require 'pdk/generate/provider'
21 |
22 | updates = PDK::Generate::Provider.new(PDK.context, provider_name, opts).run
23 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/task.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_task_cmd = @new_cmd.define_command do
4 | name 'task'
5 | usage 'task [options] '
6 | summary 'Create a new task named using given options'
7 |
8 | option nil, :description, 'A short description of the purpose of the task', argument: :required
9 |
10 | run do |opts, args, _cmd|
11 | require 'pdk/generate/task'
12 |
13 | PDK::CLI::Util.ensure_in_module!(
14 | message: 'Tasks can only be created from inside a valid module directory.',
15 | log_level: :info
16 | )
17 |
18 | task_name = args[0]
19 |
20 | if task_name.nil? || task_name.empty?
21 | puts command.help
22 | exit 1
23 | end
24 |
25 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid task name", name: task_name) unless Util::OptionValidator.valid_task_name?(task_name)
26 |
27 | updates = PDK::Generate::Task.new(PDK.context, task_name, opts).run
28 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
29 | end
30 | end
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/test.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_define_cmd = @new_cmd.define_command do
4 | name 'test'
5 | usage 'test [options] '
6 | summary 'Create a new test for the object named '
7 | flag :u, :unit, 'Create a new unit test.'
8 | PDK::CLI.puppet_version_options(self)
9 | PDK::CLI.puppet_dev_option(self)
10 |
11 | run do |opts, args, _cmd|
12 | require 'pdk/util/puppet_strings'
13 | require 'pdk/util/bundler'
14 |
15 | PDK::CLI::Util.validate_puppet_version_opts(opts)
16 | PDK::CLI::Util.ensure_in_module!(
17 | message: 'Tests can only be created from inside a valid module directory.',
18 | log_level: :info
19 | )
20 |
21 | object_name = args[0]
22 |
23 | if object_name.nil? || object_name.empty?
24 | puts command.help
25 | exit 1
26 | end
27 |
28 | unless opts[:unit]
29 | # At a future time, we'll replace this conditional with an interactive
30 | # question to choose the test type.
31 | PDK.logger.info 'Test type not specified, assuming unit.'
32 | opts[:unit] = true
33 | end
34 |
35 | puppet_env = PDK::CLI::Util.puppet_from_opts_or_env(opts)
36 | PDK::Util::RubyVersion.use(puppet_env[:ruby_version])
37 | PDK::Util::Bundler.ensure_bundle!(puppet_env[:gemset])
38 |
39 | begin
40 | generator, obj = PDK::Util::PuppetStrings.find_object(object_name)
41 |
42 | updates = generator.new(PDK.context, obj['name'], opts.merge(spec_only: true)).run
43 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
44 | rescue PDK::Util::PuppetStrings::NoObjectError
45 | raise PDK::CLI::ExitWithError, format('Unable to find anything called "%{object}" to generate unit tests for.', object: object_name)
46 | rescue PDK::Util::PuppetStrings::NoGeneratorError => e
47 | raise PDK::CLI::ExitWithError, format('PDK does not support generating unit tests for "%{object_type}" objects.', object_type: e.message)
48 | end
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/pdk/cli/new/transport.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @new_transport_cmd = @new_cmd.define_command do
4 | name 'transport'
5 | usage 'transport [options] '
6 | summary '[experimental] Create a new ruby transport named using given options'
7 |
8 | run do |opts, args, _cmd|
9 | PDK::CLI::Util.ensure_in_module!
10 |
11 | transport_name = args[0]
12 |
13 | if transport_name.nil? || transport_name.empty?
14 | puts command.help
15 | exit 1
16 | end
17 |
18 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid transport name", name: transport_name) unless Util::OptionValidator.valid_transport_name?(transport_name)
19 |
20 | require 'pdk/generate/transport'
21 |
22 | updates = PDK::Generate::Transport.new(PDK.context, transport_name, opts).run
23 | PDK::CLI::Util::UpdateManagerPrinter.print_summary(updates, tense: :past)
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/pdk/cli/release/prep.rb:
--------------------------------------------------------------------------------
1 | require 'pdk/cli/release'
2 |
3 | module PDK
4 | module CLI
5 | @release_prep_cmd = @release_cmd.define_command do
6 | name 'prep'
7 | usage 'prep [options]'
8 | summary '(Experimental) Performs all the pre-release checks to ensure module is ready to be released'
9 |
10 | flag nil, :force, 'Prepare the module automatically, with no prompts.'
11 | flag nil, :'skip-validation', 'Skips the module validation check.'
12 | flag nil, :'skip-changelog', 'Skips the automatic changelog generation.'
13 | flag nil, :'skip-dependency', 'Skips the module dependency check.'
14 | flag nil, :'skip-documentation', 'Skips the documentation update.'
15 |
16 | option nil, :version, 'Update the module to the specified version prior to release. When not specified, the new version will be computed from the Changelog where possible.',
17 | argument: :required
18 |
19 | run do |opts, _args, cmd|
20 | # Make sure build is being run in a valid module directory with a metadata.json
21 | PDK::CLI::Util.ensure_in_module!(
22 | message: "`pdk release #{cmd.name}` can only be run from inside a valid module with a metadata.json.",
23 | log_level: :info
24 | )
25 |
26 | opts[:'skip-build'] = true
27 | opts[:'skip-publish'] = true
28 |
29 | Release.prepare_interview(opts) unless opts[:force]
30 |
31 | release = PDK::Module::Release.new(nil, opts)
32 |
33 | Release.module_compatibility_checks!(release, opts)
34 |
35 | release.run
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/pdk/cli/release/publish.rb:
--------------------------------------------------------------------------------
1 | require 'pdk/cli/release'
2 |
3 | module PDK
4 | module CLI
5 | @release_publish_cmd = @release_cmd.define_command do
6 | name 'publish'
7 | usage 'publish [options] '
8 | summary '(Experimental) Publishes the module to the Forge.'
9 |
10 | flag nil, :force, 'Publish the module automatically, with no prompts.'
11 |
12 | option nil, :'forge-upload-url', 'Set forge upload url path.',
13 | argument: :required, default: 'https://forgeapi.puppetlabs.com/v3/releases'
14 |
15 | option nil, :'forge-token', 'Set Forge API token (you may also set via environment variable PDK_FORGE_TOKEN)', argument: :required
16 |
17 | run do |opts, _args, cmd|
18 | # Make sure build is being run in a valid module directory with a metadata.json
19 | PDK::CLI::Util.ensure_in_module!(
20 | message: "`pdk release #{cmd.name}` can only be run from inside a valid module with a metadata.json.",
21 | log_level: :info
22 | )
23 |
24 | opts[:'skip-validation'] = true
25 | opts[:'skip-changelog'] = true
26 | opts[:'skip-dependency'] = true
27 | opts[:'skip-documentation'] = true
28 | opts[:'skip-build'] = true
29 | opts[:'skip-versionset'] = true
30 | opts[:force] = true unless PDK::CLI::Util.interactive?
31 | opts[:'forge-token'] ||= PDK::Util::Env['PDK_FORGE_TOKEN']
32 |
33 | if opts[:'forge-token'].nil? || opts[:'forge-token'].empty?
34 | PDK.logger.error 'You must supply a Forge API token either via `--forge-token` option or PDK_FORGE_TOKEN environment variable.'
35 | exit 1
36 | end
37 |
38 | Release.prepare_publish_interview(TTY::Prompt.new(help_color: :cyan), opts) unless opts[:force]
39 |
40 | release = PDK::Module::Release.new(nil, opts)
41 |
42 | release.run
43 | end
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/pdk/cli/remove.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @remove_cmd = @base_cmd.define_command do
4 | name 'remove'
5 | usage 'remove [subcommand] [options]'
6 | summary 'Remove or delete information about the PDK or current project.'
7 | default_subcommand 'help'
8 |
9 | run do |_opts, args, _cmd|
10 | if args == ['help']
11 | PDK::CLI.run(['remove', '--help'])
12 | exit 0
13 | end
14 |
15 | PDK::CLI.run(['remove', 'help']) if args.empty?
16 | end
17 | end
18 | @remove_cmd.add_command Cri::Command.new_basic_help
19 | end
20 | end
21 |
22 | require 'pdk/cli/remove/config'
23 |
--------------------------------------------------------------------------------
/lib/pdk/cli/set.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @set_cmd = @base_cmd.define_command do
4 | name 'set'
5 | usage 'set [subcommand] [options]'
6 | summary 'Set or update information about the PDK or current project.'
7 | default_subcommand 'help'
8 |
9 | run do |_opts, args, _cmd|
10 | if args == ['help']
11 | PDK::CLI.run(['set', '--help'])
12 | exit 0
13 | end
14 |
15 | PDK::CLI.run(['set', 'help']) if args.empty?
16 | end
17 | end
18 | @set_cmd.add_command Cri::Command.new_basic_help
19 | end
20 | end
21 |
22 | require 'pdk/cli/set/config'
23 |
--------------------------------------------------------------------------------
/lib/pdk/cli/test.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @test_cmd = @base_cmd.define_command do
4 | name 'test'
5 | usage 'test [subcommand] [options]'
6 | summary 'Run tests.'
7 | default_subcommand 'help'
8 | end
9 | @test_cmd.add_command Cri::Command.new_basic_help
10 | end
11 | end
12 |
13 | require 'pdk/cli/test/unit'
14 |
--------------------------------------------------------------------------------
/lib/pdk/cli/update.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module CLI
3 | @update_cmd = @base_cmd.define_command do
4 | name 'update'
5 | usage 'update [options]'
6 | summary 'Update a module that has been created by or converted for use by PDK.'
7 |
8 | flag nil, :noop, 'Do not update the module, just output what would be done.'
9 | flag nil, :force, 'Update the module automatically, with no prompts.'
10 |
11 | PDK::CLI.template_ref_option(self)
12 |
13 | run do |opts, _args, _cmd|
14 | # Write the context information to the debug log
15 | PDK.context.to_debug_log
16 |
17 | unless PDK.context.is_a?(PDK::Context::Module) || PDK.context.is_a?(PDK::Context::ControlRepo)
18 | raise PDK::CLI::ExitWithError, '`pdk update` can only be run from inside a valid module directory.'
19 | end
20 |
21 | raise PDK::CLI::ExitWithError, 'This module does not appear to be PDK compatible. To make the module compatible with PDK, run `pdk convert`.' unless PDK::Util.module_pdk_compatible?
22 |
23 | raise PDK::CLI::ExitWithError, 'You can not specify --noop and --force when updating a module' if opts[:noop] && opts[:force]
24 |
25 | if Gem::Version.new(PDK::VERSION) < Gem::Version.new(PDK::Util.module_pdk_version)
26 | PDK.logger.warn "This module has been updated to PDK #{PDK::Util.module_pdk_version} which is newer than your PDK version (#{PDK::VERSION}), proceed with caution!"
27 |
28 | unless opts[:force]
29 | raise PDK::CLI::ExitWithError,
30 | 'Please update your PDK installation and try again. ' \
31 | 'You may also use the --force flag to override this and ' \
32 | 'continue at your own risk.'
33 | end
34 | end
35 |
36 | updater = PDK::Module::Update.new(PDK.context.root_path, opts)
37 |
38 | if updater.pinned_to_puppetlabs_template_tag?
39 | PDK.logger.info format('This module is currently pinned to version %{current_version} ' \
40 | 'of the default template. If you would like to update your ' \
41 | 'module to the latest version of the template, please run `pdk ' \
42 | 'update --template-ref %{new_version}`.', current_version: updater.template_uri.uri_fragment, new_version: PDK::TEMPLATE_REF)
43 | end
44 |
45 | updater.run
46 | end
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/lib/pdk/cli/util/command_redirector.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 | require 'tty/prompt'
3 |
4 | module PDK
5 | module CLI
6 | module Util
7 | class CommandRedirector < TTY::Prompt::AnswersCollector
8 | attr_accessor :command
9 |
10 | # Override the initialize method because the original one
11 | # doesn't work with Ruby 3.
12 | # rubocop:disable Lint/MissingSuper
13 | def initialize(prompt, options = {})
14 | @prompt = prompt
15 | @answers = options.fetch(:answers) { {} }
16 | end
17 | # rubocop:enable Lint/MissingSuper
18 |
19 | def pastel
20 | @pastel ||= Pastel.new
21 | end
22 |
23 | def target_command(cmd)
24 | @command = cmd
25 | end
26 |
27 | def run
28 | @prompt.puts "Did you mean '#{pastel.bold(@command)}'?"
29 | @prompt.yes?('-->')
30 | rescue PDK::CLI::Util::Interview::READER::InputInterrupt
31 | nil
32 | end
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/pdk/cli/util/option_normalizer.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module CLI
5 | module Util
6 | class OptionNormalizer
7 | def self.comma_separated_list_to_array(list, _options = {})
8 | raise 'Error: expected comma separated list' unless OptionValidator.comma_separated_list?(list)
9 |
10 | list.split(',').compact
11 | end
12 |
13 | # Parse one or more format:target pairs into report format
14 | # specifications.
15 | #
16 | # Each specification is a Hash with two values:
17 | # :method => The name of the method to call on the PDK::Report object
18 | # to render the report.
19 | # :target => The target to write the report to. This can be either an
20 | # IO object that implements #write, or a String filename
21 | # that will be opened for writing.
22 | #
23 | # If the target given is "stdout" or "stderr", this will convert those
24 | # strings into the appropriate IO object.
25 | #
26 | # @return [ArrayObject}>] An array of one or more report
27 | # format specifications
28 | def self.report_formats(formats)
29 | formats.map do |f|
30 | format, target = f.split(':', 2)
31 |
32 | begin
33 | OptionValidator.enum(format, PDK::Report.formats)
34 | rescue ArgumentError
35 | raise PDK::CLI::ExitWithError, format("'%{name}' is not a valid report format (%{valid})", name: format, valid: PDK::Report.formats.join(', '))
36 | end
37 |
38 | case target
39 | when 'stdout'
40 | target = $stdout
41 | when 'stderr'
42 | target = $stderr
43 | when nil
44 | target = PDK::Report.default_target
45 | end
46 |
47 | { method: :"write_#{format}", target: }
48 | end
49 | end
50 | end
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/pdk/cli/util/spinner.rb:
--------------------------------------------------------------------------------
1 | require 'tty-spinner'
2 |
3 | # Replace the built-in tty check in tty-spinner with our own implementation
4 | # that allows us to mock the behaviour during acceptance tests.
5 | module TTY
6 | class Spinner
7 | def tty?
8 | require 'pdk/cli/util'
9 |
10 | PDK::CLI::Util.interactive?
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/lib/pdk/config/errors.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | class Config
3 | class LoadError < StandardError; end
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/pdk/config/ini_file_setting.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class Config
5 | class IniFileSetting < PDK::Config::Setting
6 | # Initialises the PDK::Config::JSONSchemaSetting object.
7 | #
8 | # @see PDK::Config::Setting.initialize
9 | def initialize(_name, namespace, initial_value = nil)
10 | raise 'The IniFileSetting object can only be created within the IniFile Namespace' unless namespace.is_a?(PDK::Config::IniFile)
11 |
12 | super
13 | validate!(initial_value) unless initial_value.nil?
14 | end
15 |
16 | # Verifies that the new setting value is valid in an Ini File
17 | #
18 | # @see PDK::Config::Setting.validate!
19 | def validate!(value)
20 | # We're very restrictive here. Realistically Ini files only have string types
21 | return if value.nil? || value.is_a?(String) || value.is_a?(Integer)
22 |
23 | # The only other valid-ish type is a Hash
24 | raise ArgumentError, format('The setting %{key} may only be a String or Integer, not %{class}', key: qualified_name, class: value.class) unless value.is_a?(Hash)
25 |
26 | # Any hashes can only have a single String/Integer value
27 | value.each do |child_name, child_value|
28 | next if child_value.nil? || child_value.is_a?(String) || child_value.is_a?(Integer)
29 |
30 | raise ArgumentError, format('The setting %{key} may only be a String or Integer, not %{class}', key: "#{qualified_name}.#{child_name}", class: child_value.class)
31 | end
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/pdk/config/json.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class Config
5 | class JSON < Namespace
6 | # Parses a JSON document.
7 | #
8 | # @see PDK::Config::Namespace.parse_file
9 | def parse_file(filename)
10 | raise unless block_given?
11 |
12 | data = load_data(filename)
13 | return if data.nil? || data.empty?
14 |
15 | require 'json'
16 |
17 | data = ::JSON.parse(data)
18 | return if data.nil? || data.empty?
19 |
20 | data.each { |k, v| yield k, PDK::Config::Setting.new(k, self, v) }
21 | rescue ::JSON::ParserError => e
22 | raise PDK::Config::LoadError, e.message
23 | end
24 |
25 | # Serializes object data into a JSON string.
26 | #
27 | # @see PDK::Config::Namespace.serialize_data
28 | def serialize_data(data)
29 | require 'json'
30 |
31 | ::JSON.pretty_generate(data)
32 | end
33 | end
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/lib/pdk/config/json_schema_setting.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class Config
5 | class JSONSchemaSetting < PDK::Config::Setting
6 | # Initialises the PDK::Config::JSONSchemaSetting object.
7 | #
8 | # @see PDK::Config::Setting.initialize
9 | def initialize(_name, namespace, _initial_value)
10 | raise 'The JSONSchemaSetting object can only be created within the JSONSchemaNamespace' unless namespace.is_a?(PDK::Config::JSONSchemaNamespace)
11 |
12 | super
13 | end
14 |
15 | # Verifies that the new setting value is valid by calling the JSON schema validator on
16 | # a hash which includes the new setting
17 | #
18 | # @see PDK::Config::Setting.validate!
19 | def validate!(value)
20 | # Get the existing namespace data
21 | new_document = namespace.to_h
22 | # ... set the new value
23 | new_document[@name] = value
24 | begin
25 | # ... add validate it
26 | namespace.validate_document!(new_document)
27 | rescue ::JSON::Schema::ValidationError => e
28 | raise ArgumentError, format('%{key} %{message}', key: qualified_name, message: e.message)
29 | end
30 | end
31 |
32 | # Evaluate the default setting, firstly from the JSON schema and then
33 | # from any other default evaluators in the settings chain.
34 | #
35 | # @see PDK::Config::Setting.default
36 | #
37 | # @return [Object, nil] the result of evaluating the block given to
38 | # {#default_to}, or `nil` if the setting has no default.
39 | def default
40 | # Return the default from the schema document if it exists
41 | if namespace.schema_property_names.include?(@name)
42 | prop_schema = namespace.schema['properties'][@name]
43 | return prop_schema['default'] unless prop_schema['default'].nil?
44 | end
45 | # ... otherwise call the settings chain default
46 | # and if that doesn't exist, just return nil
47 | @previous_setting&.default
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/pdk/config/json_with_schema.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class Config
5 | class JSONWithSchema < JSONSchemaNamespace
6 | # Parses a JSON document with a schema.
7 | #
8 | # @see PDK::Config::Namespace.parse_file
9 | def parse_file(filename)
10 | raise unless block_given?
11 |
12 | data = load_data(filename)
13 | data = '{}' if data.nil? || data.empty?
14 | require 'json'
15 |
16 | @raw_data = ::JSON.parse(data)
17 | @raw_data = {} if @raw_data.nil?
18 |
19 | begin
20 | # Ensure the parsed document is actually valid
21 | validate_document!(@raw_data)
22 | rescue ::JSON::Schema::ValidationError => e
23 | raise PDK::Config::LoadError, format('The configuration file %{filename} is not valid: %{message}', filename:, message: e.message)
24 | end
25 |
26 | schema_property_names.each do |key|
27 | yield key, PDK::Config::JSONSchemaSetting.new(key, self, @raw_data[key])
28 | end
29 |
30 | # Remove all of the "known" settings from the schema and
31 | # we're left with the settings that we don't manage.
32 | self.unmanaged_settings = @raw_data.except(*schema_property_names)
33 | rescue ::JSON::ParserError => e
34 | raise PDK::Config::LoadError, e.message
35 | end
36 |
37 | # Serializes object data into a JSON string.
38 | #
39 | # @see PDK::Config::Namespace.serialize_data
40 | def serialize_data(data)
41 | require 'json'
42 |
43 | ::JSON.pretty_generate(data)
44 | end
45 | end
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/lib/pdk/config/validator.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | class Config
3 | # A collection of predefined validators for use with {PDK::Config::Value}.
4 | #
5 | # @example
6 | # value :enabled do
7 | # validate PDK::Config::Validator.boolean
8 | # end
9 | module Validator
10 | # @return [Hash{Symbol => [Proc,String]}] a {PDK::Config::Value}
11 | # validator that ensures that the value is either a TrueClass or
12 | # FalseClass.
13 | def self.boolean
14 | {
15 | proc: ->(value) { [true, false].include?(value) },
16 | message: 'must be a boolean: true or false'
17 | }
18 | end
19 |
20 | # @return [Hash{Symbol => [Proc,String]}] a {PDK::Config::Value}
21 | # validator that ensures that the value is a String that matches the
22 | # regex for a version 4 UUID.
23 | def self.uuid
24 | {
25 | proc: ->(value) { value.match(/\A\h{8}(?:-\h{4}){3}-\h{12}\z/) },
26 | message: 'must be a version 4 UUID'
27 | }
28 | end
29 | end
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/lib/pdk/config/yaml.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class Config
5 | # Parses a YAML document.
6 | #
7 | # @see PDK::Config::Namespace.parse_file
8 | class YAML < Namespace
9 | def parse_file(filename)
10 | raise unless block_given?
11 |
12 | data = load_data(filename)
13 | return if data.nil? || data.empty?
14 |
15 | require 'yaml'
16 |
17 | data = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
18 | ::YAML.safe_load(data, permitted_classes: [Symbol], permitted_symbols: [], aliases: true)
19 | else
20 | ::YAML.safe_load(data, [Symbol], [], true)
21 | end
22 | return if data.nil?
23 |
24 | data.each { |k, v| yield k, PDK::Config::Setting.new(k, self, v) }
25 | rescue Psych::SyntaxError => e
26 | raise PDK::Config::LoadError, format('Syntax error when loading %{file}: %{error}', file: filename, error: "#{e.problem} #{e.context}")
27 | rescue Psych::DisallowedClass => e
28 | raise PDK::Config::LoadError, format('Unsupported class in %{file}: %{error}', file: filename, error: e.message)
29 | end
30 |
31 | # Serializes object data into a YAML string.
32 | #
33 | # @see PDK::Config::Namespace.serialize_data
34 | def serialize_data(data)
35 | require 'yaml'
36 |
37 | ::YAML.dump(data)
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/lib/pdk/config/yaml_with_schema.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | class Config
5 | # Parses a YAML document with a JSON schema.
6 | #
7 | # @see PDK::Config::Namespace.parse_file
8 | class YAMLWithSchema < JSONSchemaNamespace
9 | def parse_file(filename)
10 | raise unless block_given?
11 |
12 | data = load_data(filename)
13 | data = '' if data.nil?
14 | require 'yaml'
15 | require 'json-schema'
16 |
17 | @raw_data = ::YAML.safe_load(data, [Symbol], [], true)
18 | @raw_data = {} if @raw_data.nil?
19 |
20 | begin
21 | # Ensure the parsed document is actually valid
22 | validate_document!(@raw_data)
23 | rescue ::JSON::Schema::ValidationError => e
24 | raise PDK::Config::LoadError, format('The configuration file %{filename} is not valid: %{message}', filename:, message: e.message)
25 | end
26 |
27 | require 'pdk/config/json_schema_setting'
28 |
29 | schema_property_names.each do |key|
30 | yield key, PDK::Config::JSONSchemaSetting.new(key, self, @raw_data[key])
31 | end
32 |
33 | # Remove all of the "known" settings from the schema and
34 | # we're left with the settings that we don't manage.
35 | self.unmanaged_settings = @raw_data.except(*schema_property_names)
36 | rescue Psych::SyntaxError => e
37 | raise PDK::Config::LoadError, format('Syntax error when loading %{file}: %{error}', file: filename, error: "#{e.problem} #{e.context}")
38 | rescue Psych::DisallowedClass => e
39 | raise PDK::Config::LoadError, format('Unsupported class in %{file}: %{error}', file: filename, error: e.message)
40 | end
41 |
42 | # Serializes object data into a YAML string.
43 | #
44 | # @see PDK::Config::Namespace.serialize_data
45 | def serialize_data(data)
46 | require 'yaml'
47 | ::YAML.dump(data)
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/pdk/context/module.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Context
5 | # Represents a context for a Puppet Module
6 | class Module < PDK::Context::AbstractContext
7 | # @param module_root [String] The root path for the module.
8 | # @param context_path [String] The path where this context was created from e.g. Dir.pwd
9 | # @see PDK::Context::AbstractContext
10 | def initialize(module_root, context_path)
11 | super(context_path)
12 | @root_path = module_root
13 | end
14 |
15 | # @see PDK::Context::AbstractContext.pdk_compatible?
16 | def pdk_compatible?
17 | PDK::Util.module_pdk_compatible?(root_path)
18 | end
19 |
20 | # :nocov:
21 | # @see PDK::Context::AbstractContext.display_name
22 | def display_name
23 | 'a Puppet Module context'
24 | end
25 | # :nocov:
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/pdk/context/none.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Context
5 | # Represents a context which the PDK does not know. For example
6 | # an empty directory
7 | class None < PDK::Context::AbstractContext
8 | # :nocov:
9 | # @see PDK::Context::AbstractContext.display_name
10 | def display_name
11 | 'an unknown context'
12 | end
13 | # :nocov:
14 |
15 | # @see PDK::Context::AbstractContext.parent_context
16 | def parent_context
17 | # An unknown context has no parent
18 | nil
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/pdk/generate.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | autoload :DefinedType, 'pdk/generate/defined_type'
6 | autoload :Module, 'pdk/generate/module'
7 | autoload :Provider, 'pdk/generate/provider'
8 | autoload :PuppetClass, 'pdk/generate/puppet_class'
9 | autoload :PuppetObject, 'pdk/generate/puppet_object'
10 | autoload :Task, 'pdk/generate/task'
11 | autoload :Transport, 'pdk/generate/transport'
12 |
13 | def generators
14 | @generators ||= [
15 | PDK::Generate::DefinedType,
16 | PDK::Generate::Provider,
17 | PDK::Generate::PuppetClass,
18 | PDK::Generate::Task,
19 | PDK::Generate::Transport
20 | ].freeze
21 | end
22 | module_function :generators
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/lib/pdk/generate/defined_type.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class DefinedType < PuppetObject
6 | PUPPET_STRINGS_TYPE = 'defined_types'.freeze
7 |
8 | def initialize(*_args)
9 | super
10 | object_name_parts = @object_name.split('::')
11 |
12 | @object_name = if object_name_parts.first == module_name
13 | object_name
14 | else
15 | [module_name, object_name].join('::')
16 | end
17 | end
18 |
19 | def friendly_name
20 | 'Defined Type'.freeze
21 | end
22 |
23 | def template_files
24 | # Calculate the defined type tests name
25 | define_name_parts = object_name.split('::')
26 | # drop the module name if the object name contains multiple parts
27 | define_name_parts.delete_at(0) if define_name_parts.length > 1
28 | files = { 'defined_type_spec.erb' => "#{File.join('spec', 'defines', *define_name_parts)}_spec.rb" }
29 | return files if spec_only?
30 |
31 | define_name_parts = object_name.split('::')[1..]
32 | define_name_parts << 'init' if define_name_parts.empty?
33 | files['defined_type.erb'] = "#{File.join('manifests', *define_name_parts)}.pp"
34 |
35 | files
36 | end
37 |
38 | def template_data
39 | { name: object_name }
40 | end
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/pdk/generate/fact.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class Fact < PuppetObject
6 | def friendly_name
7 | 'Custom Fact'.freeze
8 | end
9 |
10 | def template_files
11 | files = {
12 | 'fact_spec.erb' => "#{File.join('spec', 'unit', 'facter', object_name)}_spec.rb"
13 | }
14 | return files if spec_only?
15 |
16 | files.merge(
17 | 'fact.erb' => "#{File.join('lib', 'facter', object_name)}.rb"
18 | )
19 | end
20 |
21 | def template_data
22 | { name: object_name }
23 | end
24 | end
25 | end
26 | end
27 |
--------------------------------------------------------------------------------
/lib/pdk/generate/function.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class Function < PuppetObject
6 | def initialize(*_args)
7 | super
8 | object_name_parts = @object_name.split('::')
9 |
10 | @object_name = if object_name_parts.first == module_name
11 | object_name
12 | else
13 | [module_name, object_name].join('::')
14 | end
15 | end
16 |
17 | def friendly_name
18 | 'Function'.freeze
19 | end
20 |
21 | def template_files
22 | # Calculate the function tests name
23 | func_name_parts = object_name.split('::')
24 | # Drop the module name if the object name contains multiple parts
25 | func_name_parts.delete_at(0) if func_name_parts.length > 1
26 | files = {
27 | File.join('functions', 'function_spec.erb') => "#{File.join('spec', 'functions', *func_name_parts)}_spec.rb"
28 | }
29 | return files if spec_only?
30 |
31 | func_name_parts = object_name.split('::')[1..]
32 | template_file = File.join('functions', "#{options[:type]}_function.erb")
33 |
34 | files[template_file] = if options[:type].eql?('v4')
35 | "#{File.join('lib', 'puppet', 'functions', module_name, *func_name_parts)}.rb"
36 | else
37 | "#{File.join('functions', *func_name_parts)}.pp"
38 | end
39 | files
40 | end
41 |
42 | def template_data
43 | func_name = object_name.split('::').last
44 | namespace = object_name.split('::')[0...-1].join('::')
45 | { name: object_name, func_name:, namespace: }
46 | end
47 | end
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/lib/pdk/generate/provider.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class Provider < PuppetObject
6 | def friendly_name
7 | 'Resource API Provider'.freeze
8 | end
9 |
10 | def template_files
11 | files = {
12 | 'provider_spec.erb' => "#{File.join('spec', 'unit', 'puppet', 'provider', object_name, object_name)}_spec.rb",
13 | 'provider_type_spec.erb' => "#{File.join('spec', 'unit', 'puppet', 'type', object_name)}_spec.rb"
14 | }
15 | return files if spec_only?
16 |
17 | files.merge(
18 | 'provider.erb' => "#{File.join('lib', 'puppet', 'provider', object_name, object_name)}.rb",
19 | 'provider_type.erb' => "#{File.join('lib', 'puppet', 'type', object_name)}.rb"
20 | )
21 | end
22 |
23 | def template_data
24 | { name: object_name,
25 | provider_class: class_name_from_object_name(object_name) }
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/pdk/generate/puppet_class.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class PuppetClass < PuppetObject
6 | PUPPET_STRINGS_TYPE = 'puppet_classes'.freeze
7 |
8 | def initialize(*_args)
9 | super
10 | object_name_parts = @object_name.split('::')
11 |
12 | @object_name = if object_name_parts.first == module_name
13 | object_name
14 | else
15 | [module_name, object_name].join('::')
16 | end
17 | end
18 |
19 | def friendly_name
20 | 'Puppet Class'.freeze
21 | end
22 |
23 | def template_files
24 | # Calculate the class tests name
25 | class_name_parts = object_name.split('::')
26 | # Drop the module name if the object name contains multiple parts
27 | class_name_parts.delete_at(0) if class_name_parts.length > 1
28 | files = { 'class_spec.erb' => "#{File.join('spec', 'classes', *class_name_parts)}_spec.rb" }
29 | return files if spec_only?
30 |
31 | class_name_parts = object_name.split('::')[1..]
32 | class_name_parts << 'init' if class_name_parts.empty?
33 | files['class.erb'] = "#{File.join('manifests', *class_name_parts)}.pp"
34 |
35 | files
36 | end
37 |
38 | def template_data
39 | { name: object_name }
40 | end
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/pdk/generate/task.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class Task < PuppetObject
6 | def friendly_name
7 | 'Task'
8 | end
9 |
10 | def template_files
11 | return {} if spec_only?
12 |
13 | {
14 | 'task.erb' => File.join('tasks', "#{task_name}.sh")
15 | }
16 | end
17 |
18 | def template_data
19 | {
20 | name: object_name
21 | }
22 | end
23 |
24 | # Checks that the task has not already been defined with a different
25 | # extension.
26 | #
27 | # @raise [PDK::CLI::ExitWithError] if files with the same name as the
28 | # task exist in the /tasks/ directory
29 | def check_preconditions
30 | super
31 |
32 | error = "A task named '%{name}' already exists in this module; defined in %{file}"
33 | allowed_extensions = ['.md', '.conf']
34 |
35 | PDK::Util::Filesystem.glob(File.join(context.root_path, 'tasks', "#{task_name}.*")).each do |file|
36 | next if allowed_extensions.include?(File.extname(file))
37 |
38 | raise PDK::CLI::ExitWithError, format(error, name: task_name, file:)
39 | end
40 | end
41 |
42 | def non_template_files
43 | task_metadata_file = File.join('tasks', "#{task_name}.json")
44 | { task_metadata_file => JSON.pretty_generate(task_metadata) }
45 | end
46 |
47 | private
48 |
49 | # Calculates the file name of the task files ('init' if the task has the
50 | # same name as the module, otherwise use the specified task name).
51 | #
52 | # @return [String] the base name of the file(s) for the task.
53 | #
54 | # @api private
55 | def task_name
56 | object_name == module_name ? 'init' : object_name
57 | end
58 |
59 | def task_metadata
60 | {
61 | puppet_task_version: 1,
62 | supports_noop: false,
63 | description: options.fetch(:description, 'A short description of this task'),
64 | parameters: {}
65 | }
66 | end
67 | end
68 | end
69 | end
70 |
--------------------------------------------------------------------------------
/lib/pdk/generate/transport.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Generate
5 | class Transport < PuppetObject
6 | def friendly_name
7 | 'Resource API Transport'.freeze
8 | end
9 |
10 | def template_files
11 | # NOTE: Due to how the V1 templates work, the names of the source template files may be mismatched to
12 | # their destination, e.g. transport_type.erb is really a transport schema
13 | files = {
14 | 'transport_spec.erb' => "#{File.join('spec', 'unit', 'puppet', 'transport', object_name)}_spec.rb",
15 | 'transport_type_spec.erb' => "#{File.join('spec', 'unit', 'puppet', 'transport', 'schema', object_name)}_spec.rb"
16 | }
17 | return files if spec_only?
18 |
19 | files.merge(
20 | 'transport.erb' => "#{File.join('lib', 'puppet', 'transport', object_name)}.rb",
21 | 'transport_device.erb' => File.join('lib', 'puppet', 'util', 'network_device', object_name, 'device.rb'),
22 | 'transport_type.erb' => "#{File.join('lib', 'puppet', 'transport', 'schema', object_name)}.rb"
23 | )
24 | end
25 |
26 | def template_data
27 | {
28 | name: object_name,
29 | transport_class: class_name_from_object_name(object_name)
30 | }
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/lib/pdk/logger.rb:
--------------------------------------------------------------------------------
1 | require 'logger'
2 | require 'pdk'
3 |
4 | module PDK
5 | class Logger < ::Logger
6 | WRAP_COLUMN_LIMIT = 78
7 |
8 | def initialize
9 | super($stderr)
10 | @sent_messages = {}
11 |
12 | # TODO: Decide on output format.
13 | self.formatter = proc do |severity, _datetime, _progname, msg|
14 | prefix = "pdk (#{severity}): "
15 | if msg.is_a?(Hash)
16 | if msg.fetch(:wrap, false)
17 | wrap_pattern = /(.{1,#{WRAP_COLUMN_LIMIT - prefix.length}})(\s+|\Z)/
18 | "#{prefix}#{msg[:text].gsub(wrap_pattern, "\\1\n#{' ' * prefix.length}")}\n"
19 | else
20 | "#{prefix}#{msg[:text]}\n"
21 | end
22 | else
23 | "#{prefix}#{msg}\n"
24 | end
25 | end
26 |
27 | self.level = ::Logger::INFO
28 | end
29 |
30 | def warn_once(*args)
31 | hash = args.inspect.hash
32 | return if (@sent_messages[::Logger::WARN] ||= {}).key?(hash)
33 |
34 | @sent_messages[::Logger::WARN][hash] = true
35 | warn(*args)
36 | end
37 |
38 | def enable_debug_output
39 | self.level = ::Logger::DEBUG
40 | end
41 |
42 | def debug?
43 | level == ::Logger::DEBUG
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/pdk/module.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module Module
3 | autoload :Build, 'pdk/module/build'
4 | autoload :Convert, 'pdk/module/convert'
5 | autoload :Metadata, 'pdk/module/metadata'
6 | autoload :Release, 'pdk/module/release'
7 | autoload :UpdateManager, 'pdk/module/update_manager'
8 | autoload :Update, 'pdk/module/update'
9 |
10 | DEFAULT_IGNORED = [
11 | '/pkg/',
12 | '~*',
13 | '/coverage',
14 | '/checksums.json',
15 | '/REVISION',
16 | '/spec/fixtures/modules/',
17 | '/vendor/',
18 | '.DS_Store'
19 | ].freeze
20 |
21 | def default_ignored_pathspec(ignore_dotfiles = true)
22 | require 'pathspec'
23 |
24 | PathSpec.new(DEFAULT_IGNORED).tap do |ps|
25 | ps.add('.*') if ignore_dotfiles
26 | end
27 | end
28 | module_function :default_ignored_pathspec
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/lib/pdk/template.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Template
5 | autoload :Fetcher, 'pdk/template/fetcher'
6 | autoload :Renderer, 'pdk/template/renderer'
7 | autoload :TemplateDir, 'pdk/template/template_dir'
8 |
9 | MODULE_TEMPLATE_TYPE = :module_template
10 |
11 | # Creates a TemplateDir object with the path or URL to the template
12 | # and the block of code to run to be run while the template is available.
13 | #
14 | # The template directory is only guaranteed to be available on disk
15 | # within the scope of the block passed to this method.
16 | #
17 | # @param uri [PDK::Util::TemplateURI] The path to a directory to use as the
18 | # template or a URI to a git repository.
19 | #
20 | # @param context [PDK::Context::AbstractContext] The context in which the template will render to
21 | #
22 | # @yieldparam self [PDK::Template::TemplateDir] The initialised object with
23 | # the template available on disk.
24 | #
25 | # @example Using a git repository as a template
26 | # PDK::Template.with('https://github.com/puppetlabs/pdk-templates') do |t|
27 | # t.render_module('module, PDK.context) do |filename, content, status|
28 | # File.open(filename, 'w') do |file|
29 | # ...
30 | # end
31 | # end
32 | # end
33 | #
34 | # @raise [ArgumentError] If no block is given to this method.
35 | # @raise [PDK::CLI::FatalError]
36 | # @raise [ArgumentError]
37 | #
38 | # @api public
39 | def self.with(uri, context)
40 | raise ArgumentError, format('%{class_name}.with must be passed a block.', class_name: name) unless block_given?
41 | raise ArgumentError, format('%{class_name}.with must be passed a PDK::Util::TemplateURI, got a %{uri_type}', uri_type: uri.class, class_name: name) unless uri.is_a? PDK::Util::TemplateURI
42 |
43 | Fetcher.with(uri) do |fetcher|
44 | template_dir = TemplateDir.instance(uri, fetcher.path, context)
45 | template_dir.metadata = fetcher.metadata
46 |
47 | yield template_dir
48 | end
49 | nil
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/pdk/template/fetcher/local.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Template
5 | module Fetcher
6 | class Local < PDK::Template::Fetcher::AbstractFetcher
7 | # Whether the passed uri is fetchable. This is a catch-all and all URIs
8 | # are considered on-disk already.
9 | #
10 | # @see PDK::Template::Fetcher.instance
11 | # @return [Boolean]
12 | def self.fetchable?(_uri, _options = {})
13 | true
14 | end
15 |
16 | # @see PDK::Template::Fetcher::AbstractTemplateFetcher.fetch!
17 | def fetch!
18 | return if fetched
19 |
20 | super
21 |
22 | @path = uri.shell_path
23 | @temporary = false
24 | @metadata['template-url'] = uri.bare_uri
25 | end
26 | end
27 | end
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/pdk/template/renderer/v1.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Template
5 | module Renderer
6 | module V1
7 | autoload :LegacyTemplateDir, 'pdk/template/renderer/v1/legacy_template_dir'
8 | autoload :Renderer, 'pdk/template/renderer/v1/renderer'
9 | autoload :TemplateFile, 'pdk/template/renderer/v1/template_file'
10 |
11 | # Whether the template directory and context are valid for the V1 renderer
12 | # @see PDK::Template::Renderer.instance
13 | def self.compatible?(template_root, _context)
14 | ['moduleroot', 'moduleroot_init'].all? { |dir| PDK::Util::Filesystem.directory?(File.join(template_root, dir)) }
15 | end
16 |
17 | # Creates an instance of the V1 Renderer
18 | # @see PDK::Template::Renderer.instance
19 | def self.instance(template_root, template_uri, context)
20 | PDK::Template::Renderer::V1::Renderer.new(template_root, template_uri, context)
21 | end
22 | end
23 | end
24 | end
25 | end
26 |
--------------------------------------------------------------------------------
/lib/pdk/util/env.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 | require 'forwardable'
3 |
4 | module PDK
5 | module Util
6 | class Env
7 | class WindowsEnv
8 | extend Forwardable
9 |
10 | # Note, these delegators may not have case insensitive keys
11 | def_delegators :env_hash, :fetch, :select, :reject
12 |
13 | def []=(key, value)
14 | PDK::Util::Windows::Process.set_environment_variable(key, value)
15 | end
16 |
17 | def key?(key)
18 | !env_hash.keys.find { |item| key.casecmp(item).zero? }.nil?
19 | end
20 |
21 | def [](key)
22 | env_hash.each do |item, value|
23 | next unless key.casecmp(item).zero?
24 |
25 | return value
26 | end
27 | nil
28 | end
29 |
30 | private
31 |
32 | def env_hash
33 | PDK::Util::Windows::Process.environment_hash
34 | end
35 | end
36 |
37 | class << self
38 | extend Forwardable
39 |
40 | def_delegators :implementation, :key?, :[], :[]=, :fetch, :select, :reject
41 |
42 | def implementation
43 | @implementation ||= Gem.win_platform? ? WindowsEnv.new : ENV
44 | end
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/pdk/util/version.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Util
5 | module Version
6 | def self.version_string
7 | require 'pdk/version'
8 |
9 | "#{PDK::VERSION} #{pdk_ref}".strip.freeze
10 | end
11 |
12 | def self.pdk_ref
13 | ref = "#{pkg_sha} #{git_ref}".strip
14 | ref.empty? ? nil : "(#{ref})"
15 | end
16 |
17 | def self.pkg_sha
18 | if version_file && PDK::Util::Filesystem.exist?(version_file)
19 | ver = PDK::Util::Filesystem.read_file(version_file)
20 | sha = ver.strip.split('.')[5] unless ver.nil?
21 | end
22 |
23 | sha
24 | end
25 |
26 | def self.git_ref
27 | require 'pdk/util/git'
28 | source_git_dir = File.join(PDK::Util::Filesystem.expand_path('../../..', File.dirname(__FILE__)), '.git')
29 |
30 | return unless PDK::Util::Filesystem.directory?(source_git_dir)
31 |
32 | PDK::Util::Git.describe(source_git_dir)
33 | end
34 |
35 | def self.version_file
36 | require 'pdk/util'
37 |
38 | # FIXME: this gets called a LOT and doesn't currently get cached
39 | PDK::Util.find_upwards('PDK_VERSION', File.dirname(__FILE__))
40 | end
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/pdk/util/windows.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | module Util
3 | module Windows
4 | WIN32_FALSE = 0
5 | module File; end
6 |
7 | if Gem.win_platform?
8 | require 'pdk/util/windows/api_types'
9 | require 'pdk/util/windows/string'
10 | require 'pdk/util/windows/file'
11 | require 'pdk/util/windows/process'
12 | end
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/pdk/util/windows/file.rb:
--------------------------------------------------------------------------------
1 | require 'pdk/util/windows'
2 |
3 | module PDK
4 | module Util
5 | module Windows
6 | module File
7 | require 'ffi'
8 | extend FFI::Library
9 | extend PDK::Util::Windows::String
10 |
11 | def get_long_pathname(path)
12 | converted = ''
13 | FFI::Pointer.from_string_to_wide_string(path) do |path_ptr|
14 | # includes terminating NULL
15 | buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0)
16 | FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr|
17 | raise 'Failed to call GetLongPathName' if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == PDK::Util::Windows::WIN32_FALSE
18 |
19 | converted = converted_ptr.read_wide_string(buffer_size - 1)
20 | end
21 | end
22 |
23 | converted
24 | end
25 | module_function :get_long_pathname
26 |
27 | ffi_convention :stdcall
28 |
29 | # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
30 | # DWORD WINAPI GetLongPathName(
31 | # _In_ LPCTSTR lpszShortPath,
32 | # _Out_ LPTSTR lpszLongPath,
33 | # _In_ DWORD cchBuffer
34 | # );
35 | ffi_lib :kernel32
36 | attach_function :GetLongPathNameW, [:lpcwstr, :lpwstr, :dword], :dword
37 | end
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/pdk/util/windows/string.rb:
--------------------------------------------------------------------------------
1 | require 'pdk/util/windows'
2 |
3 | module PDK
4 | module Util
5 | module Windows
6 | module String
7 | def wide_string(str)
8 | # if given a nil string, assume caller wants to pass a nil pointer to win32
9 | return if str.nil?
10 |
11 | # ruby (< 2.1) does not respect multibyte terminators, so it is possible
12 | # for a string to contain a single trailing null byte, followed by garbage
13 | # causing buffer overruns.
14 | #
15 | # See http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=41920&view=revision
16 | newstr = str + "\0".encode(str.encoding)
17 | newstr.encode!('UTF-16LE')
18 | end
19 | module_function :wide_string
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/pdk/validate/control_repo/control_repo_validator_group.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module ControlRepo
6 | class ControlRepoValidatorGroup < ValidatorGroup
7 | def name
8 | 'control-repo'
9 | end
10 |
11 | def valid_in_context?
12 | context.is_a?(PDK::Context::ControlRepo)
13 | end
14 |
15 | def validators
16 | [
17 | EnvironmentConfValidator
18 | ].freeze
19 | end
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/pdk/validate/metadata/metadata_syntax_validator.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Metadata
6 | class MetadataSyntaxValidator < InternalRubyValidator
7 | def name
8 | 'metadata-syntax'
9 | end
10 |
11 | def pattern
12 | contextual_pattern(['metadata.json', 'tasks/*.json'])
13 | end
14 |
15 | def spinner_text
16 | format('Checking metadata syntax (%{patterns}).', patterns: pattern.join(' '))
17 | end
18 |
19 | def invoke(report)
20 | super
21 | ensure
22 | JSON.parser = JSON::Ext::Parser if defined?(JSON::Ext::Parser)
23 | end
24 |
25 | def validate_target(report, target)
26 | unless PDK::Util::Filesystem.readable?(target)
27 | report.add_event(
28 | file: target,
29 | source: name,
30 | state: :failure,
31 | severity: 'error',
32 | message: 'Could not be read.'
33 | )
34 | return 1
35 | end
36 |
37 | begin
38 | JSON.parse(PDK::Util::Filesystem.read_file(target))
39 |
40 | report.add_event(
41 | file: target,
42 | source: name,
43 | state: :passed,
44 | severity: 'ok'
45 | )
46 | 0
47 | rescue JSON::ParserError => e
48 | # Because the message contains a raw segment of the file, we use
49 | # String#dump here to unescape any escape characters like newlines.
50 | sane_message = e.message.dump
51 |
52 | report.add_event(
53 | file: target,
54 | source: name,
55 | state: :failure,
56 | severity: 'error',
57 | message: sane_message
58 | )
59 | 1
60 | end
61 | end
62 | end
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/lib/pdk/validate/metadata/metadata_validator_group.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Metadata
6 | class MetadataValidatorGroup < ValidatorGroup
7 | def name
8 | 'metadata'
9 | end
10 |
11 | def validators
12 | [
13 | MetadataSyntaxValidator,
14 | MetadataJSONLintValidator
15 | ].freeze
16 | end
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/pdk/validate/puppet/puppet_lint_validator.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Puppet
6 | class PuppetLintValidator < ExternalCommandValidator
7 | def name
8 | 'puppet-lint'
9 | end
10 |
11 | def cmd
12 | 'puppet-lint'
13 | end
14 |
15 | def pattern
16 | contextual_pattern('**/*.pp')
17 | end
18 |
19 | def spinner_text_for_targets(_targets)
20 | format('Checking Puppet manifest style (%{pattern}).', pattern: pattern.join(' '))
21 | end
22 |
23 | def parse_options(targets)
24 | cmd_options = ['--json', '--relative']
25 |
26 | cmd_options << '--fix' if options[:auto_correct]
27 |
28 | cmd_options.concat(targets)
29 | end
30 |
31 | def parse_output(report, result, targets)
32 | begin
33 | json_data = JSON.parse(result[:stdout]).flatten
34 | rescue JSON::ParserError
35 | raise PDK::Validate::ParseOutputError, result[:stdout]
36 | end
37 |
38 | # puppet-lint does not include files without problems in its JSON
39 | # output, so we need to go through the list of targets and add passing
40 | # events to the report for any target not listed in the JSON output.
41 | targets.reject { |target| json_data.any? { |j| j['path'] == target } }.each do |target|
42 | report.add_event(
43 | file: target,
44 | source: name,
45 | severity: 'ok',
46 | state: :passed
47 | )
48 | end
49 |
50 | json_data.each do |offense|
51 | report.add_event({
52 | file: offense['path'],
53 | source: name,
54 | line: offense['line'],
55 | column: offense['column'],
56 | message: offense['message'],
57 | test: offense['check'],
58 | severity: offense['kind'] == 'fixed' ? 'corrected' : offense['kind'],
59 | state: :failure
60 | })
61 | end
62 | end
63 | end
64 | end
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/lib/pdk/validate/puppet/puppet_plan_syntax_validator.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Puppet
6 | class PuppetPlanSyntaxValidator < PuppetSyntaxValidator
7 | def name
8 | 'puppet-plan-syntax'
9 | end
10 |
11 | def pattern
12 | contextual_pattern('plans/**/*.pp')
13 | end
14 |
15 | def pattern_ignore; end
16 |
17 | def spinner_text_for_targets(_targets)
18 | format('Checking Puppet plan syntax (%{pattern}).', pattern: pattern.join(' '))
19 | end
20 |
21 | def parse_options(targets)
22 | # Due to PDK-1266 we need to run `puppet parser validate` with an empty
23 | # modulepath. On *nix, Ruby treats `/dev/null` as an empty directory
24 | # however it doesn't do so with `NUL` on Windows. The workaround for
25 | # this to ensure consistent behaviour is to create an empty temporary
26 | # directory and use that as the modulepath.
27 | ['parser', 'validate', '--tasks', '--config', null_file, '--modulepath', validate_tmpdir].concat(targets)
28 | end
29 |
30 | def validate_tmpdir
31 | require 'tmpdir'
32 |
33 | @validate_tmpdir ||= Dir.mktmpdir('puppet-plan-parser-validate')
34 | end
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/pdk/validate/puppet/puppet_validator_group.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Puppet
6 | class PuppetValidatorGroup < ValidatorGroup
7 | def name
8 | 'puppet'
9 | end
10 |
11 | def validators
12 | [
13 | PuppetSyntaxValidator,
14 | PuppetPlanSyntaxValidator,
15 | PuppetLintValidator,
16 | PuppetEPPValidator
17 | ].freeze
18 | end
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/pdk/validate/ruby/ruby_rubocop_validator.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Ruby
6 | class RubyRubocopValidator < ExternalCommandValidator
7 | def allow_empty_targets?
8 | true
9 | end
10 |
11 | def name
12 | 'rubocop'
13 | end
14 |
15 | def cmd
16 | 'rubocop'
17 | end
18 |
19 | def pattern
20 | if context.is_a?(PDK::Context::ControlRepo)
21 | ['Puppetfile', '**/**.rb']
22 | else
23 | '**/**.rb'
24 | end
25 | end
26 |
27 | def spinner_text_for_targets(_targets)
28 | format('Checking Ruby code style (%{pattern}).', pattern:)
29 | end
30 |
31 | def parse_options(targets)
32 | cmd_options = ['--format', 'json']
33 |
34 | cmd_options << '--auto-correct' if options[:auto_correct]
35 |
36 | cmd_options.concat(targets)
37 | end
38 |
39 | def parse_output(report, result, _targets)
40 | return if result[:stdout].empty?
41 |
42 | begin
43 | json_data = JSON.parse(result[:stdout])
44 | rescue JSON::ParserError
45 | raise PDK::Validate::ParseOutputError, result[:stdout]
46 | end
47 |
48 | return unless json_data.key?('files')
49 |
50 | json_data['files'].each do |file_info|
51 | next unless file_info.key?('offenses')
52 |
53 | result = {
54 | file: file_info['path'],
55 | source: 'rubocop'
56 | }
57 |
58 | if file_info['offenses'].empty?
59 | report.add_event(result.merge(state: :passed, severity: :ok))
60 | else
61 | file_info['offenses'].each do |offense|
62 | report.add_event(
63 | result.merge(
64 | line: offense['location']['line'],
65 | column: offense['location']['column'],
66 | message: offense['message'],
67 | severity: offense['corrected'] ? 'corrected' : offense['severity'],
68 | test: offense['cop_name'],
69 | state: :failure
70 | )
71 | )
72 | end
73 | end
74 | end
75 | end
76 | end
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/lib/pdk/validate/ruby/ruby_validator_group.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Ruby
6 | class RubyValidatorGroup < ValidatorGroup
7 | def name
8 | 'ruby'
9 | end
10 |
11 | def validators
12 | [
13 | RubyRubocopValidator
14 | ].freeze
15 | end
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/pdk/validate/tasks/tasks_name_validator.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Tasks
6 | class TasksNameValidator < InternalRubyValidator
7 | INVALID_TASK_MSG = 'Invalid task name. Task names must start with a lowercase letter and can only contain lowercase letters, numbers, and underscores.'.freeze
8 |
9 | def name
10 | 'task-name'
11 | end
12 |
13 | def pattern
14 | contextual_pattern('tasks/**/*')
15 | end
16 |
17 | def spinner_text
18 | format('Checking task names (%{pattern}).', pattern: pattern.join(' '))
19 | end
20 |
21 | def validate_target(report, target)
22 | task_name = File.basename(target, File.extname(target))
23 | if PDK::CLI::Util::OptionValidator.valid_task_name?(task_name)
24 | report.add_event(
25 | file: target,
26 | source: name,
27 | state: :passed,
28 | severity: 'ok'
29 | )
30 | 0
31 | else
32 | report.add_event(
33 | file: target,
34 | source: name,
35 | state: :failure,
36 | severity: 'error',
37 | message: INVALID_TASK_MSG
38 | )
39 | 1
40 | end
41 | end
42 | end
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/lib/pdk/validate/tasks/tasks_validator_group.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module Tasks
6 | class TasksValidatorGroup < ValidatorGroup
7 | def name
8 | 'tasks'
9 | end
10 |
11 | def validators
12 | [
13 | TasksNameValidator,
14 | TasksMetadataLintValidator
15 | ].freeze
16 | end
17 | end
18 | end
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/lib/pdk/validate/yaml/yaml_validator_group.rb:
--------------------------------------------------------------------------------
1 | require 'pdk'
2 |
3 | module PDK
4 | module Validate
5 | module YAML
6 | class YAMLValidatorGroup < ValidatorGroup
7 | def name
8 | 'yaml'
9 | end
10 |
11 | def validators
12 | [
13 | YAMLSyntaxValidator
14 | ].freeze
15 | end
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/pdk/version.rb:
--------------------------------------------------------------------------------
1 | module PDK
2 | VERSION = '3.4.0'.freeze
3 | TEMPLATE_REF = '3.4.0.3'.freeze
4 | end
5 |
--------------------------------------------------------------------------------
/package-testing/.gitignore:
--------------------------------------------------------------------------------
1 | # bundler items
2 | /.bundle/
3 | /vendor/
4 | /Gemfile.lock
5 |
6 | # beaker output directories
7 | /tmp/
8 | /log/
9 | /repo-config/
10 | /acceptance_hosts.yml
11 | /junit/
12 | /archive/
13 | /sut-files.tgz
14 |
--------------------------------------------------------------------------------
/package-testing/Gemfile:
--------------------------------------------------------------------------------
1 | source ENV.fetch('GEM_SOURCE', nil) || 'https://rubygems.org'
2 |
3 | gem 'beaker', '~> 4.39'
4 | gem 'beaker-abs', '~> 0.11.0'
5 | gem 'beaker-docker', '~> 2'
6 | gem 'beaker-hostgenerator', '~> 2.11.0'
7 | gem 'beaker-puppet', '= 1.29.0'
8 | gem 'beaker-rspec', '= 7.1.0'
9 | gem 'beaker-vmpooler', '= 1.4.0'
10 | gem 'nokogiri', '~> 1.13.6'
11 | gem 'rake'
12 |
13 | # net-ping has a implicit dependency on win32-security
14 | gem 'win32-security', require: false if File::ALT_SEPARATOR
15 |
16 | group :development do
17 | gem 'pry'
18 | gem 'pry-stack_explorer'
19 | end
20 |
--------------------------------------------------------------------------------
/package-testing/config/options.rb:
--------------------------------------------------------------------------------
1 | {
2 | ssh: {
3 | keys: ['~/.ssh/id_rsa-acceptance'],
4 | verify_host_key: :never
5 | },
6 | preserve_hosts: 'onfail',
7 | provision: 'true'
8 | }
9 |
--------------------------------------------------------------------------------
/package-testing/lib/helper.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH << File.expand_path(__dir__)
2 |
--------------------------------------------------------------------------------
/package-testing/lib/pdk/pdk_helper.rb:
--------------------------------------------------------------------------------
1 | def install_dir(host)
2 | if host.platform.include?('windows')
3 | '/cygdrive/c/Program\ Files/Puppet\ Labs/DevelopmentKit'
4 | else
5 | '/opt/puppetlabs/pdk'
6 | end
7 | end
8 |
9 | def pdk_git_bin_dir(host)
10 | if host.platform.include?('windows')
11 | "#{install_dir(host)}/private/git/mingw64/bin"
12 | else
13 | "#{install_dir(host)}/private/git/bin"
14 | end
15 | end
16 |
17 | # Common way to just invoke 'pdk' on each platform
18 | def pdk_command(host, command, env = {})
19 | env ||= {}
20 | env_str = ''
21 |
22 | if host.platform.include?('windows')
23 | env.each do |var, val|
24 | env_str += "\\$env:#{var}='#{val}'; "
25 | end
26 |
27 | # Pass the command to powershell and exit powershell with pdk's exit code
28 | "powershell -Command \"#{env_str.tr('"', '\"').strip} pdk #{command.tr('"', '\"')}; exit $LASTEXITCODE\""
29 | else
30 | env.each do |var, val|
31 | env_str += "#{var}=#{val} "
32 | end
33 |
34 | "/bin/bash -lc \"#{env_str} pdk #{command}\""
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/package-testing/lib/testenv.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | RSpec.configure do |c|
4 | c.after(:suite) do
5 | puts "\nTarget host successfully provisioned with PDK:"
6 | puts "\n#{hosts.first.hostname}\n"
7 | end
8 | end
9 |
10 | describe 'validate PDK was successfully installed' do
11 | describe command('pdk --version') do
12 | its(:exit_status) { is_expected.to eq(0) }
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/package-testing/spec/package/add_gem_to_module_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'C100545 - Generate a module, add a gem to it, and validate it' do
4 | module_name = 'c100545_module'
5 |
6 | describe command("pdk new module #{module_name} --skip-interview --skip-interview --template-url=https://github.com/puppetlabs/pdk-templates --template-ref=main") do
7 | its(:exit_status) { is_expected.to eq(0) }
8 | end
9 |
10 | context 'when a new gem dependency has been added to the Gemfile' do
11 | before(:all) do
12 | shell("echo \"gem 'bolt'\" >> #{File.join(module_name, 'Gemfile')}")
13 | end
14 |
15 | describe command('pdk validate') do
16 | let(:cwd) { module_name }
17 |
18 | its(:exit_status) { is_expected.to eq(0) }
19 | end
20 |
21 | describe file(File.join(module_name, 'Gemfile.lock')) do
22 | it { is_expected.to exist }
23 |
24 | describe 'the content of the file' do
25 | subject { super().content }
26 |
27 | it 'differs from the vendored lockfile' do
28 | vendored_lockfile = File.join(install_dir, 'share', 'cache', 'Gemfile.lock')
29 | expect(subject).not_to eq(file(vendored_lockfile).content)
30 | end
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/package-testing/spec/package/airgapped_usage_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'Basic usage in an air-gapped environment' do
4 | module_name = 'airgapped_module'
5 |
6 | context 'with rubygems.org access disabled' do
7 | before(:all) do
8 | shell("cp #{hosts_file} #{hosts_file}.bak")
9 | shell("echo \"127.0.0.1 rubygems.org\" >> #{hosts_file}")
10 | end
11 |
12 | after(:all) do
13 | shell("cp #{hosts_file}.bak #{hosts_file}")
14 | end
15 |
16 | context 'when creating a new module' do
17 | describe command("pdk new module #{module_name} --skip-interview") do
18 | its(:exit_status) { is_expected.to eq(0) }
19 | end
20 |
21 | describe file(File.join(module_name, 'metadata.json')) do
22 | it { is_expected.to be_file }
23 |
24 | its(:content_as_json) do
25 | is_expected.to include('template-url' => a_string_matching(/pdk-templates#main/))
26 | end
27 | end
28 | end
29 |
30 | # If this test fails with a mismatch between the expected and actual Gemfile.lock content, check that the following
31 | # steps are up to date:
32 | # - Ensure that the pdk-templates main has been given an anotated (it must be annotated) tag with the version number,
33 | # if between releases add a fourth number to it, i.e. 3.2.0.3
34 | # - Ensure that the pdk version.rb is pointing to this tag
35 | # https://github.com/puppetlabs/pdk/blob/main/lib/pdk/version.rb
36 | # - Ensure that the pdk-vanagon template pin is up to date with the pdk-templates main commit
37 | context 'when validating the module' do
38 | context "with puppet #{PDK_VERSION[:latest][:major]}" do
39 | let(:ruby_version) { ruby_for_puppet(PDK_VERSION[:latest][:major]) }
40 |
41 | describe command("pdk validate --puppet-version=#{PDK_VERSION[:latest][:major]}") do
42 | let(:cwd) { module_name }
43 |
44 | its(:exit_status) { is_expected.to eq(0) }
45 | end
46 |
47 | describe file(File.join(module_name, 'Gemfile.lock')) do
48 | it { is_expected.to be_file }
49 |
50 | describe 'the content of the file' do
51 | subject { super().content.gsub(/^DEPENDENCIES.+?\n\n/m, '') }
52 |
53 | it 'is identical to the vendored lockfile' do
54 | vendored_lockfile = File.join(install_dir, 'share', 'cache', "Gemfile-#{ruby_version}.lock")
55 |
56 | expect(subject).to eq(file(vendored_lockfile).content.gsub(/^DEPENDENCIES.+?\n\n/m, ''))
57 | end
58 | end
59 | end
60 | end
61 | end
62 | end
63 | end
64 |
--------------------------------------------------------------------------------
/package-testing/spec/package/pdk_help_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'C100022 - pdk --help' do
4 | describe command('pdk --help') do
5 | its(:exit_status) { is_expected.to eq(0) }
6 | its(:stdout) { is_expected.to match(/NAME.*USAGE.*DESCRIPTION.*COMMANDS.*OPTIONS/m) }
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/package-testing/spec/package/support/serverspec_monkeypatch.rb:
--------------------------------------------------------------------------------
1 | require 'beaker-rspec'
2 |
3 | module Serverspec
4 | module Type
5 | class Command
6 | def run
7 | command_result
8 | end
9 | end
10 | end
11 | end
12 |
13 | option_keys = Specinfra::Configuration.singleton_class.const_get(:VALID_OPTIONS_KEYS).dup
14 | option_keys << :cwd
15 | option_keys << :run_as
16 |
17 | Specinfra::Configuration.singleton_class.send(:remove_const, :VALID_OPTIONS_KEYS) # rubocop:disable RSpec/RemoveConst
18 | Specinfra::Configuration.singleton_class.const_set(:VALID_OPTIONS_KEYS, option_keys.freeze)
19 | RSpec.configuration.add_setting :cwd
20 | RSpec.configuration.add_setting :run_as
21 |
22 | module Specinfra
23 | module Backend
24 | class BeakerCygwin
25 | old_create_script = instance_method(:create_script)
26 |
27 | define_method(:create_script) do |cmd|
28 | prepend_env(old_create_script.bind_call(self, cmd))
29 | end
30 |
31 | def prepend_env(script)
32 | cmd = []
33 |
34 | cmd << %(Set-Location -Path "#{get_config(:cwd)}") if get_config(:cwd)
35 | (get_config(:env) || {}).each do |k, v|
36 | cmd << %($env:#{k} = "#{v}")
37 | end
38 | cmd << script
39 |
40 | cmd.join("\n")
41 | end
42 | end
43 | end
44 | end
45 |
46 | module Specinfra
47 | module Backend
48 | class BeakerExec
49 | old_build_command = instance_method(:build_command)
50 |
51 | define_method(:build_command) do |cmd|
52 | if get_config(:cwd)
53 | cmd = cmd.shelljoin if cmd.is_a? Array
54 | cmd = "cd #{get_config(:cwd)} && #{cmd}"
55 | end
56 |
57 | prepend_env(old_build_command.bind_call(self, cmd))
58 | end
59 |
60 | def unescape(string)
61 | JSON.parse(%(["#{string}"])).first
62 | end
63 |
64 | def prepend_env(cmd)
65 | _, orig_env, orig_cmd = cmd.match(/\A(?:env (.+) )?(\S+(?: -i)?(?: -l)? -c .+)\Z/).to_a
66 |
67 | env = [orig_env].compact
68 | (get_config(:env) || {}).each do |k, v|
69 | env << %(#{k}="#{v}")
70 | end
71 |
72 | command = if env.empty?
73 | orig_cmd
74 | else
75 | "env #{env.join(' ')} #{orig_cmd}"
76 | end
77 |
78 | output = if get_config(:run_as)
79 | "su -l #{get_config(:run_as)} -c #{command.shellescape}"
80 | else
81 | command
82 | end
83 |
84 | $stderr.puts(output) if ENV.key?('BEAKER_debug')
85 | output
86 | end
87 | end
88 | end
89 | end
90 |
--------------------------------------------------------------------------------
/package-testing/spec/package/support/spec_utils.rb:
--------------------------------------------------------------------------------
1 | module SpecUtils
2 | def install_dir(cygpath = false)
3 | if windows_node?
4 | if cygpath
5 | '/cygdrive/c/Program\ Files/Puppet\ Labs/DevelopmentKit'
6 | else
7 | 'C:/Program Files/Puppet Labs/DevelopmentKit'
8 | end
9 | else
10 | '/opt/puppetlabs/pdk'
11 | end
12 | end
13 |
14 | def home_dir(cygpath = false)
15 | if windows_node?
16 | cygpath ? '/home/Administrator' : 'c:/cygwin64/home/Administrator'
17 | else
18 | get_working_node.external_copy_base
19 | end
20 | end
21 |
22 | def windows_node?
23 | get_working_node.platform.include?('windows')
24 | end
25 | module_function :windows_node?
26 |
27 | def git_bin
28 | path = File.join(install_dir, 'private', 'git')
29 | windows_node? ? "& '#{File.join(path, 'cmd', 'git.exe')}'" : File.join(path, 'bin', 'git')
30 | end
31 |
32 | def hosts_file
33 | if windows_node?
34 | '/cygdrive/c/Windows/System32/Drivers/etc/hosts'
35 | else
36 | '/etc/hosts'
37 | end
38 | end
39 |
40 | def ruby_cache_dir
41 | File.join(install_dir(true), 'private', 'ruby')
42 | end
43 |
44 | def latest_ruby
45 | installed_rubies = shell("cd #{ruby_cache_dir}; ls -dr *").stdout.split
46 | installed_rubies[0]
47 | end
48 |
49 | def ruby_for_puppet(pupver)
50 | ruby_pattern = case pupver
51 | when /^4/ then '2.1.*'
52 | when /^5/ then '2.4.*'
53 | when /^6/ then '2.5.*'
54 | when /^7/ then '2.7.*'
55 | when /^8/ then '3.2.*'
56 | end
57 |
58 | return unless ruby_pattern
59 |
60 | shell("cd #{ruby_cache_dir}; ls -dr #{ruby_pattern}").stdout.split.first
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/package-testing/spec/package/unit_test_a_new_module_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'Generate a module for unit testing' do
4 | module_name = 'unit_test_module'
5 |
6 | context 'when creating a new module and new class' do
7 | describe command("pdk new module #{module_name} --skip-interview") do
8 | its(:exit_status) { is_expected.to eq(0) }
9 | end
10 |
11 | describe command("pdk new class #{module_name}") do
12 | let(:cwd) { module_name }
13 |
14 | its(:exit_status) { is_expected.to eq(0) }
15 | end
16 |
17 | describe command('pdk new defined_type test_define') do
18 | let(:cwd) { module_name }
19 |
20 | its(:exit_status) { is_expected.to eq(0) }
21 | end
22 | end
23 |
24 | context 'when unit testing' do
25 | describe command('pdk test unit') do
26 | let(:cwd) { module_name }
27 |
28 | its(:exit_status) { is_expected.to eq(0) }
29 | its(:stdout) { is_expected.to match(/[1-9]\d* examples.*0 failures/im) }
30 | end
31 | end
32 |
33 | context 'when unit testing in parallel' do
34 | # Parallel tests gem is currently broken on Windows.
35 | describe command('pdk test unit --parallel'), unless: windows_node? do
36 | let(:cwd) { module_name }
37 |
38 | its(:exit_status) { is_expected.to eq(0) }
39 | its(:stdout) { is_expected.to match(/[1-9]\d* examples.*0 failures/im) }
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/package-testing/spec/package/unprivileged_user_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'Running PDK as an unprivileged user' do
4 | module_name = 'unprivileged_user'
5 |
6 | before(:all) do
7 | hosts.each do |host|
8 | next if host.platform.include?('windows')
9 |
10 | host.user_present('testuser')
11 |
12 | case host.platform
13 | when /osx/
14 | on(host, 'createhomedir -c -u testuser')
15 | else
16 | on(host, 'getent passwd testuser') do |result|
17 | _, _, uid, gid, _, homedir, = result.stdout.strip.split(':')
18 | on(host, "mkdir #{homedir} && chown #{uid}:#{gid} #{homedir}") unless directory_exists_on(host, homedir)
19 | end
20 | end
21 | end
22 | end
23 |
24 | let(:run_as) { 'testuser' }
25 |
26 | context 'when creating a new module and new class', unless: windows_node? do
27 | describe command('whoami') do
28 | its(:stdout) { is_expected.to contain('testuser') }
29 | end
30 |
31 | describe command("pdk new module #{module_name} --skip-interview --template-url=https://github.com/puppetlabs/pdk-templates --template-ref=main") do
32 | its(:exit_status) { is_expected.to eq(0) }
33 | end
34 |
35 | describe command("pdk new class #{module_name}") do
36 | let(:cwd) { module_name }
37 |
38 | its(:exit_status) { is_expected.to eq(0) }
39 | end
40 |
41 | describe command('pdk new defined_type test_define') do
42 | let(:cwd) { module_name }
43 |
44 | its(:exit_status) { is_expected.to eq(0) }
45 | end
46 | end
47 |
48 | context 'when unit testing', unless: windows_node? do
49 | describe command('pdk test unit') do
50 | let(:cwd) { module_name }
51 |
52 | its(:exit_status) { is_expected.to eq(0) }
53 | its(:stdout) { is_expected.to match(/[1-9]\d* examples.*0 failures/im) }
54 | end
55 | end
56 |
57 | context 'when unit testing in parallel', unless: windows_node? do
58 | describe command('pdk test unit --parallel') do
59 | let(:cwd) { module_name }
60 |
61 | its(:exit_status) { is_expected.to eq(0) }
62 | its(:stdout) { is_expected.to match(/[1-9]\d* examples.*0 failures/im) }
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/package-testing/spec/package/validate_a_new_module_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'C100321 - Generate a module and validate it (i.e. ensure bundle install works)' do
4 | module_name = 'c100321_module'
5 |
6 | context 'when creating a new module' do
7 | describe command("pdk new module #{module_name} --skip-interview") do
8 | its(:exit_status) { is_expected.to eq(0) }
9 | end
10 |
11 | describe file(File.join(module_name, 'metadata.json')) do
12 | it { is_expected.to be_file }
13 |
14 | its(:content_as_json) do
15 | is_expected.to include('template-url' => a_string_matching(/pdk-templates#main/))
16 | end
17 | end
18 | end
19 |
20 | # If this test fails with a mismatch between the expected and actual Gemfile.lock content, check that the following
21 | # steps are up to date:
22 | # - Ensure that the pdk-templates main has been given an anotated (it must be annotated) tag with the version number,
23 | # if between releases add a fourth number to it, i.e. 3.2.0.3
24 | # - Ensure that the pdk version.rb is pointing to this tag
25 | # https://github.com/puppetlabs/pdk/blob/main/lib/pdk/version.rb
26 | # - Ensure that the pdk-vanagon template pin is up to date with the pdk-templates main commit
27 | context 'when validating the module' do
28 | context "with puppet #{PDK_VERSION[:latest][:major]}" do
29 | let(:ruby_version) { ruby_for_puppet(PDK_VERSION[:latest][:major]) }
30 |
31 | describe command("pdk validate --puppet-version=#{PDK_VERSION[:latest][:major]}") do
32 | let(:cwd) { module_name }
33 |
34 | its(:exit_status) { is_expected.to eq(0) }
35 | end
36 |
37 | describe file(File.join(module_name, 'Gemfile.lock')) do
38 | it { is_expected.to be_file }
39 |
40 | describe 'the content of the file' do
41 | subject { super().content.gsub(/^DEPENDENCIES.+?\n\n/m, '') }
42 |
43 | it 'is identical to the vendored lockfile' do
44 | vendored_lockfile = File.join(install_dir, 'share', 'cache', "Gemfile-#{ruby_version}.lock")
45 |
46 | expect(subject).to eq(file(vendored_lockfile).content.gsub(/^DEPENDENCIES.+?\n\n/m, ''))
47 | end
48 | end
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/package-testing/spec/package/version_selection_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_package'
2 |
3 | describe 'Test puppet & ruby version selection' do
4 | module_name = 'version_selection'
5 | test_cases = [
6 | { envvar: 'PDK_PUPPET_VERSION', expected_puppet: PDK_VERSION[:latest][:major], expected_ruby: PDK_VERSION[:latest][:ruby] }
7 | ]
8 |
9 | before(:all) do
10 | command('pdk new module version_selection --skip-interview').run
11 | end
12 |
13 | test_cases.each do |test_case|
14 | context "Select Puppet #{test_case[:expected_puppet]}" do
15 | let(:env) { { test_case[:envvar] => test_case[:expected_puppet] } }
16 | let(:cwd) { module_name }
17 |
18 | let(:expected_puppet) { Regexp.escape(test_case[:expected_puppet]) }
19 | let(:expected_ruby) { Regexp.escape(test_case[:expected_ruby]) }
20 |
21 | describe command('rm Gemfile.lock; pdk bundle update --local') do
22 | its(:exit_status) { is_expected.to eq(0) }
23 | end
24 |
25 | describe command('pdk bundle exec puppet --version') do
26 | its(:exit_status) { is_expected.to eq(0) }
27 | its(:stderr) { is_expected.to match(/using puppet (#{expected_puppet}\.\d+\.\d+)/im) }
28 | its(:stdout) { is_expected.to match(/^(#{expected_puppet}\.\d+\.\d+)*/im) }
29 | end
30 |
31 | describe command('pdk bundle exec ruby --version') do
32 | its(:exit_status) { is_expected.to eq(0) }
33 | its(:stderr) { is_expected.to match(/using ruby #{expected_ruby}*/im) }
34 | its(:stdout) { is_expected.to match(/^(#{expected_ruby})*/im) }
35 | end
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/package-testing/spec/spec_helper_package.rb:
--------------------------------------------------------------------------------
1 | require 'beaker-rspec'
2 | require 'beaker-puppet'
3 |
4 | Dir['./spec/package/support/*.rb'].each { |f| require f }
5 |
6 | include SpecUtils # rubocop:disable Style/MixinUsage
7 |
8 | bin_path = SpecUtils.windows_node? ? 'bin$PATH' : 'bin:$PATH'
9 | set :path, "#{SpecUtils.install_dir}/#{bin_path}"
10 |
11 | # IMPORTANT: The following block should be updated with the version of ruby that is included within the newest
12 | # Puppet release for each major version. If you are running integration testing prior to a release and its
13 | # failing, verify that the following versions are correct.
14 | # Duplicates of this are found within spec_helper.rb and spec_helper_acceptance.rb and should be updated simultaneously.
15 | PDK_VERSION = {
16 | latest: {
17 | full: '8.6.0',
18 | major: '8',
19 | ruby: '3.2.*'
20 | },
21 | lts: {
22 | full: '8.6.0',
23 | major: '8',
24 | ruby: '3.2.*'
25 | }
26 | }.freeze
27 |
28 | RSpec.configure do |c|
29 | c.include SpecUtils
30 | c.extend SpecUtils
31 |
32 | c.before(:suite) do
33 | hosts.each do |host|
34 | PackageHelpers.install_pdk_on(host)
35 | end
36 | end
37 |
38 | # rubocop:disable RSpec/BeforeAfterAll
39 | c.before(:all) do
40 | RSpec.configuration.logger.log_level = :warn
41 | end
42 |
43 | c.after(:all) do
44 | RSpec.configuration.logger.log_level = :verbose
45 | end
46 | # rubocop:enable RSpec/BeforeAfterAll
47 |
48 | c.after do
49 | cmd = if windows_node?
50 | command('rm -Recurse -Force $env:LOCALAPPDATA/PDK/Cache/ruby')
51 | else
52 | command('rm -rf ~/.pdk/cache/ruby')
53 | end
54 |
55 | # clear out any cached gems
56 | cmd.run
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/pdk.gemspec:
--------------------------------------------------------------------------------
1 | lib = File.expand_path('lib', __dir__)
2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3 | require 'pdk/version'
4 |
5 | Gem::Specification.new do |spec|
6 | spec.name = 'pdk'
7 | spec.version = PDK::VERSION
8 | spec.authors = ['Puppet, Inc.']
9 | spec.email = ['pdk-maintainers@puppet.com']
10 |
11 | spec.summary = 'A key part of the Puppet Development Kit, the shortest path to better modules'
12 | spec.description = 'A CLI to facilitate easy, unified development workflows for Puppet modules.'
13 | spec.homepage = 'https://github.com/puppetlabs/pdk'
14 |
15 | spec.files = Dir['CHANGELOG.md', 'README.md', 'LICENSE', 'lib/**/*', 'exe/**/*']
16 | spec.bindir = 'exe'
17 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18 | spec.require_paths = ['lib']
19 |
20 | spec.required_ruby_version = '>= 3.1.0'
21 |
22 | # PDK Rubygems
23 | spec.add_dependency 'ffi', '>= 1.15.5', '< 2.0.0'
24 | spec.add_dependency 'minitar', '~> 0.8'
25 |
26 | # Bundler
27 | spec.add_dependency 'bundler', '>= 2.1.0', '< 3.0.0'
28 |
29 | # Cri and deps
30 | spec.add_dependency 'cri', '~> 2.15.11'
31 |
32 | # Childprocess and deps
33 | spec.add_dependency 'childprocess', '~> 5.0'
34 | spec.add_dependency 'hitimes', '2.0.0'
35 |
36 | ## root tty gems
37 | spec.add_dependency 'tty-prompt', '~> 0.23'
38 | spec.add_dependency 'tty-spinner', '~> 0.9'
39 | spec.add_dependency 'tty-which', '~> 0.5'
40 |
41 | # json-schema and deps
42 | spec.add_dependency 'json-schema', '~> 5.0'
43 |
44 | # PDK build
45 | spec.add_dependency 'puppet-modulebuilder', '~> 1.0'
46 |
47 | # Other deps
48 | spec.add_dependency 'deep_merge', '~> 1.2.2'
49 | spec.add_dependency 'diff-lcs', '>= 1.5.0'
50 | spec.add_dependency 'pathspec', '~> 1.1'
51 | spec.add_dependency 'puppet_forge', '~> 5.0'
52 |
53 | spec.metadata['rubygems_mfa_required'] = 'true'
54 | end
55 |
--------------------------------------------------------------------------------
/rakelib/test_pdk_as_library.rake:
--------------------------------------------------------------------------------
1 | require 'English'
2 |
3 | task :test_pdk_as_library do
4 | spec_dir = File.expand_path(File.join(__dir__, '..', 'spec'))
5 |
6 | Dir[File.join(spec_dir, 'unit', '**', '*_spec.rb')].each do |spec_file|
7 | system("bundle exec rspec #{spec_file}")
8 |
9 | raise unless $CHILD_STATUS.success?
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/spec/acceptance/get_config_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 | require 'fileutils'
3 |
4 | describe 'pdk get config' do
5 | include_context 'with a fake TTY'
6 |
7 | context 'when run outside of a module' do
8 | describe command('pdk get config') do
9 | its(:exit_status) { is_expected.to eq 0 }
10 | # This setting should appear in all pdk versions
11 | its(:stdout) { is_expected.to match(/user.pdk_feature_flags.available=/) }
12 | its(:stderr) { is_expected.to have_no_output }
13 | end
14 |
15 | describe command('pdk get config user.pdk_feature_flags.available') do
16 | its(:exit_status) { is_expected.to eq 0 }
17 | # This setting, and only, this setting should appear in output
18 | its(:stdout) { is_expected.to match('["controlrepo"]') }
19 | its(:stderr) { is_expected.to have_no_output }
20 | end
21 |
22 | describe command('pdk get config user.pdk_feature_flags') do
23 | its(:exit_status) { is_expected.to eq 0 }
24 | # There should be two configuration items returned
25 | its(:stdout) { expect(is_expected.target.split("\n").count).to eq(2) }
26 |
27 | its(:stdout) do
28 | result = is_expected.target.split("\n").sort
29 | expect(result[0]).to match('user.pdk_feature_flags.available=["controlrepo"]')
30 | expect(result[1]).to match(/user.pdk_feature_flags.requested=.+/)
31 | end
32 |
33 | its(:stderr) { is_expected.to have_no_output }
34 | end
35 |
36 | describe command('pdk get config does.not.exist') do
37 | its(:exit_status) { is_expected.not_to eq(0) }
38 | its(:stdout) { is_expected.to have_no_output }
39 | its(:stderr) { is_expected.to match(/does\.not\.exist/) }
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/spec/acceptance/get_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 | require 'fileutils'
3 |
4 | describe 'pdk get' do
5 | include_context 'with a fake TTY'
6 |
7 | context 'when run outside of a module' do
8 | describe command('pdk get') do
9 | its(:exit_status) { is_expected.to eq 0 }
10 | # Should show the command help
11 | its(:stdout) { is_expected.to match(/pdk get \[subcommand\] \[options\]/) }
12 | its(:stderr) { is_expected.to have_no_output }
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/acceptance/new_fact_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 |
3 | describe 'pdk new fact', :module_command do
4 | shared_examples 'it creates a fact' do |_options|
5 | context 'in a new module' do
6 | include_context 'in a new module', 'new_fact'
7 |
8 | context 'when creating the fact' do
9 | describe command('pdk new fact new_fact') do
10 | its(:exit_status) { is_expected.to eq 0 }
11 | its(:stdout) { is_expected.to match(/Files added/) }
12 | its(:stdout) { is_expected.to match(/#{File.join('lib', 'facter', 'new_fact.rb')}/) }
13 | its(:stdout) { is_expected.to match(/#{File.join('spec', 'unit', 'facter', 'new_fact_spec.rb')}/) }
14 | its(:stderr) { is_expected.to have_no_output }
15 |
16 | it_behaves_like 'it creates a fact',
17 | name: 'new_fact',
18 | file: File.join('lib', 'facter', 'new_fact.rb'),
19 | spec: File.join('spec', 'unit', 'facter', 'new_fact_spec.rb')
20 | end
21 | end
22 | end
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/acceptance/new_function_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 |
3 | describe 'pdk new function', :module_command do
4 | shared_examples 'it creates a function' do |_options|
5 | context 'in a new module' do
6 | include_context 'in a new module', 'new_function'
7 | context 'when creating the function' do
8 | describe command('pdk new function -t v4 abs') do
9 | its(:exit_status) { is_expected.to eq 0 }
10 | its(:stdout) { is_expected.to match(/Files added/) }
11 | its(:stdout) { is_expected.to match(/#{File.join('lib', 'puppet', 'functions', 'abs.rb')}/) }
12 | its(:stdout) { is_expected.to match(/#{File.join('dspec', 'functions', 'abs_spec.rb')}/) }
13 | its(:stderr) { is_expected.to have_no_output }
14 |
15 | it_behaves_like 'it creates a function',
16 | name: 'abs',
17 | file: File.join('lib', 'puppet', 'functions', 'abs.rb'),
18 | spec: File.join('spec', 'functions', 'abs_spec.rb')
19 | end
20 |
21 | describe command('pdk new function -t native abs') do
22 | its(:exit_status) { is_expected.to eq 0 }
23 | its(:stdout) { is_expected.to match(/Files added/) }
24 | its(:stdout) { is_expected.to match(/#{File.join('functions', 'abs.pp')}/) }
25 | its(:stdout) { is_expected.to match(/#{File.join('spec', 'functions', 'abs_spec.rb')}/) }
26 | its(:stderr) { is_expected.to have_no_output }
27 |
28 | it_behaves_like 'it creates a function',
29 | name: 'abs',
30 | file: File.join('functions', 'abs.rb'),
31 | spec: File.join('spec', 'functions', 'abs_spec.rb')
32 | end
33 | end
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/spec/acceptance/new_module_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 | require 'pdk/version'
3 |
4 | describe 'pdk new module' do
5 | context 'when the --skip-interview option is used' do
6 | after(:all) do
7 | FileUtils.rm_rf('new_module')
8 | end
9 |
10 | describe command('pdk new module new_module --skip-interview') do
11 | its(:exit_status) { is_expected.to eq 0 }
12 | its(:stderr) { is_expected.to match(/Creating new module: new_module/) }
13 | its(:stderr) { is_expected.not_to match(/WARN|ERR/) }
14 | its(:stdout) { is_expected.to have_no_output }
15 |
16 | describe file('new_module') do
17 | it { is_expected.to be_directory }
18 | end
19 |
20 | describe file(File.join('new_module', 'metadata.json')) do
21 | it { is_expected.to be_file }
22 |
23 | its(:content_as_json) do
24 | is_expected.to include(
25 | 'name' => match(/-new_module/),
26 | 'template-ref' => match(%r{(main-)|(^(tags/)?(\d+)\.(\d+)\.(\d+))}),
27 | 'operatingsystem_support' => include(
28 | 'operatingsystem' => 'Debian',
29 | 'operatingsystemrelease' => ['10', '11', '12']
30 | )
31 | )
32 | end
33 | end
34 |
35 | describe file(File.join('new_module', 'README.md')) do
36 | it { is_expected.to be_file }
37 | it { is_expected.to contain(/# new_module/i) }
38 | end
39 |
40 | describe file(File.join('new_module', 'CHANGELOG.md')) do
41 | it { is_expected.to be_file }
42 | it { is_expected.to contain(/## Release 0.1.0/i) }
43 | end
44 |
45 | eol_check = '(Get-Content .\new_module\spec\spec_helper.rb -Delimiter [String].Empty) -Match "`r`n"'
46 | describe command(eol_check), if: Gem.win_platform? do
47 | its(:stdout) { is_expected.to match(/\AFalse$/) }
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/spec/acceptance/new_test_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 |
3 | describe 'pdk new test', :module_command do
4 | context 'in a new module' do
5 | include_context 'in a new module', 'new_unit_test'
6 |
7 | before(:all) do
8 | File.open(File.join('manifests', 'init.pp'), 'w') do |fd|
9 | fd.puts 'class new_unit_test {}'
10 | end
11 |
12 | File.open(File.join('manifests', 'def_type.pp'), 'w') do |fd|
13 | fd.puts 'define new_unit_test::def_type() {}'
14 | end
15 | end
16 |
17 | context 'when creating a test for the main class' do
18 | describe command('pdk new test --unit new_unit_test') do
19 | its(:exit_status) { is_expected.to eq 0 }
20 | its(:stdout) { is_expected.to match(/#{File.join('spec', 'classes', 'new_unit_test_spec.rb')}/) }
21 | its(:stderr) { is_expected.to match(/Using Ruby/) }
22 | its(:stderr) { is_expected.to match(/Using Puppet/) }
23 |
24 | describe file(File.join('spec', 'classes', 'new_unit_test_spec.rb')) do
25 | it { is_expected.to be_file }
26 |
27 | its(:content) do
28 | is_expected.to match(/describe 'new_unit_test' do/)
29 | end
30 | end
31 | end
32 | end
33 |
34 | context 'when creating a test for a defined type' do
35 | describe command('pdk new test def_type') do
36 | its(:exit_status) { is_expected.to eq 0 }
37 | its(:stdout) { is_expected.to match(/#{File.join('spec', 'defines', 'def_type_spec.rb')}/) }
38 | its(:stderr) { is_expected.to match(/Using Ruby/) }
39 | its(:stderr) { is_expected.to match(/Using Puppet/) }
40 |
41 | describe file(File.join('spec', 'defines', 'def_type_spec.rb')) do
42 | it { is_expected.to be_file }
43 |
44 | its(:content) do
45 | is_expected.to match(/describe 'new_unit_test::def_type' do/)
46 | end
47 | end
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/spec/acceptance/remove_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 | require 'fileutils'
3 |
4 | describe 'pdk set' do
5 | include_context 'with a fake TTY'
6 |
7 | context 'when run outside of a module' do
8 | describe command('pdk set') do
9 | its(:exit_status) { is_expected.to eq 0 }
10 | # Should show the command help
11 | its(:stdout) { is_expected.to match(/pdk set \[subcommand\] \[options\]/) }
12 | its(:stderr) { is_expected.to have_no_output }
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/acceptance/set_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 | require 'fileutils'
3 |
4 | describe 'pdk set' do
5 | include_context 'with a fake TTY'
6 |
7 | context 'when run outside of a module' do
8 | describe command('pdk set') do
9 | its(:exit_status) { is_expected.to eq 0 }
10 | # Should show the command help
11 | its(:stdout) { is_expected.to match(/pdk set \[subcommand\] \[options\]/) }
12 | its(:stderr) { is_expected.to have_no_output }
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/spec/acceptance/support/contain_valid_junit_xml.rb:
--------------------------------------------------------------------------------
1 | require 'rspec/xsd'
2 |
3 | module RSpec
4 | module XSD
5 | class Matcher
6 | attr_reader :errors
7 | end
8 | end
9 | end
10 |
11 | RSpec::Matchers.define :contain_valid_junit_xml do
12 | match do |text|
13 | xsd = File.join(RSpec.configuration.fixtures_path, 'JUnit.xsd')
14 | @matcher = RSpec::XSD::Matcher.new(xsd, nil)
15 | @matcher.matches?(text)
16 | end
17 |
18 | description do
19 | 'contain valid JUnit XML'
20 | end
21 |
22 | failure_message do
23 | "expected that it would contain valid JUnit XML\r\n\r\n#{@matcher.errors.join("\r\n")}"
24 | end
25 |
26 | failure_message_when_negated do
27 | 'expected that it would not contain valid JUnit XML'
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/spec/acceptance/support/have_junit_testcase.rb:
--------------------------------------------------------------------------------
1 | require 'rexml/document'
2 |
3 | RSpec::Matchers.define :have_junit_testcase do
4 | match do |xml_text|
5 | document = REXML::Document.new(xml_text)
6 |
7 | return false if document.root.nil?
8 |
9 | testcases = document.elements.to_a("/testsuites/testsuite[@name=\"#{@testsuite_name}\"]/testcase")
10 |
11 | if @expected_attributes
12 | testcases.select! do |testcase|
13 | @expected_attributes.all? do |attribute, expected_value|
14 | values_match?(expected_value, testcase.attribute(attribute).value)
15 | end
16 | end
17 | end
18 |
19 | case @status
20 | when :pass
21 | testcases.reject!(&:has_elements?)
22 | when :skip
23 | testcases.reject! do |testcase|
24 | testcase.elements.to_a('skipped').empty?
25 | end
26 | when :fail
27 | testcases.reject! do |testcase|
28 | testcase.elements.to_a('failure').empty?
29 | end
30 |
31 | unless @failure_attributes.nil?
32 | testcases.select! do |testcase|
33 | @failure_attributes.all? do |attribute, expected_value|
34 | failure_element = testcase.elements.to_a('failure').first
35 |
36 | values_match?(expected_value, failure_element.attribute(attribute).value)
37 | end
38 | end
39 | end
40 | end
41 |
42 | !testcases.empty?
43 | end
44 |
45 | chain :with_attributes do |attributes|
46 | @expected_attributes = attributes
47 | end
48 |
49 | chain :in_testsuite do |testsuite_name|
50 | @testsuite_name = testsuite_name
51 | end
52 |
53 | chain :that_passed do
54 | @status = :pass
55 | end
56 |
57 | chain :that_was_skipped do
58 | @status = :skip
59 | end
60 |
61 | chain :that_failed do |attributes = nil|
62 | @status = :fail
63 | @failure_attributes = attributes
64 | end
65 |
66 | failure_message do |body|
67 | "expected to find a JUnit testcase#{chained_method_clause_sentences} in:\n#{body}"
68 | end
69 |
70 | failure_message_when_negated do |body|
71 | "expected not to find a JUnit testcase#{chained_method_clause_sentences} in:\n#{body}"
72 | end
73 |
74 | description do
75 | "have a JUnit testcase#{chained_method_clause_sentences}"
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/spec/acceptance/support/have_junit_testsuite.rb:
--------------------------------------------------------------------------------
1 | require 'rexml/document'
2 |
3 | RSpec::Matchers.define :have_junit_testsuite do |testsuite_name|
4 | match do |xml_text|
5 | document = REXML::Document.new(xml_text)
6 |
7 | return false if document.root.nil?
8 |
9 | testsuites = document.elements.to_a("/testsuites/testsuite[@name=\"#{testsuite_name}\"]")
10 |
11 | if @expected_attributes
12 | testsuites.select! do |testsuite|
13 | @expected_attributes.all? do |attribute, expected_value|
14 | actual_value = case attribute
15 | when 'tests', 'failures', 'errors', 'skipped'
16 | testsuite.attribute(attribute).value.to_i
17 | else
18 | testsuite.attribute(attribute).value
19 | end
20 | values_match?(expected_value, actual_value)
21 | end
22 | end
23 | end
24 |
25 | !testsuites.empty?
26 | end
27 |
28 | chain :with_attributes do |attributes|
29 | @expected_attributes = attributes
30 | end
31 |
32 | failure_message do |body|
33 | "expected to find a JUnit testsuite named '#{testsuite_name}'#{chained_method_clause_sentences} in:\n#{body}"
34 | end
35 |
36 | failure_message_when_negated do |body|
37 | "expected not to find a JUnit testsuite named '#{testsuite_name}'#{chained_method_clause_sentences} in:\n#{body}"
38 | end
39 |
40 | description do
41 | "have a JUnit testsuite named '#{testsuite_name}'#{chained_method_clause_sentences}"
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/spec/acceptance/support/have_no_output.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define :have_no_output do
2 | match do |text|
3 | values_match?(/\A\Z/, text)
4 | end
5 |
6 | diffable
7 | end
8 |
--------------------------------------------------------------------------------
/spec/acceptance/support/have_xpath.rb:
--------------------------------------------------------------------------------
1 | require 'rexml/document'
2 |
3 | RSpec::Matchers.define :have_xpath do |path|
4 | match do |xml_text|
5 | doc = REXML::Document.new(xml_text)
6 | nodes = doc.elements.to_a(path)
7 |
8 | if @expected_text
9 | nodes.select! do |node|
10 | values_match?(@expected_text, node.text)
11 | end
12 | end
13 |
14 | if @expected_attributes
15 | nodes.reject! do |node|
16 | retval = false
17 |
18 | @expected_attributes.each do |key, value|
19 | retval = true unless values_match?(value, node.attributes[key])
20 | end
21 |
22 | retval
23 | end
24 | end
25 |
26 | !nodes.empty?
27 | end
28 |
29 | chain :with_text do |text|
30 | @expected_text = text
31 | end
32 |
33 | chain :with_attributes do |attributes|
34 | @expected_attributes = attributes
35 | end
36 |
37 | failure_message do |body|
38 | "expected to find an XML tag matching XPath '#{path}'#{chained_method_clause_sentences} in:\n#{body}"
39 | end
40 |
41 | failure_message_when_negated do |body|
42 | "expected not to find an XML tag matching XPath '#{path}'#{chained_method_clause_sentences} in:\n#{body}"
43 | end
44 |
45 | description do
46 | "have an XML tag matching XPath '#{path}'#{chained_method_clause_sentences}"
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/spec/acceptance/support/in_a_new_module.rb:
--------------------------------------------------------------------------------
1 | require 'open3'
2 |
3 | shared_context 'in a new module' do |name, options = {}|
4 | before(:all) do
5 | default_template = RSpec.configuration.template_dir
6 | default_template = "file:///#{default_template}" unless Gem.win_platform?
7 | template = options.fetch(:template, default_template)
8 | argv = [
9 | 'pdk', 'new', 'module', name,
10 | '--skip-interview',
11 | '--template-url', template
12 | ]
13 | env = {
14 | 'PDK_ANSWER_FILE' => File.join(Dir.pwd, "#{name}_answers.json"),
15 | 'PDK_PUPPET_VERSION' => ENV.fetch('PDK_PUPPET_VERSION', PDK_VERSION[:lts][:full])
16 | }
17 |
18 | output, status = Open3.capture2e(env, *argv)
19 |
20 | raise "Failed to create test module:\n#{output}" unless status.success?
21 |
22 | Dir.chdir(name)
23 | end
24 |
25 | after(:all) do
26 | Dir.chdir('..')
27 | FileUtils.rm_rf(name)
28 | FileUtils.rm_f("#{name}_answers.json")
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/spec/acceptance/support/it_requires_running_in_a_module.rb:
--------------------------------------------------------------------------------
1 | require 'fileutils'
2 |
3 | RSpec.shared_examples('it requires running from inside a module', :module_command) do
4 | context 'when run outside of a module' do
5 | before(:all) do
6 | Dir.mkdir('empty_test_dir')
7 | Dir.chdir('empty_test_dir')
8 | end
9 |
10 | after(:all) do
11 | Dir.chdir('..')
12 | FileUtils.rm_rf('empty_test_dir')
13 | end
14 |
15 | describe command(top_level_description) do
16 | its(:exit_status) { is_expected.not_to eq(0) }
17 | its(:stderr) { is_expected.to match(/a valid module/i) }
18 | its(:stdout) { is_expected.to have_no_output }
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/acceptance/support/with_a_fake_answer_file.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_context 'with a fake answer file' do |initial_content = nil|
2 | before(:all) do
3 | fake_answer_file = Tempfile.new('mock_answers.json')
4 | unless initial_content.nil?
5 | require 'json'
6 | fake_answer_file.binmode
7 | fake_answer_file.write(JSON.pretty_generate(initial_content))
8 | end
9 | fake_answer_file.close
10 | ENV['PDK_ANSWER_FILE'] = fake_answer_file.path
11 | end
12 |
13 | after(:all) do
14 | FileUtils.rm_f(ENV.fetch('PDK_ANSWER_FILE', nil)) # Need actual file calls here
15 | ENV.delete('PDK_ANSWER_FILE')
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/spec/acceptance/support/with_a_fake_tty.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_context 'with a fake TTY' do
2 | around do |example|
3 | ENV['PDK_FRONTEND'] = 'INTERACTIVE'
4 | example.run
5 | ENV.delete('PDK_FRONTEND')
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/acceptance/template_ref_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper_acceptance'
2 |
3 | describe 'Specifying a template-ref' do
4 | after(:all) do
5 | # We may or may not be in the foo module directory. If we are, go back one directory.
6 | # This can happen if you only run a subset of tests in this file.
7 | Dir.chdir('..') if Dir.pwd.end_with?('foo')
8 | FileUtils.rm_rf('foo')
9 | FileUtils.rm('foo_answers.json')
10 | end
11 |
12 | context 'when creating a new module' do
13 | create_cmd = [
14 | 'pdk', 'new', 'module', 'foo',
15 | '--skip-interview',
16 | '--template-url', 'https://github.com/puppetlabs/pdk-templates',
17 | '--template-ref', '3.0.0'
18 | ]
19 |
20 | around do |example|
21 | old_answer_file = ENV.fetch('PDK_ANSWER_FILE', nil)
22 | ENV['PDK_ANSWER_FILE'] = 'foo_answers.json'
23 | example.run
24 | ENV['PDK_ANSWER_FILE'] = old_answer_file
25 | end
26 |
27 | describe command(create_cmd.join(' ')) do
28 | its(:exit_status) { is_expected.to eq(0) }
29 | its(:stderr) { is_expected.to match(/creating new module: foo/i) }
30 | its(:stderr) { is_expected.not_to match(/WARN|ERR/) }
31 | its(:stdout) { is_expected.to match(/\A\Z/) }
32 |
33 | describe file('foo/metadata.json') do
34 | it { is_expected.to be_file }
35 |
36 | its(:content_as_json) do
37 | is_expected.to include('template-ref' => match(/3\.0\.0/))
38 | end
39 | end
40 | end
41 |
42 | context 'and then updating the module to a specific ref' do
43 | before(:all) { Dir.chdir('foo') }
44 |
45 | describe command('pdk update --template-ref 3.2.0 --force') do
46 | its(:exit_status) { is_expected.to eq(0) }
47 |
48 | describe file('metadata.json') do
49 | its(:content_as_json) do
50 | is_expected.to include('template-ref' => match(/3\.2\.0/))
51 | end
52 | end
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/spec/fixtures/control_repo/Puppetfile:
--------------------------------------------------------------------------------
1 | forge 'https://forge.puppetlabs.com/'
2 |
3 | # Modules from the Puppet Forge
4 | mod 'puppetlabs-stdlib', '1.0.0'
5 |
--------------------------------------------------------------------------------
/spec/fixtures/control_repo/data/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/puppetlabs/pdk/9c9acab177d14a94f113f81b75220cb215133f3a/spec/fixtures/control_repo/data/.gitkeep
--------------------------------------------------------------------------------
/spec/fixtures/control_repo/environment.conf:
--------------------------------------------------------------------------------
1 | modulepath = modules:site:$basemodulepath
2 | environment_timeout = 0
3 |
--------------------------------------------------------------------------------
/spec/fixtures/control_repo/site/profile/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/puppetlabs/pdk/9c9acab177d14a94f113f81b75220cb215133f3a/spec/fixtures/control_repo/site/profile/.gitkeep
--------------------------------------------------------------------------------
/spec/fixtures/control_repo/site/role/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/puppetlabs/pdk/9c9acab177d14a94f113f81b75220cb215133f3a/spec/fixtures/control_repo/site/role/.gitkeep
--------------------------------------------------------------------------------
/spec/fixtures/module_root/lib/facter/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/puppetlabs/pdk/9c9acab177d14a94f113f81b75220cb215133f3a/spec/fixtures/module_root/lib/facter/.gitkeep
--------------------------------------------------------------------------------
/spec/fixtures/puppet_module/manifests/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/puppetlabs/pdk/9c9acab177d14a94f113f81b75220cb215133f3a/spec/fixtures/puppet_module/manifests/.gitkeep
--------------------------------------------------------------------------------
/spec/fixtures/puppet_module/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "testfixture-valid",
3 | "version": "0.1.0",
4 | "author": "testfixture",
5 | "summary": "Skeleton module test fixture",
6 | "license": "Apache-2.0",
7 | "source": "http://localhost",
8 | "dependencies": []
9 | }
10 |
--------------------------------------------------------------------------------
/spec/support/exit_with_status.rb:
--------------------------------------------------------------------------------
1 | RSpec::Matchers.define(:exit_with_status) do |expected_status|
2 | supports_block_expectations
3 |
4 | match do |block|
5 | expectation_passed = false
6 |
7 | begin
8 | block.call
9 | rescue SystemExit => e
10 | expectation_passed = values_match?(expected_status, e.status)
11 | rescue StandardError
12 | nil
13 | end
14 |
15 | expectation_passed
16 | end
17 | end
18 |
19 | RSpec::Matchers.define(:exit_zero) do
20 | supports_block_expectations
21 |
22 | match do |block|
23 | expect { block.call }.to exit_with_status(0)
24 | end
25 | end
26 |
27 | RSpec::Matchers.define(:exit_nonzero) do
28 | supports_block_expectations
29 |
30 | match do |block|
31 | expect { block.call }.to exit_with_status(be_nonzero)
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/support/hash_with_defaults_including.rb:
--------------------------------------------------------------------------------
1 | # Similar to RSpec’s built-in #hash_including, but does not require the keys of
2 | # `expected` to be present in `actual` -- what matters is that for all keys in
3 | # `expected`, the value in `actual` and `expected` is the same.
4 | #
5 | # hash = Hash.new { |hash, key| 9000 }
6 | # moo(hash)
7 | #
8 | # This passes:
9 | #
10 | # expect(something)
11 | # .to receive(:moo)
12 | # .with(hash_with_defaults_including(stuff: 9000))
13 | #
14 | # This does not pass:
15 | #
16 | # expect(something)
17 | # .to receive(:moo)
18 | # .with(hash_including(stuff: 9000))
19 | RSpec::Matchers.define :hash_with_defaults_including do |expected|
20 | include RSpec::Matchers::Composable
21 |
22 | match do |actual|
23 | expected.keys.all? do |key|
24 | values_match?(expected[key], actual[key])
25 | end
26 | end
27 |
28 | description do
29 | "hash_with_defaults_including(#{expected.inspect})"
30 | end
31 | end
32 |
--------------------------------------------------------------------------------
/spec/support/it_accepts_epp_targets.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_examples_for 'it accepts .epp targets' do
2 | describe '.pattern' do
3 | it 'matches EPP files' do
4 | expect(described_class.pattern).to include('**/*.epp')
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/support/it_accepts_metadata_json_targets.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_examples_for 'it accepts metadata.json targets' do
2 | describe '.pattern' do
3 | it 'matches metadata.json in the root' do
4 | expect(described_class.pattern).to include('metadata.json')
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/support/it_accepts_pp_targets.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_examples_for 'it accepts .pp targets' do
2 | describe '.pattern' do
3 | it 'matches manifest files' do
4 | expect(described_class.pattern).to include('**/*.pp')
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/spec/support/mock_configuration.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_context 'mock configuration' do
2 | let(:default_answer_file_content) { nil }
3 | let(:system_answers_content) { nil }
4 | let(:user_config_content) { nil }
5 | let(:system_config_content) { nil }
6 |
7 | let(:new_config) do
8 | PDK::Config.new.tap do |item|
9 | item.user_config.read_only!
10 | item.system_config.read_only!
11 | item.project_config.read_only!
12 | end
13 | end
14 |
15 | before do
16 | # The PDK.config method memoizes, so create a new read only config object every time
17 | allow(PDK).to receive(:config).and_return(new_config)
18 |
19 | # Mock any configuration file read/writes
20 | # Note - That we don't yet know what the project config filename/path will be so it's not mockable.
21 | [
22 | { file: PDK::AnswerFile.default_answer_file_path, content: default_answer_file_content },
23 | { file: PDK::Config.system_answers_path, content: system_answers_content },
24 | { file: PDK::Config.user_config_path, content: user_config_content },
25 | { file: PDK::Config.system_config_path, content: system_config_content }
26 | ].each do |item|
27 | # If the content is nil then mock a missing file, otherwise mock a read-able file
28 | if item[:content].nil?
29 | allow(PDK::Util::Filesystem).to receive(:file?).with(PDK::Util::Filesystem.expand_path(item[:file])).and_return(false)
30 | allow(PDK::Util::Filesystem).to receive(:read_file).with(PDK::Util::Filesystem.expand_path(item[:file])).and_raise('Mock configuration file does not exist')
31 | else
32 | allow(PDK::Util::Filesystem).to receive(:file?).with(PDK::Util::Filesystem.expand_path(item[:file])).and_return(true)
33 | allow(PDK::Util::Filesystem).to receive(:read_file).with(PDK::Util::Filesystem.expand_path(item[:file])).and_return(item[:content])
34 | end
35 | allow(PDK::Util::Filesystem).to receive(:write_file).with(PDK::Util::Filesystem.expand_path(item[:file]), anything)
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/spec/support/packaged_install.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_context 'packaged install' do
2 | let(:package_cachedir) { '/package/share/cache' }
3 |
4 | before do
5 | allow(PDK::Util).to receive_messages(package_install?: true, package_cachedir:)
6 | allow(PDK::Util::Filesystem).to receive(:file?).with(/PDK_VERSION/).and_return(true)
7 | allow(PDK::Util::Filesystem).to receive(:exist?).with(/bundle(\.bat)?$/).and_return(true)
8 | allow(PDK::Util::RubyVersion).to receive_messages(versions: { '2.4.4' => '2.4.0' }, default_ruby_version: '2.4.4')
9 | end
10 | end
11 |
12 | RSpec.shared_context 'not packaged install' do
13 | before do
14 | allow(PDK::Util).to receive(:package_install?).and_return(false)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/support/run_outside_module.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_context 'run outside module' do
2 | let(:mock_context) { PDK::Context::None.new(nil) }
3 |
4 | before do
5 | msg = 'must be run from inside a valid module (no metadata.json found)'
6 | allow(PDK::CLI::Util).to receive(:ensure_in_module!).with(any_args).and_raise(PDK::CLI::ExitWithError, msg)
7 | allow(PDK).to receive(:context).and_return(mock_context)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/spec/support/valid_in_context.rb:
--------------------------------------------------------------------------------
1 | RSpec.shared_examples_for 'only valid in specified PDK contexts' do |*context_class|
2 | describe '.valid_in_context?' do
3 | [
4 | PDK::Context::None.new(nil),
5 | PDK::Context::Module.new(nil, nil),
6 | PDK::Context::ControlRepo.new(nil, nil)
7 | ].each do |pdk_context|
8 | context "in #{pdk_context.display_name}" do
9 | subject { described_class.new(pdk_context, {}).valid_in_context? }
10 |
11 | if context_class.include?(pdk_context.class)
12 | it { is_expected.to be(true) }
13 | else
14 | it { is_expected.to be(false) }
15 | end
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/unit/pdk/bolt_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/util'
3 |
4 | describe PDK::Bolt do
5 | describe '.bolt_project_root?' do
6 | # We use NUL here because that should never be a valid directory name. But it will work with RSpec mocking.
7 | let(:test_path) { '\x00path/test' }
8 |
9 | before do
10 | allow(PDK::Util::Filesystem).to receive(:file?).and_call_original
11 | end
12 |
13 | # Directories which indicate a bolt project
14 | ['Boltdir'].each do |testcase|
15 | it "detects the directory #{testcase} as being the root of a bolt project" do
16 | path = File.join(test_path, testcase)
17 | allow(PDK::Util::Filesystem).to receive(:directory?).with(path).and_return(true)
18 | expect(described_class.bolt_project_root?(path)).to be(true)
19 | end
20 | end
21 |
22 | # Directories which do not indicate a bolt project
23 | ['boltdir', 'Boltdir/something'].each do |testcase|
24 | it "detects the directory #{testcase} as not being the root of a bolt project" do
25 | path = File.join(test_path, testcase)
26 | allow(PDK::Util::Filesystem).to receive(:directory?).with(path).and_return(true)
27 | expect(described_class.bolt_project_root?(path)).to be(false)
28 | end
29 | end
30 |
31 | # Files which indicate a bolt project
32 | ['bolt.yaml'].each do |testcase|
33 | it "detects ./#{testcase} as being in the root of a bolt project" do
34 | allow(PDK::Util::Filesystem).to receive(:file?).with(File.join(test_path, testcase)).and_return(true)
35 | expect(described_class.bolt_project_root?(test_path)).to be(true)
36 | end
37 | end
38 |
39 | # Files which do not indicate a bolt project
40 | ['Puppetfile', 'environment.conf', 'metadata.json'].each do |testcase|
41 | it "detects ./#{testcase} as not being in the root of a bolt project" do
42 | allow(PDK::Util::Filesystem).to receive(:file?).with(File.join(test_path, testcase)).and_return(true)
43 | expect(described_class.bolt_project_root?(test_path)).to be(false)
44 | end
45 | end
46 |
47 | it 'uses the current directory if a directory is not specified' do
48 | expect(PDK::Util::Filesystem).to receive(:file?) { |path| expect(path).to start_with(Dir.pwd) }.at_least(:once)
49 | described_class.bolt_project_root?
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/bundle_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli'
3 |
4 | describe 'Running `pdk bundle`' do
5 | let(:command_args) { ['bundle'] }
6 | let(:command_result) { { exit_code: 0 } }
7 |
8 | context 'when it calls bundler successfully' do
9 | before do
10 | mock_command = instance_double(
11 | PDK::CLI::Exec::InteractiveCommand,
12 | :context= => true,
13 | :update_environment => true,
14 | :execute! => command_result
15 | )
16 | allow(PDK::CLI::Exec::InteractiveCommand).to receive(:new)
17 | .with(PDK::CLI::Exec.bundle_bin, *(command_args[1..] || []))
18 | .and_return(mock_command)
19 |
20 | allow(PDK::Util).to receive(:module_root)
21 | .and_return(File.join('path', 'to', 'test', 'module'))
22 | allow(PDK::Module::Metadata).to receive(:from_file)
23 | .with('metadata.json').and_return({})
24 | allow(PDK::CLI::Util).to receive(:puppet_from_opts_or_env)
25 | .and_return(ruby_version: '2.4.3', gemset: { puppet: '5.4.0' })
26 | allow(PDK::Util::RubyVersion).to receive(:use)
27 | end
28 |
29 | context 'and called with no arguments' do
30 | it 'runs without an error' do
31 | expect do
32 | PDK::CLI.run(command_args)
33 | end.to exit_zero
34 | end
35 | end
36 |
37 | context 'and called with a bundler subcommand that is not "exec"' do
38 | let(:command_args) { super() + ['config', 'something'] }
39 |
40 | it 'runs without an error' do
41 | expect do
42 | PDK::CLI.run(command_args)
43 | end.to exit_zero
44 | end
45 | end
46 |
47 | context 'and called with the "exec" bundler subcommand' do
48 | let(:command_args) { super() + ['exec', 'rspec', 'some/path'] }
49 |
50 | it 'runs without an error' do
51 | expect do
52 | PDK::CLI.run(command_args)
53 | end.to exit_zero
54 | end
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/env_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli'
3 |
4 | describe 'Running `pdk env`' do
5 | let(:command_args) { ['env'] }
6 | let(:command_result) { { exit_code: 0 } }
7 |
8 | context 'when it calls env successfully' do
9 | after do
10 | expect do
11 | PDK::CLI.run(command_args)
12 | end.to exit_zero
13 | end
14 |
15 | before do
16 | allow(PDK::Util::RubyVersion).to receive_messages(gem_home: '/opt/puppetlabs/pdk/share/cache/ruby/2.4.0', gem_path: '/opt/puppetlabs/pdk/private/ruby/2.4.3/lib',
17 | bin_path: '/opt/puppetlabs/pdk/private/ruby/2.4.3/bin', gem_paths_raw: ['/opt/puppetlabs/pdk/private/ruby/2.4.3/lib'])
18 | allow(PDK::Util::Env).to receive(:[]).and_call_original
19 | allow(PDK::Util::Env).to receive(:[]).with('PATH').and_return('/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin')
20 |
21 | allow(PDK::CLI::Util).to receive(:puppet_from_opts_or_env)
22 | .and_return(ruby_version: '2.4.3', gemset: { puppet: '5.4.0' })
23 | allow(PDK::Util::RubyVersion).to receive(:use)
24 | end
25 |
26 | context 'and called with no arguments' do
27 | it 'outputs export commands for environment variables' do
28 | output_regexes = [
29 | /export PDK_RESOLVED_PUPPET_VERSION="\d\.\d+\.\d+"/,
30 | /export PDK_RESOLVED_RUBY_VERSION="\d\.\d+\.\d+"/,
31 | /export GEM_HOME=.*/,
32 | /export GEM_PATH=.*/,
33 | /export PATH=.*/
34 | ]
35 |
36 | output_regexes.each do |regex|
37 | expect($stdout).to receive(:puts).with(a_string_matching(regex))
38 | end
39 | end
40 | end
41 |
42 | context 'and called with a puppet version' do
43 | let(:command_args) { super() + ['--puppet-version=6'] }
44 |
45 | it 'outputs export commands for environment variables' do
46 | output_regexes = [
47 | /export PDK_RESOLVED_PUPPET_VERSION="\d\.\d+\.\d+"/,
48 | /export PDK_RESOLVED_RUBY_VERSION="\d\.\d+\.\d+"/,
49 | /export GEM_HOME=.*/,
50 | /export GEM_PATH=.*/,
51 | /export PATH=.*/
52 | ]
53 |
54 | output_regexes.each do |regex|
55 | expect($stdout).to receive(:puts).with(a_string_matching(regex))
56 | end
57 | end
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/errors_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli/errors'
3 |
4 | describe PDK::CLI::FatalError do # rubocop:disable RSpec/MultipleDescribes
5 | subject(:error) { described_class.new }
6 |
7 | it 'has a default error message' do
8 | expect(error.message).to match(/an unexpected error has occurred/i)
9 | end
10 |
11 | it 'has a default non-zero exit code' do
12 | expect(error.exit_code).to be_an(Integer)
13 | expect(error.exit_code).not_to be_zero
14 | end
15 |
16 | context 'when provided a custom error message' do
17 | subject(:error) { described_class.new('test message') }
18 |
19 | it 'uses the custom error message' do
20 | expect(error.message).to eq('test message')
21 | end
22 |
23 | context 'and a custom exit code' do
24 | subject(:error) { described_class.new('test message', exit_code: 2) }
25 |
26 | it 'uses the custom exit code' do
27 | expect(error.exit_code).to eq(2)
28 | end
29 | end
30 | end
31 | end
32 |
33 | describe PDK::CLI::ExitWithError do
34 | subject(:error) { described_class.new(message, options) }
35 |
36 | let(:message) { 'test message' }
37 | let(:options) { {} }
38 |
39 | it 'uses the provided error message' do
40 | expect(error.message).to eq(message)
41 | end
42 |
43 | it 'has a default non-zero exit code' do
44 | expect(error.exit_code).to be_an(Integer)
45 | expect(error.exit_code).not_to be_zero
46 | end
47 |
48 | it 'has a default log level of error' do
49 | expect(error.log_level).to eq(:error)
50 | end
51 |
52 | context 'when provided with a custom exit code' do
53 | let(:options) { { exit_code: 2 } }
54 |
55 | it 'uses the custom exit code' do
56 | expect(error.exit_code).to eq(2)
57 | end
58 | end
59 |
60 | context 'when provided with a custom log level' do
61 | let(:options) { { log_level: :info } }
62 |
63 | it 'uses the custom log level' do
64 | expect(error.log_level).to eq(:info)
65 | end
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/exec_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli/exec'
3 |
4 | describe PDK::CLI::Exec do
5 | describe '#try_vendored_bin' do
6 | context 'when installed as gem' do
7 | before { allow(PDK::Util).to receive(:package_install?).with(no_args).and_return(false) }
8 |
9 | it 'returns the fallback' do
10 | expect(described_class.try_vendored_bin('something', 'fallback')).to eq 'fallback'
11 | end
12 | end
13 |
14 | context 'when installed as package' do
15 | before do
16 | allow(PDK::Util).to receive(:package_install?).with(no_args).and_return(true)
17 | allow(PDK::Util).to receive(:pdk_package_basedir).with(no_args).and_return('/foo')
18 | end
19 |
20 | context 'when the file exists in the package' do
21 | before do
22 | allow(PDK::Util::Filesystem).to receive(:exist?).with('/foo/private/bin/bar').and_return(true)
23 | end
24 |
25 | it 'returns the full path' do
26 | expect(described_class.try_vendored_bin('private/bin/bar', 'fallback')).to eq '/foo/private/bin/bar'
27 | end
28 | end
29 |
30 | context 'when the file is not in the package' do
31 | before do
32 | allow(PDK::Util::Filesystem).to receive(:exist?).with('/foo/private/bin/bar').and_return(false)
33 | end
34 |
35 | it 'returns the full path' do
36 | expect(described_class.try_vendored_bin('private/bin/bar', 'fallback')).to eq 'fallback'
37 | end
38 | end
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/new/class_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli'
3 |
4 | describe 'PDK::CLI new class' do
5 | let(:help_text) { a_string_matching(/^USAGE\s+pdk new class/m) }
6 |
7 | before do
8 | # Stop printing out the result
9 | allow(PDK::CLI::Util::UpdateManagerPrinter).to receive(:print_summary)
10 | end
11 |
12 | context 'when not run from inside a module' do
13 | include_context 'run outside module'
14 |
15 | it 'exits with an error' do
16 | expect(logger).to receive(:error).with(a_string_matching(/must be run from inside a valid module/))
17 |
18 | expect { PDK::CLI.run(['new', 'class', 'test_class']) }.to exit_nonzero
19 | end
20 | end
21 |
22 | context 'when run from inside a module' do
23 | let(:root_dir) { '/path/to/test/module' }
24 |
25 | before do
26 | allow(PDK::Util).to receive(:module_root).and_return(root_dir)
27 | end
28 |
29 | context 'and not provided with a class name' do
30 | it 'exits non-zero and prints the `pdk new class` help' do
31 | expect { PDK::CLI.run(['new', 'class']) }.to exit_nonzero.and output(help_text).to_stdout
32 | end
33 | end
34 |
35 | context 'and provided an empty string as the class name' do
36 | it 'exits non-zero and prints the `pdk new class` help' do
37 | expect { PDK::CLI.run(['new', 'class', '']) }.to exit_nonzero.and output(help_text).to_stdout
38 | end
39 | end
40 |
41 | context 'and provided an invalid class name' do
42 | it 'exits with an error' do
43 | expect(logger).to receive(:error).with(a_string_matching(/'test-class' is not a valid class name/))
44 |
45 | expect { PDK::CLI.run(['new', 'class', 'test-class']) }.to exit_nonzero
46 | end
47 | end
48 |
49 | context 'and provided a valid class name' do
50 | let(:generator) { instance_double(PDK::Generate::PuppetClass, run: true) }
51 |
52 | after do
53 | PDK::CLI.run(['new', 'class', 'test_class'])
54 | end
55 |
56 | it 'generates the class' do
57 | expect(PDK::Generate::PuppetClass).to receive(:new).with(anything, 'test_class', instance_of(Hash)).and_return(generator)
58 | expect(generator).to receive(:run)
59 | end
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/new_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli'
3 |
4 | describe 'Running `pdk new`' do
5 | subject { PDK::CLI.instance_variable_get(:@new_cmd) }
6 |
7 | context 'when no arguments or options are provided' do
8 | it do
9 | expect do
10 | PDK::CLI.run(['new'])
11 | end.to output(/^USAGE\s+pdk new/m).to_stdout
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/release/prep_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli'
3 |
4 | describe 'PDK::CLI release prep' do
5 | let(:help_text) { a_string_matching(/^USAGE\s+pdk release prep/m) }
6 | let(:cli_args) { ['release', 'prep'] }
7 |
8 | context 'when not run from inside a module' do
9 | include_context 'run outside module'
10 |
11 | it 'exits with an error' do
12 | expect(logger).to receive(:error).with(a_string_matching(/must be run from inside a valid module/))
13 |
14 | expect { PDK::CLI.run(cli_args) }.to exit_nonzero
15 | end
16 | end
17 |
18 | context 'when run from inside a module' do
19 | let(:release_object) do
20 | instance_double(
21 | PDK::Module::Release,
22 | pdk_compatible?: true,
23 | module_metadata: mock_metadata_obj,
24 | run: nil
25 | )
26 | end
27 |
28 | let(:mock_metadata_obj) do
29 | instance_double(
30 | PDK::Module::Metadata,
31 | forge_ready?: true
32 | )
33 | end
34 |
35 | before do
36 | allow(PDK::CLI::Util).to receive(:ensure_in_module!).and_return(nil)
37 | allow(PDK::Module::Release).to receive(:new).and_return(release_object)
38 | allow(PDK::Util).to receive(:exit_process).and_raise('exit_process mock should not be called')
39 | end
40 |
41 | it 'calls PDK::Module::Release.run' do
42 | expect(release_object).to receive(:run)
43 |
44 | expect { PDK::CLI.run(cli_args.push('--force')) }.not_to raise_error
45 | end
46 |
47 | it 'skips building and publishing' do
48 | expect(PDK::Module::Release).to receive(:new).with(Object, hash_including('skip-build': true, 'skip-publish': true))
49 |
50 | expect { PDK::CLI.run(cli_args.push('--force')) }.not_to raise_error
51 | end
52 |
53 | it 'does not start an interview when --force is used' do
54 | expect(PDK::CLI::Util::Interview).not_to receive(:new)
55 |
56 | PDK::CLI.run(cli_args.push('--force'))
57 | end
58 |
59 | it 'calls PDK::CLI::Release.module_compatibility_checks!' do
60 | expect(PDK::CLI::Release).to receive(:module_compatibility_checks!).and_return(nil)
61 |
62 | expect { PDK::CLI.run(cli_args.push('--force')) }.not_to raise_error
63 | end
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/test_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli'
3 |
4 | describe 'Running `pdk test`' do
5 | subject { PDK::CLI.instance_variable_get(:@test_cmd) }
6 |
7 | it { is_expected.not_to be_nil }
8 |
9 | context 'when no arguments or options are provided' do
10 | it do
11 | expect do
12 | PDK::CLI.run(['test'])
13 | end.to output(/^USAGE\s+pdk test/m).to_stdout
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/util/command_redirector_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli/util/command_redirector'
3 |
4 | describe PDK::CLI::Util::CommandRedirector do
5 | subject(:command_redirector) do
6 | described_class.new(prompt, {})
7 | end
8 |
9 | let(:prompt) { instance_double(TTY::Prompt) }
10 |
11 | let(:command) { 'foo' }
12 |
13 | it 'initially has no target command' do
14 | expect(command_redirector.command).to be_nil
15 | end
16 |
17 | it 'sets the target' do
18 | command_redirector.target_command(command)
19 | expect(command_redirector.command).to eq(command)
20 | end
21 |
22 | it 'prints a query when run' do
23 | command_redirector.target_command('foo')
24 | expect(prompt).to receive(:puts).with(a_string_matching(/Did you mean.*foo.*?/i))
25 | expect(prompt).to receive(:yes?).with('-->')
26 | command_redirector.run
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/spec/unit/pdk/cli/util/interview_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/cli/util/interview'
3 |
4 | describe 'Module interview' do
5 | subject(:interview) { PDK::CLI::Util::Interview }
6 |
7 | it 'initially has 0 questions' do
8 | expect(interview.new({}, {}).num_questions).to eq(0)
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/unit/pdk/config/ini_file_setting_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/config/ini_file_setting'
3 |
4 | describe PDK::Config::IniFileSetting do
5 | subject(:setting) { described_class.new('spec_setting', namespace, initial_value) }
6 |
7 | let(:initial_value) { nil }
8 |
9 | let(:namespace) { PDK::Config::IniFile.new('spec') }
10 |
11 | context 'when not in an Ini File Namespace' do
12 | let(:namespace) { PDK::Config::Namespace.new }
13 |
14 | it 'raises' do
15 | expect { setting }.to raise_error(/IniFile Namespace/)
16 | end
17 | end
18 |
19 | context 'with invalid initial value' do
20 | let(:initial_value) { ['abc', '123'] }
21 |
22 | it 'raises' do
23 | expect { setting }.to raise_error(ArgumentError, /spec_setting/)
24 | end
25 | end
26 |
27 | RSpec.shared_examples 'a setting validator' do |value_type_name, value|
28 | it "validates #{value_type_name}" do
29 | expect { setting.validate!(value) }.not_to raise_error
30 | end
31 |
32 | it "validates #{value_type_name} in a simple hash" do
33 | expect { setting.validate!('foo' => value) }.not_to raise_error
34 | end
35 | end
36 |
37 | RSpec.shared_examples 'an error raising validator' do |value_type_name, value|
38 | it "raises for #{value_type_name}" do
39 | expect { setting.validate!(value) }.to raise_error(ArgumentError, /spec_setting/)
40 | end
41 | end
42 |
43 | describe '#validate!' do
44 | context 'with valid values' do
45 | include_examples 'a setting validator', 'String', 'value'
46 | include_examples 'a setting validator', 'Nil', nil
47 | include_examples 'a setting validator', 'Integer', 1
48 | end
49 |
50 | context 'with invalid values' do
51 | include_examples 'an error raising validator', 'Symbol', :value
52 | include_examples 'an error raising validator', 'Array', ['abc', '123']
53 | include_examples 'an error raising validator', 'Nested hash', 'foo' => { 'bar' => 'baz' }
54 | include_examples 'an error raising validator', 'Float', 1.0
55 | end
56 | end
57 | end
58 |
--------------------------------------------------------------------------------
/spec/unit/pdk/config/json_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'tempfile'
3 | require 'pdk/config/json'
4 |
5 | describe PDK::Config::JSON do
6 | subject(:json_config) { described_class.new(file: tempfile) }
7 |
8 | let(:tempfile) do
9 | file = Tempfile.new('test')
10 | file.path
11 | end
12 |
13 | it_behaves_like 'a file based namespace', "{\n \"foo\": \"bar\"\n}", 'foo' => 'bar'
14 |
15 | it_behaves_like 'a file based namespace without a schema'
16 |
17 | it_behaves_like 'a json file based namespace'
18 | end
19 |
--------------------------------------------------------------------------------
/spec/unit/pdk/config/json_with_schema_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/config/json_with_schema'
3 | require 'tempfile'
4 |
5 | # Note that the JSON Schema Gem is too unreliable for testing right now.
6 | # For the moment, all tests are skipped here.
7 | describe PDK::Config::JSONWithSchema, :skip do
8 | subject(:json_config) { described_class.new(file: tempfile, schema_file: temp_schema_file) }
9 |
10 | let(:tempfile) do
11 | file = Tempfile.new('test')
12 | file.write(data)
13 | file.close
14 | file.path
15 | end
16 | let(:data) { nil }
17 | let(:temp_schema_file) do
18 | file = Tempfile.new('schema')
19 | file.write(schema_data)
20 | file.close
21 | file.path
22 | end
23 |
24 | let(:schema_data) do
25 | <<-SCHEMA
26 | {
27 | "definitions": {},
28 | "$schema": "http://json-schema.org/draft-06/schema#",
29 | "$id": "http://puppet.com/schema/does_not_exist.json",
30 | "type": "object",
31 | "title": "A Schema",
32 | "properties": {
33 | "foo": {
34 | "$id": "#/properties/foo",
35 | "title": "A property"
36 | }
37 | }
38 | }
39 | SCHEMA
40 | end
41 |
42 | it_behaves_like 'a file based namespace', "{\n \"foo\": \"bar\"\n}", 'foo' => 'bar'
43 |
44 | it_behaves_like 'a file based namespace with a schema', "{\n\"extra_setting\": \"extra_value\",\n\"foo\": \"oldvalue\"\n}\n"
45 |
46 | it_behaves_like 'a json file based namespace'
47 |
48 | it 'inherits from JSONSchemaNamespace' do
49 | expect(json_config).to be_a(PDK::Config::JSONSchemaNamespace)
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/spec/unit/pdk/config/schema_files_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/config'
3 | require 'json-schema'
4 |
5 | describe 'PDK::Config Schema Files' do
6 | PDK::Util::Filesystem.glob(File.join(PDK::Config.json_schemas_path, '*_schema.json')).each do |schema_path|
7 | describe File.basename(schema_path) do
8 | # rubocop:disable PDK/FileOpen
9 | subject(:schema) { JSON.parse(File.open(schema_path, 'rb:UTF-8').read) }
10 | # rubocop:enable PDK/FileOpen
11 |
12 | it 'is a valid JSON schema document' do
13 | # The Schema Document specifies which schema version it uses
14 | expect(schema['$schema']).to match(/draft-\d+/)
15 | metaschema = JSON::Validator.validator_for_name(schema['$schema']).metaschema
16 |
17 | expect(JSON::Validator.validate(metaschema, schema)).to be true
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/unit/pdk/config/yaml_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/config/yaml'
3 |
4 | describe PDK::Config::YAML do
5 | subject(:yaml_config) { described_class.new(file: tempfile) }
6 |
7 | let(:tempfile) do
8 | file = Tempfile.new('test')
9 | file.write(data)
10 | file.close
11 | file.path
12 | end
13 | let(:data) { nil }
14 |
15 | it_behaves_like 'a file based namespace', "---\nfoo: bar\n", 'foo' => 'bar'
16 |
17 | it_behaves_like 'a file based namespace without a schema'
18 |
19 | it_behaves_like 'a yaml file based namespace'
20 | end
21 |
--------------------------------------------------------------------------------
/spec/unit/pdk/config/yaml_with_schema_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/config/yaml_with_schema'
3 | require 'tempfile'
4 |
5 | # Note that the JSON Schema Gem is too unreliable for testing right now.
6 | # For the moment, all tests are skipped here.
7 | describe PDK::Config::YAMLWithSchema, :skip do
8 | subject(:yaml_config) { described_class.new(file: tempfile, schema_file: temp_schema_file) }
9 |
10 | let(:tempfile) do
11 | file = Tempfile.new('test')
12 | file.write(data)
13 | file.close
14 | file.path
15 | end
16 | let(:data) { nil }
17 | let(:temp_schema_file) do
18 | file = Tempfile.new('schema')
19 | file.write(schema_data)
20 | file.close
21 | file.path
22 | end
23 |
24 | let(:schema_data) do
25 | <<-SCHEMA
26 | {
27 | "definitions": {},
28 | "$schema": "http://json-schema.org/draft-06/schema#",
29 | "$id": "http://puppet.com/schema/does_not_exist.json",
30 | "type": "object",
31 | "title": "A Schema",
32 | "properties": {
33 | "foo": {
34 | "$id": "#/properties/foo",
35 | "title": "A property"
36 | }
37 | }
38 | }
39 | SCHEMA
40 | end
41 |
42 | it_behaves_like 'a file based namespace', "---\nfoo: bar\n", { 'foo' => 'bar' }, true
43 |
44 | it_behaves_like 'a file based namespace with a schema', "---\nextra_setting: \"extra value\"\nfoo: oldvalue"
45 |
46 | it_behaves_like 'a yaml file based namespace'
47 |
48 | it 'inherits from JSONSchemaNamespace' do
49 | expect(yaml_config).to be_a(PDK::Config::JSONSchemaNamespace)
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/spec/unit/pdk/context/control_repo_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/context'
3 |
4 | describe PDK::Context::ControlRepo do
5 | subject(:context) { described_class.new(repo_root, nil) }
6 |
7 | let(:repo_root) { File.join(FIXTURES_DIR, 'control_repo') }
8 | let(:expected_module_paths) { ['modules', 'site', '$basemodulepath'] }
9 | let(:module_paths_in_fixture) { ['site'] }
10 |
11 | it 'subclasses PDK::Context::AbstractContext' do
12 | expect(context).is_a?(PDK::Context::AbstractContext)
13 | end
14 |
15 | it 'remembers the repo root' do
16 | expect(context.root_path).to eq(repo_root)
17 | end
18 |
19 | it 'is PDK compatible' do
20 | expect(context.pdk_compatible?).to be(true)
21 | end
22 |
23 | describe '.module_paths' do
24 | it 'returns an array of paths' do
25 | expect(context.module_paths).to eq(expected_module_paths)
26 | end
27 |
28 | it 'is memoized' do
29 | expect(context).to receive(:environment_conf).and_call_original # rubocop:disable RSpec/SubjectStub We are still calling the original so this is fine
30 |
31 | context.module_paths
32 | context.module_paths
33 | context.module_paths
34 | end
35 | end
36 |
37 | describe '.actualized_module_paths' do
38 | it 'returns absolute paths that exist on disk' do
39 | expect(context.actualized_module_paths).to eq(module_paths_in_fixture)
40 | end
41 |
42 | it 'is memoized' do
43 | expect(context).to receive(:module_paths).and_call_original # rubocop:disable RSpec/SubjectStub We are still calling the original so this is fine
44 |
45 | context.actualized_module_paths
46 | context.actualized_module_paths
47 | context.actualized_module_paths
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/unit/pdk/context/module_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/context'
3 |
4 | describe PDK::Context::Module do
5 | subject(:context) { described_class.new(module_root, nil) }
6 |
7 | let(:module_root) { File.join(FIXTURES_DIR, 'puppet_module') }
8 |
9 | it 'subclasses PDK::Context::AbstractContext' do
10 | expect(context).is_a?(PDK::Context::AbstractContext)
11 | end
12 |
13 | it 'remembers the module root' do
14 | expect(context.root_path).to eq(module_root)
15 | end
16 |
17 | describe '.pdk_compatible?' do
18 | it 'calls PDK::Util to determine compatibility' do
19 | expect(PDK::Util).to receive(:module_pdk_compatible?).with(context.root_path).and_return(true)
20 | expect(context.pdk_compatible?).to be(true)
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/unit/pdk/context/none_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/context'
3 |
4 | describe PDK::Context::None do
5 | subject(:context) { described_class.new(nil) }
6 |
7 | it 'subclasses PDK::Context::AbstractContext' do
8 | expect(context).is_a?(PDK::Context::AbstractContext)
9 | end
10 |
11 | it 'has no parent context' do
12 | expect(context.parent_context).to be_nil
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/spec/unit/pdk/generate/provider_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/generate/provider'
3 |
4 | describe PDK::Generate::Provider do
5 | subject(:generator) { described_class.new(context, given_name, options) }
6 |
7 | let(:context) { PDK::Context::Module.new(module_dir, module_dir) }
8 | let(:module_dir) { '/tmp/test_module' }
9 | let(:options) { {} }
10 | let(:given_name) { 'test_provider' }
11 |
12 | it 'inherits from PuppetObject' do
13 | expect(generator).to be_a(PDK::Generate::PuppetObject)
14 | end
15 |
16 | describe '#template_files' do
17 | let(:given_class_name) { module_name }
18 |
19 | context 'when spec_only is true' do
20 | let(:options) { { spec_only: true } }
21 |
22 | it 'only returns spec files' do
23 | expect(generator.template_files.keys).to eq(['provider_spec.erb', 'provider_type_spec.erb'])
24 | end
25 | end
26 |
27 | context 'when spec_only is false' do
28 | let(:options) { { spec_only: false } }
29 |
30 | it 'returns all files' do
31 | expect(generator.template_files.keys).to eq(['provider_spec.erb', 'provider_type_spec.erb', 'provider.erb', 'provider_type.erb'])
32 | end
33 | end
34 | end
35 |
36 | # TODO: Write some tests!!
37 | end
38 |
--------------------------------------------------------------------------------
/spec/unit/pdk/generate/transport_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/generate/transport'
3 |
4 | describe PDK::Generate::Transport do
5 | subject(:generator) { described_class.new(context, given_name, options) }
6 |
7 | let(:context) { PDK::Context::Module.new(module_dir, module_dir) }
8 | let(:module_dir) { '/tmp/test_module' }
9 | let(:options) { {} }
10 | let(:given_name) { 'test_transport' }
11 |
12 | it 'inherits from PuppetObject' do
13 | expect(generator).to be_a(PDK::Generate::PuppetObject)
14 | end
15 |
16 | describe '#template_files' do
17 | let(:given_class_name) { module_name }
18 |
19 | context 'when spec_only is true' do
20 | let(:options) { { spec_only: true } }
21 |
22 | it 'only returns spec files' do
23 | expect(generator.template_files.keys).to eq(['transport_spec.erb', 'transport_type_spec.erb'])
24 | end
25 | end
26 |
27 | context 'when spec_only is false' do
28 | let(:options) { { spec_only: false } }
29 |
30 | it 'returns all files' do
31 | expect(generator.template_files.keys).to eq(['transport_spec.erb', 'transport_type_spec.erb', 'transport.erb', 'transport_device.erb', 'transport_type.erb'])
32 | end
33 | end
34 | end
35 |
36 | # TODO: Write some tests!!
37 | end
38 |
--------------------------------------------------------------------------------
/spec/unit/pdk/logger_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/logger'
3 |
4 | describe PDK::Logger do
5 | subject(:pdk_logger) { described_class.new }
6 |
7 | context 'by default' do
8 | it 'prints info messages to stdout' do
9 | expect($stderr).to receive(:write).with(a_string_matching(/test message/))
10 |
11 | pdk_logger.info('test message')
12 | end
13 |
14 | it 'does not print debug messages to stdout' do
15 | expect($stderr).not_to receive(:write).with(anything)
16 |
17 | pdk_logger.debug('test message')
18 | end
19 |
20 | it { is_expected.to have_attributes(debug?: false) }
21 | end
22 |
23 | context 'with debug output enabled' do
24 | before do
25 | pdk_logger.enable_debug_output
26 | end
27 |
28 | it 'prints debug messages to stdout' do
29 | expect($stderr).to receive(:write).with(a_string_matching(/test debug message/))
30 |
31 | pdk_logger.debug('test debug message')
32 | end
33 |
34 | it { is_expected.to have_attributes(debug?: true) }
35 | end
36 |
37 | describe '#warn_once' do
38 | it 'only sends each message once' do
39 | expect($stderr).to receive(:write).with("pdk (WARN): message 1\n").once
40 |
41 | pdk_logger.warn_once('message 1')
42 | pdk_logger.warn_once('message 1')
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/spec/unit/pdk/template/fetcher/local_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/template/fetcher/local'
3 |
4 | describe PDK::Template::Fetcher::Local do
5 | subject(:fetcher) { described_class.new(template_uri, pdk_context) }
6 |
7 | let(:template_path) { '/some/path' }
8 | let(:template_uri) { PDK::Util::TemplateURI.new(template_path) }
9 | let(:pdk_context) { PDK::Context::None.new(nil) }
10 |
11 | describe '.fetchable?' do
12 | it 'is always fetchable' do
13 | expect(described_class.fetchable?(nil, nil)).to be true
14 | end
15 | end
16 |
17 | describe '.fetch!' do
18 | it 'is not temporary' do
19 | fetcher.fetch!
20 | expect(fetcher.temporary).to be false
21 | end
22 |
23 | it 'uses the path from the uri' do
24 | fetcher.fetch!
25 | expect(fetcher.path).to eq(template_path)
26 | end
27 |
28 | it 'sets template-url in the metadata' do
29 | fetcher.fetch!
30 | expect(fetcher.metadata).to include('template-url' => template_path)
31 | end
32 | end
33 | end
34 |
--------------------------------------------------------------------------------
/spec/unit/pdk/template/renderer/v1_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/template/renderer/v1'
3 |
4 | describe PDK::Template::Renderer::V1 do
5 | let(:template_root) { '/some/path' }
6 | let(:template_uri) { PDK::Util::TemplateURI.new(template_root) }
7 | let(:pdk_context) { PDK::Context::None.new(nil) }
8 |
9 | describe '.compatible?' do
10 | subject(:compatible) { described_class.compatible?(template_root, pdk_context) }
11 |
12 | context 'when all module template directories exist' do
13 | before do
14 | allow(PDK::Util::Filesystem).to receive(:directory?).with('/some/path/moduleroot').and_return(true)
15 | allow(PDK::Util::Filesystem).to receive(:directory?).with('/some/path/moduleroot_init').and_return(true)
16 | end
17 |
18 | it 'is compatible' do
19 | expect(compatible).to be true
20 | end
21 | end
22 |
23 | context 'when only some of module template directories exist' do
24 | before do
25 | allow(PDK::Util::Filesystem).to receive(:directory?).with('/some/path/moduleroot').and_return(true)
26 | allow(PDK::Util::Filesystem).to receive(:directory?).with('/some/path/moduleroot_init').and_return(false)
27 | end
28 |
29 | it 'is not compatible' do
30 | expect(compatible).to be false
31 | end
32 | end
33 | end
34 |
35 | describe '.instance' do
36 | it 'creates a PDK::Template::Renderer::V1::Renderer object' do
37 | expect(described_class.instance(template_root, template_uri, pdk_context)).to be_a(described_class::Renderer)
38 | end
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/spec/unit/pdk/template/renderer_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/template/renderer'
3 |
4 | describe PDK::Template::Renderer do
5 | let(:template_path) { '/some/path' }
6 | let(:template_uri) { PDK::Util::TemplateURI.new(template_path) }
7 | let(:pdk_context) { PDK::Context::None.new(nil) }
8 |
9 | describe '.instance' do
10 | subject(:instance) { described_class.instance(template_uri, template_path, pdk_context) }
11 |
12 | context 'given an original template directory' do
13 | before do
14 | allow(described_class::V1).to receive(:compatible?).and_return(true)
15 | end
16 |
17 | it 'creates a version 1 renderer' do
18 | expect(instance).to be_a(described_class::V1::Renderer)
19 | end
20 | end
21 |
22 | context 'given a template that has no appropriate renderer' do
23 | before do
24 | allow(described_class::V1).to receive(:compatible?).and_return(false)
25 | end
26 |
27 | it 'creates a Local Fetcher object' do
28 | expect(instance).to be_nil
29 | end
30 | end
31 | end
32 |
33 | describe PDK::Template::Renderer::AbstractRenderer do
34 | subject(:renderer) { described_class.new(template_path, template_uri, pdk_context) }
35 |
36 | it 'responds to template_root' do
37 | expect(renderer.template_root).to eq(template_path)
38 | end
39 |
40 | it 'responds to template_uri' do
41 | expect(renderer.template_uri).to eq(template_uri)
42 | end
43 |
44 | it 'responds to context' do
45 | expect(renderer.context).to eq(pdk_context)
46 | end
47 |
48 | it 'responds to has_single_item?' do
49 | expect(renderer.has_single_item?(nil)).to be false
50 | end
51 |
52 | it 'responds to render' do
53 | expect(renderer.render(nil, nil, nil)).to be_nil
54 | end
55 |
56 | it 'responds to render_single_item' do
57 | expect(renderer.render_single_item(nil, nil)).to be_nil
58 | end
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/spec/unit/pdk/template/template_dir_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/template/template_dir'
3 |
4 | describe PDK::Template::TemplateDir do
5 | subject(:template_dir) { described_class.new(template_uri, template_path, pdk_context, renderer) }
6 |
7 | let(:template_uri) { PDK::Util::TemplateURI.new(PDK::Util::TemplateURI::PDK_TEMPLATE_URL) }
8 | let(:template_path) { '/some/path' }
9 | let(:pdk_context) { PDK::Context::None.new(nil) }
10 | let(:renderer) { instance_double(PDK::Template::Renderer::AbstractRenderer) }
11 |
12 | describe '#instance' do
13 | it 'creates a TemplateDir object' do
14 | expect(described_class.instance(template_uri, template_path, pdk_context, renderer)).to be_a(PDK::Template::TemplateDir) # rubocop:disable RSpec/DescribedClass No, this is correct
15 | end
16 | end
17 |
18 | context 'when not passed a renderer' do
19 | subject(:template_dir) { described_class.new(template_uri, template_path, pdk_context) }
20 |
21 | it 'tries to create a renderer' do
22 | expect(PDK::Template::Renderer).to receive(:instance).with(template_uri, template_path, pdk_context).and_return(renderer)
23 |
24 | template_dir
25 | end
26 |
27 | context 'when a renderer could not be found' do
28 | before do
29 | expect(PDK::Template::Renderer).to receive(:instance).with(template_uri, template_path, pdk_context).and_return(nil)
30 | end
31 |
32 | it 'raises a RuntimeError' do
33 | expect { template_dir }.to raise_error(RuntimeError, /Could not find a compatible/)
34 | end
35 | end
36 | end
37 |
38 | it 'has a uri method' do
39 | expect(template_dir.uri).to be(template_uri)
40 | end
41 |
42 | it 'has a path method' do
43 | expect(template_dir.path).to be(template_path)
44 | end
45 |
46 | it 'has a metadata method' do
47 | expect(template_dir.metadata).to eq({})
48 | end
49 |
50 | it 'delegates a render method' do
51 | expect(renderer).to receive(:render)
52 | template_dir.render(nil, nil, nil)
53 | end
54 |
55 | it 'delegates a render_single_item method' do
56 | expect(renderer).to receive(:render_single_item)
57 | template_dir.render_single_item(nil, nil)
58 | end
59 |
60 | it 'delegates a has_single_item? method' do
61 | expect(renderer).to receive(:has_single_item?)
62 | template_dir.has_single_item?(nil)
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/spec/unit/pdk/template_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/template'
3 |
4 | describe PDK::Template do
5 | let(:pdk_context) { PDK::Context::None.new(nil) }
6 | let(:template_uri) { PDK::Util::TemplateURI.new(PDK::Util::TemplateURI::PDK_TEMPLATE_URL) }
7 |
8 | describe '.with' do
9 | context 'when not passed a block' do
10 | it 'raises an ArgumentError' do
11 | expect do
12 | described_class.with(template_uri, pdk_context)
13 | end.to raise_error(ArgumentError, /must be passed a block/i)
14 | end
15 | end
16 |
17 | context 'when not initialized with a PDK::Util::TemplateURI' do
18 | let(:template_uri) { 'string uri' }
19 |
20 | it 'raises an ArgumentError' do
21 | expect do
22 | described_class.with(template_uri, pdk_context) {}
23 | end.to raise_error(ArgumentError, /must be passed a PDK::Util::TemplateURI/i)
24 | end
25 | end
26 |
27 | context 'when initialized correctly' do
28 | let(:fetcher) { described_class::Fetcher::AbstractFetcher.new(template_uri, {}) }
29 | let(:template_dir) do
30 | described_class::TemplateDir.new(
31 | template_uri,
32 | nil,
33 | pdk_context,
34 | instance_double(described_class::Renderer::AbstractRenderer)
35 | )
36 | end
37 |
38 | before do
39 | expect(described_class::Fetcher).to receive(:with).with(template_uri).and_yield(fetcher)
40 | allow(described_class::TemplateDir).to receive(:instance).with(template_uri, anything, pdk_context).and_return(template_dir)
41 | end
42 |
43 | it 'fetches remote templates' do
44 | described_class.with(template_uri, pdk_context) {}
45 | end
46 |
47 | it 'yields a PDK::Template::TemplateDir' do
48 | expect { |b| described_class.with(template_uri, pdk_context, &b) }.to yield_with_args(template_dir)
49 | end
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/spec/unit/pdk/util/env_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/util/env'
3 | require 'securerandom'
4 |
5 | def on_windows
6 | Gem.win_platform?
7 | end
8 |
9 | describe PDK::Util::Env do
10 | before do
11 | ENV[env_name] = env_val
12 | end
13 |
14 | after do
15 | ENV.delete(env_name)
16 | end
17 |
18 | let(:env_name) { "#{SecureRandom.hex(10)}ABCabc" }
19 | let(:env_val) { 'PDK::Util::Env test value' }
20 | let(:upcase_name) { env_name.upcase }
21 | let(:downcase_name) { env_name.upcase }
22 |
23 | describe '[]' do
24 | it 'is case insensitive on Windows platform', if: on_windows do
25 | expect(described_class[env_name]).to eq(env_val)
26 | expect(described_class[downcase_name]).to eq(env_val)
27 | expect(described_class[upcase_name]).to eq(env_val)
28 | end
29 |
30 | it 'is case sensitive on non-Windows platform', unless: on_windows do
31 | expect(described_class[env_name]).to eq(env_val)
32 | expect(described_class[downcase_name]).to be_nil
33 | expect(described_class[upcase_name]).to be_nil
34 | end
35 | end
36 |
37 | describe '[]=' do
38 | let(:new_val) { 'New PDK::Util::Env test value' }
39 |
40 | before do
41 | # Order is important here.
42 | ENV.delete(upcase_name)
43 | ENV[env_name] = env_val
44 | expect(described_class[env_name]).to eq(env_val)
45 | end
46 |
47 | after do
48 | ENV.delete(upcase_name)
49 | end
50 |
51 | it 'is case insensitive on Windows platform', if: on_windows do
52 | described_class[upcase_name] = new_val
53 | expect(described_class[env_name]).to eq(new_val)
54 | expect(described_class[upcase_name]).to eq(new_val)
55 | end
56 |
57 | it 'is case sensitive on non-Windows platform', unless: on_windows do
58 | described_class[upcase_name] = new_val
59 | expect(described_class[env_name]).to eq(env_val)
60 | expect(described_class[upcase_name]).to eq(new_val)
61 | end
62 | end
63 |
64 | describe '.key?' do
65 | let(:new_val) { 'New PDK::Util::Env test value' }
66 |
67 | it 'is case insensitive on Windows platform', if: on_windows do
68 | expect(described_class.key?(env_name)).to be true
69 | expect(described_class.key?(downcase_name)).to be true
70 | expect(described_class.key?(upcase_name)).to be true
71 | end
72 |
73 | it 'is case sensitive on non-Windows platform', unless: on_windows do
74 | expect(described_class.key?(env_name)).to be true
75 | expect(described_class.key?(downcase_name)).to be false
76 | expect(described_class.key?(upcase_name)).to be false
77 | end
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/spec/unit/pdk/util/version_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/util/version'
3 |
4 | describe PDK::Util::Version do
5 | context 'Getting the version_string' do
6 | subject(:version_string) { described_class.version_string }
7 |
8 | it { is_expected.not_to be_nil }
9 | end
10 |
11 | context 'when running from a checkout' do
12 | before do
13 | allow(PDK::Util).to receive(:find_upwards).and_return('/tmp/package/PDK_VERSION')
14 | allow(PDK::Util::Filesystem).to receive(:exist?).with('/tmp/package/PDK_VERSION').and_return(false)
15 | allow(PDK::Util::Filesystem).to receive(:directory?).with(/.git\Z/).and_return(true)
16 |
17 | result = instance_double(Hash)
18 | allow(result).to receive(:[]).with(:stdout).and_return('git_hash')
19 | allow(result).to receive(:[]).with(:exit_code).and_return(0)
20 |
21 | allow(PDK::Util::Git).to receive(:git).with('--git-dir', /.git\Z/, 'describe', '--all', '--long', '--always').and_return(result)
22 | end
23 |
24 | describe '#git_ref' do
25 | it { expect(described_class.git_ref).to eq 'git_hash' }
26 | end
27 |
28 | describe '#pkg_sha' do
29 | it { expect(described_class.pkg_sha).to be_nil }
30 | end
31 | end
32 |
33 | context 'when running from a package' do
34 | before do
35 | allow(PDK::Util).to receive(:find_upwards).and_return('/tmp/package/PDK_VERSION')
36 | allow(PDK::Util::Filesystem).to receive(:exist?).with('/tmp/package/PDK_VERSION').and_return(true)
37 | allow(PDK::Util::Filesystem).to receive(:read_file).with('/tmp/package/PDK_VERSION').and_return('0.1.2.3.4.pkg_hash')
38 | allow(PDK::Util::Filesystem).to receive(:directory?).with(/.git\Z/).and_return(false)
39 | expect(PDK::CLI::Exec).not_to receive(:git)
40 | end
41 |
42 | describe '#git_ref' do
43 | it { expect(described_class.git_ref).to be_nil }
44 | end
45 |
46 | describe '#pkg_sha' do
47 | it { expect(described_class.pkg_sha).to eq 'pkg_hash' }
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/spec/unit/pdk/validate/control_repo/control_repo_validator_group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/validate/control_repo/control_repo_validator_group'
3 |
4 | describe PDK::Validate::ControlRepo::ControlRepoValidatorGroup do
5 | subject(:validator) { described_class.new(validator_context, validator_options) }
6 |
7 | let(:validator_context) { nil }
8 | let(:validator_options) { {} }
9 |
10 | describe '.name' do
11 | subject { validator.name }
12 |
13 | it { is_expected.to eq('control-repo') }
14 | end
15 |
16 | it_behaves_like 'only valid in specified PDK contexts', PDK::Context::ControlRepo
17 |
18 | describe '.validators' do
19 | subject { validator.validators }
20 |
21 | it { is_expected.not_to be_empty }
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/spec/unit/pdk/validate/metadata/metadata_validator_group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/validate/metadata/metadata_validator_group'
3 |
4 | describe PDK::Validate::Metadata::MetadataValidatorGroup do
5 | describe '.name' do
6 | subject { described_class.new.name }
7 |
8 | it { is_expected.to eq('metadata') }
9 | end
10 |
11 | describe '.validators' do
12 | subject { described_class.new.validators }
13 |
14 | it { is_expected.not_to be_empty }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/unit/pdk/validate/puppet/puppet_validator_group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/validate/puppet/puppet_validator_group'
3 |
4 | describe PDK::Validate::Puppet::PuppetValidatorGroup do
5 | describe '.name' do
6 | subject { described_class.new.name }
7 |
8 | it { is_expected.to eq('puppet') }
9 | end
10 |
11 | describe '.validators' do
12 | subject { described_class.new.validators }
13 |
14 | it { is_expected.not_to be_empty }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/unit/pdk/validate/ruby/ruby_validator_group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/validate/ruby/ruby_validator_group'
3 |
4 | describe PDK::Validate::Ruby::RubyValidatorGroup do
5 | describe '.name' do
6 | subject { described_class.new.name }
7 |
8 | it { is_expected.to eq('ruby') }
9 | end
10 |
11 | describe '.validators' do
12 | subject { described_class.new.validators }
13 |
14 | it { is_expected.not_to be_empty }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/unit/pdk/validate/tasks/tasks_validator_group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/validate/tasks/tasks_validator_group'
3 |
4 | describe PDK::Validate::Tasks::TasksValidatorGroup do
5 | describe '.name' do
6 | subject { described_class.new.name }
7 |
8 | it { is_expected.to eq('tasks') }
9 | end
10 |
11 | describe '.validators' do
12 | subject { described_class.new.validators }
13 |
14 | it { is_expected.not_to be_empty }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/unit/pdk/validate/yaml/yaml_validator_group_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/validate/yaml/yaml_validator_group'
3 |
4 | describe PDK::Validate::YAML::YAMLValidatorGroup do
5 | describe '.name' do
6 | subject { described_class.new.name }
7 |
8 | it { is_expected.to eq('yaml') }
9 | end
10 |
11 | describe '.validators' do
12 | subject { described_class.new.validators }
13 |
14 | it { is_expected.not_to be_empty }
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/spec/unit/pdk/version_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 | require 'pdk/version'
3 |
4 | describe 'PDK version string' do
5 | it 'has major minor and patch numbers' do
6 | expect(PDK::VERSION).to match(/^[0-9]+\.[0-9]+\.[0-9]+/)
7 | end
8 | end
9 |
--------------------------------------------------------------------------------