gergich 1.2.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/{bin → exe}/gergich +0 -0
  3. data/{bin → exe}/master_bouncer +0 -0
  4. data/lib/gergich/capture/androidlint_capture.rb +8 -3
  5. data/lib/gergich/capture/brakeman_capture.rb +3 -2
  6. data/lib/gergich/capture/eslint_capture.rb +2 -1
  7. data/lib/gergich/capture/flake8_capture.rb +1 -1
  8. data/lib/gergich/capture/i18nliner_capture.rb +1 -1
  9. data/lib/gergich/capture/rubocop_capture.rb +19 -3
  10. data/lib/gergich/capture/shellcheck_capture.rb +2 -1
  11. data/lib/gergich/capture/stylelint_capture.rb +2 -5
  12. data/lib/gergich/capture/swiftlint_capture.rb +1 -1
  13. data/lib/gergich/capture/tsc_capture.rb +27 -0
  14. data/lib/gergich/capture/yamllint_capture.rb +8 -2
  15. data/lib/gergich/capture.rb +12 -5
  16. data/lib/gergich/cli/gergich.rb +7 -4
  17. data/lib/gergich/cli/master_bouncer.rb +5 -6
  18. data/lib/gergich/cli.rb +1 -1
  19. data/lib/gergich.rb +21 -25
  20. metadata +43 -33
  21. data/LICENSE +0 -20
  22. data/README.md +0 -178
  23. data/bin/check_coverage +0 -8
  24. data/bin/run_tests.sh +0 -22
  25. data/spec/gergich/capture/androidlint_capture_spec.rb +0 -61
  26. data/spec/gergich/capture/brakeman_capture_spec.rb +0 -91
  27. data/spec/gergich/capture/custom_capture_spec.rb +0 -41
  28. data/spec/gergich/capture/eslint_capture_spec.rb +0 -31
  29. data/spec/gergich/capture/flake8_capture_spec.rb +0 -23
  30. data/spec/gergich/capture/i18nliner_capture_spec.rb +0 -25
  31. data/spec/gergich/capture/rubocop_capture_spec.rb +0 -75
  32. data/spec/gergich/capture/shellcheck_capture_spec.rb +0 -83
  33. data/spec/gergich/capture/stylelint_capture_spec.rb +0 -54
  34. data/spec/gergich/capture/swiftlint_capture_spec.rb +0 -42
  35. data/spec/gergich/capture/yamllint_capture_spec.rb +0 -31
  36. data/spec/gergich/capture_spec.rb +0 -75
  37. data/spec/gergich_spec.rb +0 -396
  38. data/spec/spec_helper.rb +0 -92
  39. data/spec/support/capture_shared_examples.rb +0 -19
data/README.md DELETED
@@ -1,178 +0,0 @@
1
- # Gergich
2
-
3
- [![Gem Version](https://badge.fury.io/rb/gergich.svg)](https://rubygems.org/gems/gergich)
4
- [![Build Status](https://travis-ci.org/instructure/gergich.svg?branch=master)](https://travis-ci.org/instructure/gergich)
5
-
6
- Gergich is a command-line tool (and ruby lib) for easily posting comments
7
- on a [Gerrit](https://www.gerritcodereview.com/) review from a CI
8
- environment. It can be wired up to linters (rubocop, eslint, etc.) so that
9
- you can get nice inline comments right on the Gerrit review. That way
10
- developers don't have to go digging through CI logs to see why their
11
- builds failed.
12
-
13
- ## How does it work?
14
-
15
- Gergich maintains a little sqlite db of any draft comments/labels/etc.
16
- for the current patchset (defined by revision+ChangeId). This way
17
- different processes can all contribute to the review. For example,
18
- various linters add inline comments, and when the CI build finishes,
19
- Gergich publishes the review to Gerrit.
20
-
21
- ## Limitations
22
-
23
- Because everything is synchronized/stored in a local sqlite db, you
24
- should only call Gergich from a single box/build per patchset unless you
25
- have a unique `GERGICH_COMMENT_PREFIX` set for each box/build per patchset.
26
- Gergich does a check when publishing to ensure he hasn't already posted on
27
- this patchset before (w/ the same `GERGICH_COMMENT_PREFIX`); if he has,
28
- publish will be a no-op. This protects against reposts (say, on a retrigger),
29
- but it does mean that you shouldn't have completely different builds posting
30
- Gergich comments on the same revision, unless you set up different
31
- credentials for each.
32
-
33
- ## Installation
34
-
35
- Add the following to your Gemfile (perhaps in your `:test` group?):
36
-
37
- ```ruby
38
- gem "gergich"
39
- ```
40
-
41
- To use Gergich, you'll need a Gerrit user whose credentials it'll use
42
- (ideally not your own). With your shiny new username and password in hand,
43
- set `GERGICH_USER` and `GERGICH_KEY` accordingly in your CI environment.
44
-
45
- Additionally, Gergich needs to know where your Gerrit installation
46
- lives, so be sure to set `GERRIT_BASE_URL` (e.g.
47
- `https://gerrit.example.com`) or `GERRIT_HOST` (e.g. `gerrit.example.com`).
48
-
49
- Lastly, if you have no .git directory in CI land (say if you are building
50
- in docker and want to keep your images small), you also need to set
51
- `GERRIT_CHANGE_ID` and `GERRIT_PATCHSET_REVISION`. If you use Jenkins and
52
- the gerrit-trigger plugin, typcially all `GERRIT_*` vars will already be
53
- set, it's just a matter of plumbing them down to docker.
54
-
55
- ## Usage
56
-
57
- Run `gergich help` for detailed information about all supported commands.
58
- In your build scripts, you'll typically be using `gergich comment`,
59
- `gergich capture` and `gergich publish`. Comments are stored locally in a
60
- sqlite database until you publish. This way you can queue up comments from
61
- many disparate processes. Comments are published to `HEAD`'s corresponding
62
- patchset in Gerrit (based on Change-Id + `<sha>`)
63
-
64
- ### `gergich comment <comment_data>`
65
-
66
- `<comment_data>` is a JSON object (or array of objects). Each comment
67
- object should have the following properties:
68
-
69
- * **path** - the relative file path, e.g. "app/models/user.rb"
70
- * **position** - either a number (line) or an object (range). If an object,
71
- must have the following numeric properties:
72
- * start_line
73
- * start_character
74
- * end_line
75
- * end_character
76
- * **message** - the text of the comment
77
- * **severity** - `"info"|"warn"|"error"` - this will automatically prefix
78
- the comment (e.g. `"[ERROR] message here"`), and the most severe comment
79
- will be used to determine the overall `Code-Review` score (0, -1, or -2
80
- respectively)
81
-
82
- Note that a cover message and `Code-Review` score will be inferred from the
83
- most severe comment.
84
-
85
- #### Examples
86
-
87
- ```bash
88
- gergich comment '{"path":"foo.rb","position":3,"severity":"error",
89
- "message":"ಠ_ಠ"}'
90
- gergich comment '{"path":"bar.rb","severity":"warn",
91
- "position":{"start_line":3,"start_character":5,...},
92
- "message":"¯\_(ツ)_/¯"}'
93
- gergich comment '[{"path":"baz.rb",...}, {...}, {...}]'
94
- ```
95
-
96
- ### `gergich capture <format> <command>`
97
-
98
- For common linting formats, `gergich capture` can be used to automatically
99
- do `gergich comment` calls so you don't have to wire it up yourself.
100
-
101
- `<format>` - One of the following:
102
-
103
- * `brakeman`
104
- * `rubocop`
105
- * `eslint`
106
- * `i18nliner`
107
- * `flake8`
108
- * `stylelint`
109
- * `yamllint`
110
- * `shellcheck` - shellcheck json output
111
- * `custom:<path>:<class_name>` - file path and ruby class_name of a custom
112
- formatter.
113
-
114
- `<command>` - The command to run whose output conforms to `<format>`.
115
- Output from the command will still go to STDOUT, and Gergich will
116
- preserve its exit status. If command is "-", Gergich will simply read
117
- from STDIN and the exit status will always be 0.
118
-
119
- #### Custom formatters:
120
-
121
- To create a custom formatter, create a class that implements a `run`
122
- method that takes a string of command output and returns an array of
123
- comment hashes (see `gergich comment`'s `<comment_data>` format), e.g.
124
-
125
- ```ruby
126
- class MyFormatter
127
- def run(output)
128
- output.scan(/^Oh noes! (.+?):(\d+): (.*)$/).map do |file, line, error|
129
- { path: file, message: error, position: line.to_i, severity: "error" }
130
- end
131
- end
132
- end
133
- ```
134
-
135
- #### Examples:
136
-
137
- ```bash
138
- gergich capture rubocop "bundle exec rubocop"
139
-
140
- gergich capture eslint eslint
141
-
142
- gergich capture i18nliner "rake i18nliner:check"
143
-
144
- gergich capture shellcheck "shellcheck --format json build.sh"
145
-
146
- gergich capture custom:./gergich/xss:Gergich::XSS "node script/xsslint"
147
-
148
- docker-compose run --rm web eslint | gergich capture eslint -
149
- # you might be interested in $PIPESTATUS[0]
150
- ```
151
-
152
- ### `gergich publish`
153
-
154
- Publish all draft comments/labels/messages for this patchset. no-op if
155
- there are none.
156
-
157
- The cover message and `Code-Review` label (e.g. -2) are inferred from the
158
- comments, but labels and messages may be manually set (via `gergich
159
- message` and `gergich labels`)
160
-
161
- ## How do I test my changes?
162
-
163
- Write tests of course, but also be sure to test it end-to-end via the
164
- CLI... Run `gergich` for a list of commands, as well as help for each
165
- command. There's also a `citest` thing that we run on our Jenkins that
166
- ensures each CLI command succeeds, but it doesn't test all branches for
167
- each command.
168
-
169
- After running a given command, you can run `gergich status` to see the
170
- current draft of the review (what will be sent to Gerrit when you do
171
- `gergich publish`).
172
-
173
- You can even do a test `publish` to Gerrit, if you have valid Gerrit
174
- credentials in `GERGICH_USER` / `GERGICH_KEY`. It infers the Gerrit patchset
175
- from the working directory, which may or may not correspond to something
176
- actually in Gerrit, so YMMV. That means you can post to a Gergich commit
177
- in Gerrit, or if you run it from another project's directory, you can post
178
- to its Gerrit revision.
data/bin/check_coverage DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require "simplecov"
5
-
6
- SimpleCov.command_name "check_coverage"
7
- SimpleCov.minimum_coverage 85
8
- SimpleCov.at_exit { SimpleCov.result.format! }
data/bin/run_tests.sh DELETED
@@ -1,22 +0,0 @@
1
- #!/bin/bash
2
-
3
- set -e
4
-
5
- bundle exec rubocop --fail-level autocorrect
6
-
7
- ruby -v | egrep "^ruby 2\.4" && export COVERAGE=1
8
-
9
- bundle exec rspec
10
-
11
- # these actually hit gerrit; only run them in CI land (you can do it
12
- # locally if you set all the docker-compose env vars)
13
- if [[ "$GERRIT_PATCHSET_REVISION" ]]; then
14
- bundle exec bin/gergich citest
15
- bundle exec bin/master_bouncer check
16
- DRY_RUN=1 bundle exec bin/master_bouncer check_all
17
- # ensure gergich works without .git directories
18
- rm -rf .git
19
- bundle exec bin/gergich status
20
- fi
21
-
22
- bundle exec bin/check_coverage
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::AndroidlintCapture do
6
- # rubocop:disable Layout/LineLength
7
- let(:rtl_hardcoded) { 'Consider adding android:drawableStart="@drawable/a_media" to better support right-to-left layouts [RtlHardcoded]' }
8
- let(:rtl_enabled) { "The project references RTL attributes, but does not explicitly enable or disable RTL support with android:supportsRtl in the manifest [RtlEnabled]" }
9
- let(:lint_error) { 'No .class files were found in project "0.0.2", so none of the classfile based checks could be run. Does the project need to be built first? [LintError]' }
10
- let(:unused_quantity) { 'For language "fr" (French) the following quantities are not relevant: few, zero [UnusedQuantity]' }
11
- # rubocop:enable Layout/LineLength
12
- let(:output) do
13
- <<~OUTPUT
14
- /path/to/some.xml:27: Warning: #{rtl_hardcoded}
15
- android:drawableLeft="@drawable/ic_cv_media"/>
16
- ~~~~~~~~~~~~~~~~~~~~
17
-
18
- /path/to/AndroidManifest.xml: Warning: #{rtl_enabled}
19
-
20
- /path/to/library/0.0.2: Error: #{lint_error}
21
-
22
- /path/to/values.xml:5: Warning: #{unused_quantity}
23
- <plurals name="number">
24
- ^
25
-
26
- OUTPUT
27
- end
28
-
29
- let(:comments) do
30
- [
31
- {
32
- path: "/path/to/some.xml",
33
- position: 27,
34
- # rubocop:disable Layout/LineLength
35
- message: "[androidlint] #{rtl_hardcoded}\n\n android:drawableLeft=\"@drawable/ic_cv_media\"/>\n ~~~~~~~~~~~~~~~~~~~~",
36
- # rubocop:enable Layout/LineLength
37
- severity: "warn"
38
- },
39
- {
40
- path: "/path/to/AndroidManifest.xml",
41
- position: 0,
42
- message: "[androidlint] #{rtl_enabled}",
43
- severity: "warn"
44
- },
45
- {
46
- path: "/path/to/library/0.0.2",
47
- position: 0,
48
- message: "[androidlint] #{lint_error}",
49
- severity: "error"
50
- },
51
- {
52
- path: "/path/to/values.xml",
53
- position: 5,
54
- message: "[androidlint] #{unused_quantity}\n\n <plurals name=\"number\">\n ^",
55
- severity: "warn"
56
- }
57
- ]
58
- end
59
-
60
- it_behaves_like "a captor"
61
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::BrakemanCapture do
6
- let(:output) do
7
- File.read(
8
- File.expand_path(File.dirname(__FILE__) + "/brakeman_example.json")
9
- )
10
- end
11
-
12
- let(:comments) do
13
- [
14
- {
15
- path: "app/models/custom_data.rb",
16
- position: 36,
17
- message: <<~MESSAGE.strip,
18
- [brakeman] Attribute Restriction: attr_accessible is recommended over attr_protected
19
- See: http://brakemanscanner.org/docs/warning_types/attribute_restriction/
20
- MESSAGE
21
- severity: "warn"
22
- },
23
- {
24
- path: "app/models/submission_comment.rb",
25
- position: 0,
26
- message: <<~MESSAGE.strip,
27
- [brakeman] Mass Assignment: Potentially dangerous attribute available for mass assignment
28
- Code: :context_id
29
- See: http://brakemanscanner.org/docs/warning_types/mass_assignment/
30
- MESSAGE
31
- severity: "warn"
32
- },
33
- {
34
- path: "app/controllers/context_controller.rb",
35
- position: 60,
36
- message: <<~MESSAGE.strip,
37
- [brakeman] Redirect: Possible unprotected redirect
38
- Code: redirect_to(CanvasKaltura::ClientV3.new.assetSwfUrl(params[:id]))
39
- User Input: params[:id]
40
- See: http://brakemanscanner.org/docs/warning_types/redirect/
41
- MESSAGE
42
- severity: "warn"
43
- },
44
- {
45
- path: "app/views/context/object_snippet.html.erb",
46
- position: 6,
47
- message: <<~MESSAGE.strip,
48
- [brakeman] Cross Site Scripting: Unescaped parameter value
49
- Code: Base64.decode64((params[:object_data] or ""))
50
- User Input: params[:object_data]
51
- See: http://brakemanscanner.org/docs/warning_types/cross_site_scripting
52
- MESSAGE
53
- severity: "warn"
54
- },
55
- {
56
- path: "app/models/account.rb",
57
- position: 795,
58
- message: <<~MESSAGE.strip,
59
- [brakeman] SQL Injection: Possible SQL injection
60
- Code: Account.find_by_sql(Account.sub_account_ids_recursive_sql(parent_account_id))
61
- User Input: Account.sub_account_ids_recursive_sql(parent_account_id)
62
- See: http://brakemanscanner.org/docs/warning_types/sql_injection/
63
- MESSAGE
64
- severity: "error"
65
- },
66
- {
67
- path: "lib/cc/importer/blti_converter.rb",
68
- position: 145,
69
- message: <<~MESSAGE.strip,
70
- [brakeman] SSL Verification Bypass: SSL certificate verification was bypassed
71
- Code: Net::HTTP.new(URI.parse(url).host, URI.parse(url).port).verify_mode = OpenSSL::SSL::VERIFY_NONE
72
- See: http://brakemanscanner.org/docs/warning_types/ssl_verification_bypass/
73
- MESSAGE
74
- severity: "error"
75
- },
76
- {
77
- path: "lib/cc/importer/canvas/quiz_converter.rb",
78
- position: 44,
79
- message: <<~MESSAGE.strip,
80
- [brakeman] Command Injection: Possible command injection
81
- Code: `\#{Qti.get_conversion_command(File.join(qti_folder, "qti_2_1"), qti_folder)}`
82
- User Input: Qti.get_conversion_command(File.join(qti_folder, "qti_2_1"), qti_folder)
83
- See: http://brakemanscanner.org/docs/warning_types/command_injection/
84
- MESSAGE
85
- severity: "warn"
86
- }
87
- ]
88
- end
89
-
90
- it_behaves_like "a captor"
91
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../../lib/gergich/capture"
4
-
5
- RSpec.describe "CustomCaptor" do
6
- class CustomCaptor
7
- def run(output)
8
- output.scan(/^(.+?):(\d+): (.*)$/).map do |file, line, error|
9
- { path: file, message: error, position: line.to_i, severity: "error" }
10
- end
11
- end
12
- end
13
-
14
- let(:described_class) { CustomCaptor }
15
- let(:capture_format) { "custom:sqlite3:CustomCaptor" }
16
- let(:output) do
17
- <<~OUTPUT
18
- foo.rb:1: you done screwed up
19
- OUTPUT
20
- end
21
- let(:comments) do
22
- [
23
- {
24
- path: "foo.rb",
25
- position: 1,
26
- message: "you done screwed up",
27
- severity: "error"
28
- }
29
- ]
30
- end
31
-
32
- it "loads" do
33
- captor = Gergich::Capture.load_captor(capture_format)
34
- expect(captor).to eq(described_class)
35
- end
36
-
37
- it "catches errors" do
38
- comments = subject.run(output)
39
- expect(comments).to match_array(comments)
40
- end
41
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::EslintCapture do
6
- let(:output) do
7
- <<~OUTPUT
8
- jsapp/models/user.js
9
- 4:21 error Missing semicolon semi
10
- 5:1 warning Too much cowbell cowbell-overload
11
- OUTPUT
12
- end
13
- let(:comments) do
14
- [
15
- {
16
- path: "jsapp/models/user.js",
17
- position: 4,
18
- message: "[eslint] Missing semicolon",
19
- severity: "error"
20
- },
21
- {
22
- path: "jsapp/models/user.js",
23
- position: 5,
24
- message: "[eslint] Too much cowbell",
25
- severity: "warn"
26
- }
27
- ]
28
- end
29
-
30
- it_behaves_like "a captor"
31
- end
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::Flake8Capture do
6
- let(:output) do
7
- <<~OUTPUT
8
- ./djangogeneric/settings/base.py:73:80: E501 line too long (81 > 79 characters)
9
- OUTPUT
10
- end
11
- let(:comments) do
12
- [
13
- {
14
- path: "./djangogeneric/settings/base.py",
15
- position: 73,
16
- message: "[flake8] E501 line too long (81 > 79 characters)",
17
- severity: "error"
18
- }
19
- ]
20
- end
21
-
22
- it_behaves_like "a captor"
23
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::I18nlinerCapture do
6
- let(:output) do
7
- <<~OUTPUT
8
- 1)
9
- invalid signature on line 4: <unsupported expression>
10
- jsapp/models/user.js
11
- OUTPUT
12
- end
13
- let(:comments) do
14
- [
15
- {
16
- path: "jsapp/models/user.js",
17
- position: 4,
18
- message: "[i18n] invalid signature: <unsupported expression>",
19
- severity: "error"
20
- }
21
- ]
22
- end
23
-
24
- it_behaves_like "a captor"
25
- end
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::RubocopCapture do
6
- let(:output) do
7
- <<~OUTPUT
8
- Offenses:
9
-
10
- bin/gergich:47:8: C: Prefer double-quoted strings
11
- if ENV['DEBUG']
12
- ^^^^^^^
13
- foo/bar/baz.rb:1:2: W: no context for this one :shrug:
14
- lib/gergich.rb:10:9: E: this is a terrible name
15
-
16
- seriously, what were you thinking?
17
- def foo
18
- ^^^
19
- lib/gergich.rb:22:55: W: Line is too long. [55/54]
20
- def initialize(ref = "HEAD", revision_number = nil)
21
- ^^
22
-
23
- 1 file inspected, 35 offenses detected, 27 offenses auto-correctable
24
- OUTPUT
25
- end
26
- let(:comments) do
27
- [
28
- {
29
- path: "bin/gergich",
30
- position: 47,
31
- message: "[rubocop] Prefer double-quoted strings\n\n if ENV['DEBUG']\n ^^^^^^^\n",
32
- severity: "info"
33
- },
34
- {
35
- path: "foo/bar/baz.rb",
36
- position: 1,
37
- message: "[rubocop] no context for this one :shrug:\n",
38
- severity: "warn"
39
- },
40
- {
41
- path: "lib/gergich.rb",
42
- position: 10,
43
- message: <<~OUTPUT,
44
- [rubocop] this is a terrible name
45
-
46
- seriously, what were you thinking?
47
-
48
- def foo
49
- ^^^
50
- OUTPUT
51
- severity: "error"
52
- },
53
- {
54
- path: "lib/gergich.rb",
55
- position: 22,
56
- message: <<~OUTPUT,
57
- [rubocop] Line is too long. [55/54]
58
-
59
- def initialize(ref = "HEAD", revision_number = nil)
60
- ^^
61
- OUTPUT
62
- severity: "warn"
63
- }
64
- ]
65
- end
66
-
67
- it_behaves_like "a captor"
68
-
69
- it "raises an error if it couldn't run" do
70
- expect { subject.run(<<-OUTPUT) }.to raise_error(/RuboCop failed to run properly/)
71
- Could not find i18n-1.8.9 in any of the sources
72
- Run `bundle install` to install missing gems.
73
- OUTPUT
74
- end
75
- end
@@ -1,83 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "../../support/capture_shared_examples"
4
-
5
- RSpec.describe Gergich::Capture::ShellcheckCapture do
6
- let(:output) do
7
- <<~'OUTPUT'
8
- [
9
- {
10
- "file": "bin/sync-translations.sh",
11
- "line": 23,
12
- "endLine": 23,
13
- "column": 21,
14
- "endColumn": 21,
15
- "level": "style",
16
- "code": 2006,
17
- "message": "Use $(..) instead of legacy `..`."
18
- },
19
- {
20
- "file": "bin/sync-translations.sh",
21
- "line": 23,
22
- "endLine": 23,
23
- "column": 43,
24
- "endColumn": 43,
25
- "level": "warning",
26
- "code": 2046,
27
- "message": "Quote this to prevent word splitting."
28
- },
29
- {
30
- "file": "bin/sync-translations.sh",
31
- "line": 32,
32
- "endLine": 32,
33
- "column": 62,
34
- "endColumn": 62,
35
- "level": "info",
36
- "code": 2086,
37
- "message": "Double quote to prevent globbing and word splitting."
38
- },
39
- {
40
- "file": "fail.sh",
41
- "line": 3,
42
- "endLine": 3,
43
- "column": 12,
44
- "endColumn": 12,
45
- "level": "error",
46
- "code": 1101,
47
- "message": "Delete trailing spaces after \\ to break line (or use quotes for literal space)."
48
- }
49
- ]
50
- OUTPUT
51
- end
52
-
53
- let(:comments) do
54
- [
55
- {
56
- path: "bin/sync-translations.sh",
57
- position: 23,
58
- message: "Use $(..) instead of legacy `..`.",
59
- severity: "info"
60
- },
61
- {
62
- path: "bin/sync-translations.sh",
63
- position: 23,
64
- message: "Quote this to prevent word splitting.",
65
- severity: "warn"
66
- },
67
- {
68
- path: "bin/sync-translations.sh",
69
- position: 32,
70
- message: "Double quote to prevent globbing and word splitting.",
71
- severity: "info"
72
- },
73
- {
74
- path: "fail.sh",
75
- position: 3,
76
- message: "Delete trailing spaces after \\ to break line (or use quotes for literal space).",
77
- severity: "error"
78
- }
79
- ]
80
- end
81
-
82
- it_behaves_like "a captor"
83
- end