puppetlabs-onceover-codequality 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # PuppetlabsOnceover::CodeQuality
2
+
3
+ This is the Code Quality plugin for [puppetlabs-onceover](https://github.com/puppetlabs/puppetlabs-onceover), _The gateway drug to automated infrastructure testing with Puppet_
4
+
5
+ ## What does it do?
6
+
7
+ The plugin checks your control repository for:
8
+
9
+ * Linting
10
+ * Syntax
11
+
12
+ And then generates documentation using [Puppet Strings](https://github.com/puppetlabs/puppet-strings/)
13
+
14
+ For sure you can hack around with rake/make and hack something up each time but aint nobody got time for that!
15
+
16
+ ## Installation
17
+
18
+ Install the `puppetlabs-onceover-codequality` gem by adding it to your `Gemfile` or by running the following command:
19
+
20
+ ```shell
21
+ gem install puppetlabs-onceover-codequality
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Installing the codequality gem creates a new item within puppetlabs-onceover's `run` command: `codequality`. See `puppetlabs-onceover run codequality --help` for all available options.
27
+
28
+ The command will return `1` to the system if any tests fail, otherwise `0`, which makes it perfect to include in build pipelines.
29
+
30
+ ### Check all code in the control repository for Lint and Syntax errors
31
+
32
+ ```shell
33
+ puppetlabs-onceover run codequality
34
+ ```
35
+
36
+ ### Skip Lint check
37
+
38
+ ```shell
39
+ puppetlabs-onceover run codequality --no_lint
40
+ ```
41
+
42
+ ### Skip Puppet syntax check
43
+
44
+ ```shell
45
+ puppetlabs-onceover run codequality --no_syntax
46
+ ```
47
+
48
+ ### Skip Puppetfile syntax check
49
+
50
+ ```shell
51
+ puppetlabs-onceover run codequality --no_puppetfile
52
+ ```
53
+
54
+ ### Skip documentation generation
55
+
56
+ ```shell
57
+ puppetlabs-onceover run codequality --no_docs
58
+ ```
59
+
60
+ ### Use custom lint setting
61
+
62
+ Create a file `puppet-lint.rc` in the directory you run `puppetlabs-onceover codequality`
63
+ from and it will be automatically used by Puppet Lint. If missing, the built-in
64
+ defaults from `lib/puppetlabs-onceover/codequality/lint.rb` will be used.
65
+
66
+
67
+ ## Sample output
68
+
69
+ ### All clear
70
+
71
+ ```shell
72
+ $ puppetlabs-onceover run codequality
73
+ INFO -> Checking for lint...
74
+ INFO -> checking manifests
75
+ INFO -> checking site/role
76
+ INFO -> Checking syntax...
77
+ ---> syntax:manifests
78
+ ---> syntax:templates
79
+ ---> syntax:hiera:yaml
80
+ INFO -> Code Quality tests passed, have a nice day
81
+ ```
82
+
83
+ ### Lint and syntax errors
84
+
85
+ ```shell
86
+ $ puppetlabs-onceover run codequality
87
+ INFO -> Checking for lint...
88
+ INFO -> checking manifests
89
+ INFO -> checking site/role
90
+ ./manifests/example.pp - WARNING: class not documented on line 1
91
+ ./manifests/webserver.pp - WARNING: class not documented on line 1
92
+ ./manifests/database_server.pp - WARNING: class not documented on line 1
93
+ ERROR -> Lint test failed, see previous errors
94
+ INFO -> Checking syntax...
95
+ ---> syntax:manifests
96
+ Could not parse for environment *root*: Syntax error at 'ensure' at /home/geoff/crud/control-repo/manifests/bogus.pp:2:3
97
+ ERROR -> Syntax test failed, see previous errors
98
+ ERROR -> Code Quality tests failed, see previous error
99
+ $ echo $?
100
+ 1
101
+ ```
102
+
103
+ ## FAQ
104
+
105
+ **`WARN: Unresolved specs during Gem::Specification.reset`?**
106
+
107
+ I get these errors when I run `puppetlabs-onceover run codequality` but everything seems to work, what gives?:
108
+
109
+ ```shell
110
+ WARN: Unresolved specs during Gem::Specification.reset:
111
+ rake (>= 0)
112
+ hiera (< 4, >= 2.0)
113
+ gettext (>= 3.0.2)
114
+ WARN: Clearing out unresolved specs.
115
+ Please report a bug if this causes problems.
116
+ ```
117
+
118
+ Beats me - something to do with rubygems. The best way to beat this message is to use [bundler](https://github.com/bundler/bundler) which basically you should already be doing anyway (for your own sanity):
119
+
120
+ ```shell
121
+ bundle exec puppetlabs-onceover run codequality
122
+ ```
123
+
124
+ **What are you using under-the-hood?**
125
+
126
+ * [puppet-lint](https://github.com/rodjek/puppet-lint)
127
+ * [puppet-syntax](https://github.com/voxpupuli/puppet-syntax)
128
+ * [puppet-strings](https://github.com/puppetlabs/puppet-strings/)
129
+ * [r10k](https://github.com/puppetlabs/r10k/)
130
+
131
+ ## Development
132
+
133
+ ### Writing your own onceover plugins
134
+
135
+ Have a look at [helloworld plugin](https://github.com/declarativesystems/onceover-helloworld) if you want to have a go at writing a plugin of your own.
136
+
137
+ ### How to run tests
138
+
139
+ ```shell
140
+ bundle exec rake spec
141
+ ```
142
+
143
+ ## Contributing
144
+
145
+ * Bug reports and pull requests welcome
146
+
147
+ ## Transfer Notice
148
+
149
+ This project was originally authored by dylanratcliffe. The maintainer preferred that Vox Pupuli take ownership of the project for future improvement and maintenance.
150
+ Existing pull requests and issues were transferred over, please fork and continue to contribute here instead of voxpupuli/onceover-codequality.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ begin
9
+ require 'rubygems'
10
+ require 'github_changelog_generator/task'
11
+ rescue LoadError
12
+ # Do nothing if no required gem installed
13
+ else
14
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
15
+ config.exclude_labels = %w[duplicate question invalid wontfix wont-fix skip-changelog github_actions]
16
+ config.user = 'puppetlabs'
17
+ config.project = 'puppetlabs-onceover-codequality'
18
+ gem_version = Gem::Specification.load("#{config.project}.gemspec").version
19
+ config.future_release = "v#{gem_version}"
20
+ end
21
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "puppetlabs-onceover/codequality"
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
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
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
@@ -0,0 +1,65 @@
1
+ require "puppetlabs-onceover/codequality/lint"
2
+ require "puppetlabs-onceover/codequality/syntax"
3
+ require "puppetlabs-onceover/codequality/docs"
4
+ require "puppetlabs-onceover/codequality/puppetfile"
5
+ require "puppetlabs-onceover/codequality/environment"
6
+ require "puppetlabs-onceover/codequality/executor"
7
+ require "puppetlabs-onceover/codequality/formatter"
8
+ class PuppetlabsOnceover
9
+ module CodeQuality
10
+ class CLI
11
+ def self.command
12
+ @cmd ||= Cri::Command.define do
13
+ name 'codequality'
14
+ usage 'codequality [--no-syntax] [--no-lint] [--no-docs] [--html-docs] [--no-puppetfile]'
15
+ summary "Code Quality checking for onceover"
16
+ description <<-DESCRIPTION
17
+ Check files in your Control Repository for Lint and Syntax errors
18
+ DESCRIPTION
19
+
20
+ flag nil, :no_lint, 'Do not check for lint errors', :argument => :optional
21
+ flag nil, :no_puppetfile, 'Do not check Puppetfile for syntax errors', :argument => :optional
22
+ flag nil, :no_syntax, 'Do not check for syntax errors', :argument => :optional
23
+ flag nil, :no_docs, 'Do not generate documentation (puppet-strings) for local modules', :argument => :optional
24
+ flag nil, :html_docs, 'Generate docs in HTML format instead of markdown', :argument => :optional
25
+
26
+ run do |opts, args, cmd|
27
+ no_lint = opts[:no_lint] || false
28
+ no_syntax = opts[:no_syntax] || false
29
+ no_docs = opts[:no_docs] || false
30
+ no_puppetfile = opts[:no_puppetfile] || false
31
+ html_docs = opts[:html_docs] || false
32
+ status = true
33
+
34
+ logger.info "Running Code Quality tests"
35
+
36
+ if !no_puppetfile
37
+ status &= PuppetlabsOnceover::CodeQuality::Puppetfile.puppetfile
38
+ end
39
+
40
+ if !no_syntax
41
+ status &= PuppetlabsOnceover::CodeQuality::Syntax.puppet
42
+ end
43
+
44
+ if !no_lint
45
+ status &= PuppetlabsOnceover::CodeQuality::Lint.puppet
46
+ end
47
+
48
+ if !no_docs
49
+ status &= PuppetlabsOnceover::CodeQuality::Docs.puppet_strings(html_docs)
50
+ end
51
+
52
+ if status
53
+ logger.info "Code Quality tests PASSED, have a nice day"
54
+ else
55
+ logger.error "Code Quality tests FAILED, see previous error(s)"
56
+ exit 1
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ PuppetlabsOnceover::CLI::Run.command.add_command(PuppetlabsOnceover::CodeQuality::CLI.command)
@@ -0,0 +1,28 @@
1
+ # Support for generating documentation using Puppet Strings for each module
2
+ # listed in the environment.conf modulepath
3
+ class PuppetlabsOnceover
4
+ module CodeQuality
5
+ module Docs
6
+ def self.puppet_strings(html_docs)
7
+ status = true
8
+ format = html_docs ? "--markup html" : "--format markdown"
9
+
10
+ CodeQuality::Environment.get_site_dirs.each { |local_mod_dir|
11
+ if Dir.exist?(local_mod_dir)
12
+ Dir.glob("#{local_mod_dir}/*/") { |dir|
13
+ Dir.chdir(dir) {
14
+ CodeQuality::Formatter.start_test("Generate documentation in #{dir}")
15
+ # puppet strings prints useful metrics so don't swallow its output
16
+ output, ok = CodeQuality::Executor.run("puppet strings generate #{format}")
17
+ status &= ok
18
+ CodeQuality::Formatter.end_test(output, ok, true)
19
+ }
20
+ }
21
+ end
22
+ }
23
+
24
+ status
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ class PuppetlabsOnceover
2
+ module CodeQuality
3
+ module Environment
4
+ ENVIRONMENT_CONF = "environment.conf"
5
+
6
+ # Latest best practice is to change the name of `site` to be
7
+ # `site-modules`. This means we need to start processing `environment.conf`
8
+ # to extract this...
9
+ def self.get_site_dirs()
10
+ if File.exist?(ENVIRONMENT_CONF)
11
+ # modulepath = site-modules:modules:$basemodulepath
12
+ modulepath = open(ENVIRONMENT_CONF) { |f| f.each_line.find { |line| line.include?("modulepath") } }
13
+
14
+ begin
15
+ environments = modulepath.split("=")[1].split(":").reject { |e|
16
+ # reject any elements containing interpolation or referencing modules
17
+ # loaded by r10k
18
+ e =~ /\$/ || e == "modules" || e =~ /[\\.\/]/
19
+ }.map { |e|
20
+ e.strip
21
+ }
22
+ rescue NoMethodError => e
23
+ raise "Malformed environment configuration: #{ENVIRONMENT_CONF}"
24
+ end
25
+ else
26
+ raise "Missing environment configuration: #{ENVIRONMENT_CONF}"
27
+ end
28
+
29
+ environments
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ class PuppetlabsOnceover
2
+ module CodeQuality
3
+ module Executor
4
+ # Capture all program output and check the exit status was zero
5
+ def self.run(*command)
6
+ output, s = Open3.capture2e(*command)
7
+ ok = s.exitstatus.zero?
8
+
9
+ return output, ok
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ class PuppetlabsOnceover
2
+ module CodeQuality
3
+ module Formatter
4
+ START_TEST = "Checking".freeze
5
+ MSG_TEST_OK = "...OK".freeze
6
+
7
+ def self.start_test(test_name)
8
+ logger.info "Checking #{test_name}..."
9
+ end
10
+
11
+ def self.end_test(output, ok, show_output = false)
12
+ if !ok || show_output
13
+ output.each_line do |line|
14
+ if line.start_with?('::') # This is special syntax for GitHub annotations
15
+ puts(line)
16
+ else
17
+ logger.info("\t#{line.chomp}")
18
+ end
19
+ end
20
+ end
21
+
22
+ if ok
23
+ logger.info "...OK"
24
+ else
25
+ logger.error "...FAILED"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,45 @@
1
+ class PuppetlabsOnceover
2
+ module CodeQuality
3
+ module Lint
4
+ # Apply linting to the manifests directory and each module under `site`
5
+ LINT_PATHS = [
6
+ "manifests",
7
+ ]
8
+
9
+ # use our default options unless user has created own settings
10
+ if !File.exist? ".puppet-lint.rc"
11
+ LINT_OPTIONS = [
12
+ "--relative",
13
+ "--fail-on-warnings",
14
+ "--no-double_quoted_strings-check",
15
+ "--no-80chars-check",
16
+ "--no-variable_scope-check",
17
+ "--no-quoted_booleans-check",
18
+ ].freeze
19
+ else
20
+ LINT_OPTIONS = [].freeze
21
+ end
22
+
23
+ def self.puppet
24
+ status = true
25
+
26
+ # wait until runtime to scan directories for unit tests
27
+ lint_paths = LINT_PATHS.concat(
28
+ CodeQuality::Environment.get_site_dirs.each { |site_dir|
29
+ Dir.glob("#{site_dir}/*").select { |f| File.directory? f }
30
+ }
31
+ )
32
+ lint_paths.each { |p|
33
+ if Dir.exist?(p)
34
+ CodeQuality::Formatter.start_test("lint in #{p}")
35
+ output, ok = CodeQuality::Executor.run("puppet-lint #{LINT_OPTIONS.join ' '} #{p}")
36
+ status &= ok
37
+ CodeQuality::Formatter.end_test(output, ok)
38
+ end
39
+ }
40
+
41
+ status
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ # Support for checking syntax of Puppetfile
2
+ class PuppetlabsOnceover
3
+ module CodeQuality
4
+ module Puppetfile
5
+ def self.puppetfile
6
+ status = true
7
+ if File.exist?("Puppetfile")
8
+ CodeQuality::Formatter.start_test("Puppetfile")
9
+ output, ok = CodeQuality::Executor.run("r10k puppetfile check")
10
+
11
+ status &= ok
12
+
13
+ CodeQuality::Formatter.end_test(output, ok)
14
+ else
15
+ logger.warn("No Puppetfile found... continuing")
16
+ end
17
+
18
+ status
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ class PuppetlabsOnceover
2
+ module CodeQuality
3
+ module Syntax
4
+ def self.puppet
5
+ status = true
6
+
7
+ #
8
+ # puppet-syntax
9
+ #
10
+
11
+ CodeQuality::Formatter.start_test("puppetlabs-syntax rake task")
12
+ # puppetlabs-syntax seems to assign $stdout/$stderr internally in ways that
13
+ # prevent capturing output. As a nasty hack, run it as inline ruby and
14
+ # capture the output from the process...
15
+ inline_ruby = <<-RUBY_CODE
16
+ require 'puppetlabs-syntax/tasks/puppetlabs-syntax'
17
+ PuppetlabsSyntax.exclude_paths = ['vendor/**/*','spec/templates/*.erb']
18
+ Rake::Task['syntax'].invoke
19
+ RUBY_CODE
20
+ # output, s = Open3.capture2e("ruby", "-e", inline_ruby)
21
+ output, ok = CodeQuality::Executor.run("ruby", "-e", inline_ruby)
22
+ status &= ok
23
+ CodeQuality::Formatter.end_test(output, ok)
24
+
25
+ #
26
+ # python yaml
27
+ #
28
+
29
+ # Python gives us "better" validation of YAML data then ruby, eg:
30
+ # ```yaml
31
+ # foo: bar
32
+ # baz: clive
33
+ # ```
34
+ #
35
+ # would parse only the foo key in ruby, throwing away the baz key due to
36
+ # a perceived negative indent, whereas python would tell you to fix the
37
+ # file and make it consistent. This is yaml implementation dependent but
38
+ # users would be advised to fix the file, so lets _also_ validate yaml
39
+ # files with python if available on our path...
40
+ if system("python --version && python -c 'import yaml'", :err => File::NULL)
41
+ CodeQuality::Formatter.start_test("Additional python YAML validation")
42
+ script = File.join(File.dirname(File.expand_path(__FILE__)), "../../../res/validate_yaml.py")
43
+ output, ok = CodeQuality::Executor.run("python #{script}")
44
+ status &= ok
45
+ CodeQuality::Formatter.end_test(output, ok)
46
+ else
47
+ logger.warn("Please install python and pyyaml for enhanced YAML validation (pip install pyyaml)")
48
+ end
49
+
50
+ status
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ class PuppetlabsOnceover
2
+ module CodeQuality
3
+ VERSION = "1.2.1".freeze
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ require "puppetlabs-onceover/codequality/version"
2
+ require "puppetlabs-onceover/codequality/cli"
3
+
4
+ class PuppetlabsOnceover
5
+ module CodeQuality
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "puppetlabs-onceover/codequality/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "puppetlabs-onceover-codequality"
9
+ spec.version = PuppetlabsOnceover::CodeQuality::VERSION
10
+ spec.authors = ["Puppet, Inc."]
11
+ spec.email = ["modules-team@puppet.com"]
12
+ spec.license = "Apache-2.0"
13
+
14
+ spec.summary = %q{Lint and syntax validation for puppetlabs-onceover}
15
+ spec.homepage = "https://github.com/puppetlabs/puppetlabs-onceover-codequality"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.2')
23
+
24
+ spec.add_dependency 'puppetlabs-onceover', '~> 5.0'
25
+ spec.add_dependency 'puppetlabs-syntax', '~> 7.0'
26
+ spec.add_dependency 'puppet-lint', '~> 5.1'
27
+ spec.add_dependency 'puppet-strings', '>= 5.0', '< 8.0'
28
+ spec.add_dependency 'rake', '~> 13.3'
29
+ spec.add_dependency 'rspec', '~> 3.13'
30
+ end
@@ -0,0 +1,25 @@
1
+ import sys
2
+ import os
3
+ import re
4
+
5
+ status = 0
6
+ try:
7
+ import yaml
8
+ for root, dirs, files in os.walk("."):
9
+ for f in files:
10
+ # we want yaml files that are not in hidden (.) dirs or vendor directory. account for / vs \ on windows/linux
11
+ target = os.path.join(root, f)
12
+ if target.endswith(".yaml") and not re.match("^\.(/|\\\)(\.|vendor)", target):
13
+ try:
14
+ yaml.safe_load(open(target,"r"))
15
+ except Exception as e:
16
+ status = 1
17
+ print("YAML syntax error in %s: %s" % (target, str(e)))
18
+
19
+ except ImportError as e:
20
+ status = 1
21
+ print("Please install the python-yaml library: pip install pyyaml")
22
+
23
+
24
+ sys.exit(status)
25
+