danger-dangermattic 1.2.3 → 1.3.0
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.
- checksums.yaml +4 -4
- data/.buildkite/pipeline.yml +2 -2
- data/.github/dependabot.yml +23 -0
- data/.github/workflows/README.md +18 -3
- data/.github/workflows/reusable-check-labels-on-issues.yml +100 -27
- data/.github/workflows/reusable-run-danger.yml +1 -1
- data/AGENTS.md +35 -0
- data/CHANGELOG.md +12 -0
- data/CLAUDE.md +1 -0
- data/Gemfile.lock +32 -31
- data/README.md +28 -0
- data/danger-dangermattic.gemspec +1 -1
- data/lib/dangermattic/gem_version.rb +1 -1
- data/lib/dangermattic/plugins/android_strings_checker.rb +76 -0
- data/lib/dangermattic/plugins/manifest_pr_checker.rb +12 -7
- data/lib/dangermattic/plugins/pr_size_checker.rb +10 -12
- data/spec/android_strings_checker_spec.rb +218 -0
- data/spec/pr_size_checker_spec.rb +13 -12
- metadata +13 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 455052da651ef9af5d20ffc6be33a06ab961393e4c0adc8d3d87e27beba62efd
|
|
4
|
+
data.tar.gz: f04b1494ea66bc1fb3677e69658c08fa40b7373f548215f424ffd6f5fd0a4148
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58e0ffcb0c9ee7ce6f6a9080dc95e5aa8d9fe21eac85b576782625c4a528d0ebdc7069498fc6e965345f23d159eba6a186c7a9074f33ba38988e8bd3e24cc70c
|
|
7
|
+
data.tar.gz: 5401b6ce0765675148c5a6951c4f1279be562a3bc55a0e9dc824d6e0fd92a123738cdef5b8258fb734e8427939c206af845dac89d89d3192ee0f32f6ac2c0c3e
|
data/.buildkite/pipeline.yml
CHANGED
|
@@ -41,13 +41,13 @@ steps:
|
|
|
41
41
|
#################
|
|
42
42
|
# Danger Lint
|
|
43
43
|
#################
|
|
44
|
-
- label: "
|
|
44
|
+
- label: ":danger: Lint (Danger)"
|
|
45
45
|
key: dangerlint
|
|
46
46
|
command: |
|
|
47
47
|
echo "--- :rubygems: Setting up Gems"
|
|
48
48
|
bundle install
|
|
49
49
|
|
|
50
|
-
echo "---
|
|
50
|
+
echo "--- :danger: Run Danger Lint"
|
|
51
51
|
bundle exec danger plugins lint
|
|
52
52
|
plugins: *common_plugins
|
|
53
53
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "github-actions"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
day: "monday"
|
|
8
|
+
open-pull-requests-limit: 10
|
|
9
|
+
groups:
|
|
10
|
+
actions-minor-patch:
|
|
11
|
+
patterns:
|
|
12
|
+
- '*'
|
|
13
|
+
update-types:
|
|
14
|
+
- minor
|
|
15
|
+
- patch
|
|
16
|
+
actions-major:
|
|
17
|
+
patterns:
|
|
18
|
+
- '*'
|
|
19
|
+
update-types:
|
|
20
|
+
- major
|
|
21
|
+
cooldown:
|
|
22
|
+
default-days: 7
|
|
23
|
+
semver-major-days: 14
|
data/.github/workflows/README.md
CHANGED
|
@@ -11,19 +11,34 @@ All jobs are run on `ubuntu-latest`.
|
|
|
11
11
|
This workflow is an independent check (not using Danger) to verify if the labels on an issue match specified regex patterns.
|
|
12
12
|
|
|
13
13
|
### Inputs:
|
|
14
|
-
- `label-format-list`: JSON
|
|
14
|
+
- `label-format-list`: JSON array of regex strings expected for the labels (default: `[".*"]`)
|
|
15
15
|
- `label-error-message`: Error message when labels don't match
|
|
16
|
-
- `label-success-message`:
|
|
16
|
+
- `label-success-message`: Deprecated and ignored. Kept only for backward compatibility with existing callers, and scheduled for removal in the next major release.
|
|
17
17
|
- `cancel-running-jobs`: Cancel in-progress jobs when new ones are created (default: `true`)
|
|
18
18
|
|
|
19
|
+
Example:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
with:
|
|
23
|
+
label-format-list: |
|
|
24
|
+
[
|
|
25
|
+
"^\\[.+\\]",
|
|
26
|
+
"^[[:alnum:]]"
|
|
27
|
+
]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Backslashes in regex patterns must be doubled because the workflow parses the input as JSON.
|
|
31
|
+
|
|
19
32
|
### Secrets:
|
|
20
33
|
- `github-token`: Required GitHub token
|
|
34
|
+
- The token must resolve through `gh api user` to the same login that appears on issue comments, because the workflow only manages comments authored by that login.
|
|
21
35
|
|
|
22
36
|
### Job: `check-issue-labels`
|
|
23
37
|
- Permissions: `issues: write`
|
|
24
38
|
- Main step: "🏷️ Check Issue Labels"
|
|
25
39
|
- Checks if issue labels match the specified regex patterns
|
|
26
|
-
-
|
|
40
|
+
- Updates a managed comment authored by the configured token when labels are missing
|
|
41
|
+
- Removes that managed comment when labels become valid
|
|
27
42
|
|
|
28
43
|
## Retry Buildkite Step on Pull Request Events
|
|
29
44
|
|
|
@@ -4,7 +4,7 @@ on:
|
|
|
4
4
|
workflow_call:
|
|
5
5
|
inputs:
|
|
6
6
|
label-format-list:
|
|
7
|
-
description: The
|
|
7
|
+
description: The regex formats expected for the labels; must be a JSON array of strings
|
|
8
8
|
default: |
|
|
9
9
|
[
|
|
10
10
|
".*"
|
|
@@ -17,8 +17,8 @@ on:
|
|
|
17
17
|
type: string
|
|
18
18
|
required: false
|
|
19
19
|
label-success-message:
|
|
20
|
-
description:
|
|
21
|
-
default:
|
|
20
|
+
description: Deprecated and ignored. Kept only for backward compatibility with existing callers, and scheduled for removal in the next major release.
|
|
21
|
+
default: ''
|
|
22
22
|
type: string
|
|
23
23
|
required: false
|
|
24
24
|
cancel-running-jobs:
|
|
@@ -42,22 +42,103 @@ jobs:
|
|
|
42
42
|
steps:
|
|
43
43
|
- name: 🏷️ Check Issue Labels
|
|
44
44
|
env:
|
|
45
|
-
|
|
45
|
+
GH_TOKEN: ${{ secrets.github-token }}
|
|
46
46
|
GH_REPO: ${{ github.repository }}
|
|
47
47
|
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
|
48
48
|
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels.*.name) }}
|
|
49
49
|
LABEL_REGEX_LIST: ${{ inputs.label-format-list }}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<!-- generated_by_dangermattic -->
|
|
53
|
-
ISSUE_ERROR_COMMENT: >
|
|
54
|
-
${{ inputs.label-error-message }}
|
|
55
|
-
<!-- generated_by_dangermattic -->
|
|
50
|
+
DEPRECATED_SUCCESS_MESSAGE: ${{ inputs.label-success-message }}
|
|
51
|
+
ISSUE_ERROR_MESSAGE: ${{ inputs.label-error-message }}
|
|
56
52
|
run: |
|
|
57
|
-
#!/bin/bash
|
|
53
|
+
#!/usr/bin/env bash
|
|
54
|
+
|
|
55
|
+
set -euo pipefail
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
comment_marker='<!-- generated_by_dangermattic -->'
|
|
58
|
+
authenticated_login=''
|
|
59
|
+
managed_comment_id=''
|
|
60
|
+
declare -a managed_comment_ids=()
|
|
61
|
+
|
|
62
|
+
if [ -n "$DEPRECATED_SUCCESS_MESSAGE" ]; then
|
|
63
|
+
echo "⚠️ label-success-message is deprecated and ignored. Success now removes the managed workflow comment."
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
build_comment_body() {
|
|
67
|
+
local message="$1"
|
|
68
|
+
|
|
69
|
+
printf '%s\n%s' "$message" "$comment_marker"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
load_authenticated_login() {
|
|
73
|
+
authenticated_login="$(gh api user --jq '.login')"
|
|
74
|
+
|
|
75
|
+
if [ -z "$authenticated_login" ] || [ "$authenticated_login" = 'null' ]; then
|
|
76
|
+
echo '❌ Unable to determine the authenticated GitHub login for managed comment filtering.'
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
load_managed_comment_ids() {
|
|
82
|
+
readarray -t managed_comment_ids < <(
|
|
83
|
+
gh api "repos/$GH_REPO/issues/$ISSUE_NUMBER/comments" --paginate |
|
|
84
|
+
jq -r --arg author "$authenticated_login" --arg marker "$comment_marker" '.[] | select(.user.login == $author) | select((.body // "") | contains($marker)) | .id'
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if [ "${#managed_comment_ids[@]}" -gt 0 ]; then
|
|
88
|
+
managed_comment_id="${managed_comment_ids[$((${#managed_comment_ids[@]} - 1))]}"
|
|
89
|
+
else
|
|
90
|
+
managed_comment_id=''
|
|
91
|
+
fi
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
delete_extra_managed_comments() {
|
|
95
|
+
local duplicate_count=0
|
|
96
|
+
|
|
97
|
+
duplicate_count=$((${#managed_comment_ids[@]} - 1))
|
|
98
|
+
if [ "$duplicate_count" -le 0 ]; then
|
|
99
|
+
return
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
for comment_id in "${managed_comment_ids[@]:0:$duplicate_count}"; do
|
|
103
|
+
echo "🧹 Deleting duplicate managed comment $comment_id"
|
|
104
|
+
gh api --method DELETE "repos/$GH_REPO/issues/comments/$comment_id" > /dev/null
|
|
105
|
+
done
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
upsert_managed_comment() {
|
|
109
|
+
local comment_body="$1"
|
|
110
|
+
|
|
111
|
+
if [ -n "$managed_comment_id" ]; then
|
|
112
|
+
echo "✍️ Updating managed comment $managed_comment_id on issue $ISSUE_NUMBER"
|
|
113
|
+
gh api --method PATCH "repos/$GH_REPO/issues/comments/$managed_comment_id" --raw-field "body=$comment_body" > /dev/null
|
|
114
|
+
else
|
|
115
|
+
echo "✍️ Creating managed comment on issue $ISSUE_NUMBER"
|
|
116
|
+
gh api --method POST "repos/$GH_REPO/issues/$ISSUE_NUMBER/comments" --raw-field "body=$comment_body" > /dev/null
|
|
117
|
+
fi
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
delete_managed_comments() {
|
|
121
|
+
if [ "${#managed_comment_ids[@]}" -eq 0 ]; then
|
|
122
|
+
echo "🧼 No managed comment found to delete."
|
|
123
|
+
return
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
for comment_id in "${managed_comment_ids[@]}"; do
|
|
127
|
+
echo "🧼 Deleting managed comment $comment_id"
|
|
128
|
+
gh api --method DELETE "repos/$GH_REPO/issues/comments/$comment_id" > /dev/null
|
|
129
|
+
done
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
readarray -t labels < <(printf '%s\n' "$ISSUE_LABELS" | jq -r '.[]')
|
|
133
|
+
|
|
134
|
+
if ! printf '%s\n' "$LABEL_REGEX_LIST" | jq -e 'type == "array" and all(.[]; type == "string")' > /dev/null; then
|
|
135
|
+
echo "❌ LABEL_REGEX_LIST must be a JSON array of strings."
|
|
136
|
+
echo ' Example: ["^\\[.+\\]", "^[[:alnum:]]"]'
|
|
137
|
+
echo ' Regex backslashes must be doubled because the input is parsed as JSON.'
|
|
138
|
+
exit 1
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
readarray -t label_regex_list < <(printf '%s\n' "$LABEL_REGEX_LIST" | jq -r '.[]')
|
|
61
142
|
|
|
62
143
|
all_patterns_matched=true
|
|
63
144
|
|
|
@@ -78,22 +159,14 @@ jobs:
|
|
|
78
159
|
fi
|
|
79
160
|
done
|
|
80
161
|
|
|
81
|
-
|
|
162
|
+
load_authenticated_login
|
|
163
|
+
load_managed_comment_ids
|
|
164
|
+
delete_extra_managed_comments
|
|
165
|
+
|
|
82
166
|
if [ "$all_patterns_matched" = true ]; then
|
|
83
167
|
echo "✅ All regex patterns have at least one match."
|
|
84
|
-
|
|
168
|
+
delete_managed_comments
|
|
85
169
|
else
|
|
86
170
|
echo "❌ Not all regex patterns have at least one match."
|
|
87
|
-
|
|
88
|
-
fi
|
|
89
|
-
|
|
90
|
-
set +e
|
|
91
|
-
echo "✍️ Attempting to edit existing comment on issue $ISSUE_NUMBER, if it exists:"
|
|
92
|
-
gh issue comment $ISSUE_NUMBER --body "$ISSUE_COMMENT" --edit-last
|
|
93
|
-
comment_update_status=$?
|
|
94
|
-
set -e
|
|
95
|
-
|
|
96
|
-
if [ $comment_update_status -ne 0 ]; then
|
|
97
|
-
echo "✍️ Adding new comment on issue $ISSUE_NUMBER:"
|
|
98
|
-
gh issue comment $ISSUE_NUMBER --body "$ISSUE_COMMENT"
|
|
171
|
+
upsert_managed_comment "$(build_comment_body "$ISSUE_ERROR_MESSAGE")"
|
|
99
172
|
fi
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## Overview
|
|
2
|
+
|
|
3
|
+
Dangermattic is a shared collection of Danger plugins used across Automattic's mobile repositories.
|
|
4
|
+
It provides reusable Danger rules for PR checks, code review automation, and CI enforcement.
|
|
5
|
+
|
|
6
|
+
## Bootstrap
|
|
7
|
+
|
|
8
|
+
Requires Ruby at the version specified in `.ruby-version`.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bundle install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
- `bundle exec rake` — run all checks (specs + RuboCop + Danger lint)
|
|
17
|
+
- `bundle exec rspec` — run tests only
|
|
18
|
+
- `bundle exec rubocop` — run linter only
|
|
19
|
+
|
|
20
|
+
## Project Structure
|
|
21
|
+
|
|
22
|
+
- `lib/dangermattic/plugins/` — Danger plugin implementations
|
|
23
|
+
- `lib/dangermattic/plugins/common/` — shared helpers used across plugins
|
|
24
|
+
- `spec/` — RSpec tests; each plugin has a matching `*_spec.rb`
|
|
25
|
+
- `spec/fixtures/` — test fixtures
|
|
26
|
+
|
|
27
|
+
## Conventions
|
|
28
|
+
|
|
29
|
+
- Tests written with RSpec
|
|
30
|
+
- CI via Buildkite, see `.buildkite`
|
|
31
|
+
- Gem releases are triggered by Git tags pushed to the remote and run in CI.
|
|
32
|
+
|
|
33
|
+
## Pitfalls
|
|
34
|
+
|
|
35
|
+
- Together with unit tests, `bundle exec danger plugins lint` must also pass — it validates plugin metadata and code correctness.
|
data/CHANGELOG.md
CHANGED
|
@@ -20,6 +20,18 @@ _None_
|
|
|
20
20
|
|
|
21
21
|
_None_
|
|
22
22
|
|
|
23
|
+
## 1.3.0
|
|
24
|
+
|
|
25
|
+
### New Features
|
|
26
|
+
|
|
27
|
+
- Added `android_strings_checker.check_existing_strings_not_modified`, which fails when the value of an existing translatable `<string>` is changed in place (rather than added under a new key). This enforces string-key immutability, which keeps in-progress translations valid in a continuous-localization setup. [#126]
|
|
28
|
+
|
|
29
|
+
## 1.2.4
|
|
30
|
+
|
|
31
|
+
### Internal Changes
|
|
32
|
+
|
|
33
|
+
- `pr_size_checker` and `manifest_pr_checker`: optimize performance for large PRs [#103]
|
|
34
|
+
|
|
23
35
|
## 1.2.3
|
|
24
36
|
|
|
25
37
|
### Bug Fixes
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
danger-dangermattic (1.
|
|
5
|
-
danger (~> 9.
|
|
4
|
+
danger-dangermattic (1.3.0)
|
|
5
|
+
danger (~> 9.5, >= 9.5.3)
|
|
6
6
|
danger-plugin-api (~> 1.0)
|
|
7
7
|
danger-rubocop (~> 0.13)
|
|
8
8
|
rubocop (~> 1.63)
|
|
@@ -10,25 +10,24 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
activesupport (8.
|
|
13
|
+
activesupport (8.1.3)
|
|
14
14
|
base64
|
|
15
|
-
benchmark (>= 0.3)
|
|
16
15
|
bigdecimal
|
|
17
16
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
18
17
|
connection_pool (>= 2.2.5)
|
|
19
18
|
drb
|
|
20
19
|
i18n (>= 1.6, < 2)
|
|
20
|
+
json
|
|
21
21
|
logger (>= 1.4.2)
|
|
22
22
|
minitest (>= 5.1)
|
|
23
23
|
securerandom (>= 0.3)
|
|
24
24
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
25
25
|
uri (>= 0.13.1)
|
|
26
|
-
addressable (2.
|
|
27
|
-
public_suffix (>= 2.0.2, <
|
|
26
|
+
addressable (2.9.0)
|
|
27
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
28
28
|
ast (2.4.3)
|
|
29
29
|
base64 (0.3.0)
|
|
30
|
-
|
|
31
|
-
bigdecimal (3.2.2)
|
|
30
|
+
bigdecimal (4.0.1)
|
|
32
31
|
claide (1.1.0)
|
|
33
32
|
claide-plugins (0.9.2)
|
|
34
33
|
cork
|
|
@@ -36,8 +35,8 @@ GEM
|
|
|
36
35
|
open4 (~> 1.3)
|
|
37
36
|
coderay (1.1.3)
|
|
38
37
|
colored2 (3.1.2)
|
|
39
|
-
concurrent-ruby (1.3.
|
|
40
|
-
connection_pool (
|
|
38
|
+
concurrent-ruby (1.3.6)
|
|
39
|
+
connection_pool (3.0.2)
|
|
41
40
|
cork (0.3.0)
|
|
42
41
|
colored2 (~> 3.1)
|
|
43
42
|
danger (9.5.3)
|
|
@@ -61,14 +60,14 @@ GEM
|
|
|
61
60
|
rubocop (~> 1.0)
|
|
62
61
|
diff-lcs (1.6.2)
|
|
63
62
|
drb (2.2.3)
|
|
64
|
-
faraday (2.
|
|
63
|
+
faraday (2.14.1)
|
|
65
64
|
faraday-net_http (>= 2.0, < 3.5)
|
|
66
65
|
json
|
|
67
66
|
logger
|
|
68
|
-
faraday-http-cache (2.
|
|
67
|
+
faraday-http-cache (2.6.1)
|
|
69
68
|
faraday (>= 0.8)
|
|
70
|
-
faraday-net_http (3.4.
|
|
71
|
-
net-http (
|
|
69
|
+
faraday-net_http (3.4.2)
|
|
70
|
+
net-http (~> 0.5)
|
|
72
71
|
ffi (1.17.2)
|
|
73
72
|
ffi (1.17.2-arm64-darwin)
|
|
74
73
|
formatador (1.1.1)
|
|
@@ -93,11 +92,11 @@ GEM
|
|
|
93
92
|
guard (~> 2.1)
|
|
94
93
|
guard-compat (~> 1.1)
|
|
95
94
|
rspec (>= 2.99.0, < 4.0)
|
|
96
|
-
i18n (1.14.
|
|
95
|
+
i18n (1.14.8)
|
|
97
96
|
concurrent-ruby (~> 1.0)
|
|
98
|
-
json (2.
|
|
99
|
-
kramdown (2.5.
|
|
100
|
-
rexml (>= 3.
|
|
97
|
+
json (2.19.3)
|
|
98
|
+
kramdown (2.5.2)
|
|
99
|
+
rexml (>= 3.4.4)
|
|
101
100
|
kramdown-parser-gfm (1.1.0)
|
|
102
101
|
kramdown (~> 2.0)
|
|
103
102
|
language_server-protocol (3.17.0.5)
|
|
@@ -108,11 +107,13 @@ GEM
|
|
|
108
107
|
logger (1.7.0)
|
|
109
108
|
lumberjack (1.4.0)
|
|
110
109
|
method_source (1.1.0)
|
|
111
|
-
minitest (
|
|
110
|
+
minitest (6.0.2)
|
|
111
|
+
drb (~> 2.0)
|
|
112
|
+
prism (~> 1.5)
|
|
112
113
|
nap (1.1.0)
|
|
113
114
|
nenv (0.3.0)
|
|
114
|
-
net-http (0.
|
|
115
|
-
uri
|
|
115
|
+
net-http (0.9.1)
|
|
116
|
+
uri (>= 0.11.1)
|
|
116
117
|
notiffany (0.1.3)
|
|
117
118
|
nenv (~> 0.1)
|
|
118
119
|
shellany (~> 0.0)
|
|
@@ -125,22 +126,22 @@ GEM
|
|
|
125
126
|
parser (3.3.9.0)
|
|
126
127
|
ast (~> 2.4.1)
|
|
127
128
|
racc
|
|
128
|
-
prism (1.
|
|
129
|
+
prism (1.9.0)
|
|
129
130
|
process_executer (1.3.0)
|
|
130
131
|
pry (0.15.2)
|
|
131
132
|
coderay (~> 1.1)
|
|
132
133
|
method_source (~> 1.0)
|
|
133
134
|
pstore (0.2.0)
|
|
134
|
-
public_suffix (
|
|
135
|
+
public_suffix (7.0.5)
|
|
135
136
|
racc (1.8.1)
|
|
136
137
|
rainbow (3.1.1)
|
|
137
138
|
rake (13.3.0)
|
|
138
139
|
rb-fsevent (0.11.2)
|
|
139
140
|
rb-inotify (0.11.1)
|
|
140
141
|
ffi (~> 1.0)
|
|
141
|
-
rchardet (1.
|
|
142
|
+
rchardet (1.10.0)
|
|
142
143
|
regexp_parser (2.11.0)
|
|
143
|
-
rexml (3.4.
|
|
144
|
+
rexml (3.4.4)
|
|
144
145
|
rspec (3.13.1)
|
|
145
146
|
rspec-core (~> 3.13.0)
|
|
146
147
|
rspec-expectations (~> 3.13.0)
|
|
@@ -175,7 +176,7 @@ GEM
|
|
|
175
176
|
lint_roller (~> 1.1)
|
|
176
177
|
rubocop (~> 1.72, >= 1.72.1)
|
|
177
178
|
ruby-progressbar (1.13.0)
|
|
178
|
-
sawyer (0.9.
|
|
179
|
+
sawyer (0.9.3)
|
|
179
180
|
addressable (>= 2.3.5)
|
|
180
181
|
faraday (>= 0.17.3, < 3)
|
|
181
182
|
securerandom (0.4.1)
|
|
@@ -185,11 +186,11 @@ GEM
|
|
|
185
186
|
thor (1.4.0)
|
|
186
187
|
tzinfo (2.0.6)
|
|
187
188
|
concurrent-ruby (~> 1.0)
|
|
188
|
-
unicode-display_width (3.
|
|
189
|
-
unicode-emoji (~> 4.
|
|
190
|
-
unicode-emoji (4.0
|
|
191
|
-
uri (1.
|
|
192
|
-
yard (0.9.
|
|
189
|
+
unicode-display_width (3.2.0)
|
|
190
|
+
unicode-emoji (~> 4.1)
|
|
191
|
+
unicode-emoji (4.2.0)
|
|
192
|
+
uri (1.1.1)
|
|
193
|
+
yard (0.9.43)
|
|
193
194
|
|
|
194
195
|
PLATFORMS
|
|
195
196
|
arm64-darwin-22
|
data/README.md
CHANGED
|
@@ -71,6 +71,34 @@ my_new_plugin_checker.check_method(param: my_param_value)
|
|
|
71
71
|
|
|
72
72
|
Please follow the existing naming convention for validation and check plugins: classes end with a `*Checker` suffix and the main validation methods are named with a `check_*` prefix.
|
|
73
73
|
|
|
74
|
+
### How to verify a change against a real pull request
|
|
75
|
+
|
|
76
|
+
Unit tests are the main development loop, but before releasing a new or changed check it's often useful to see it run end-to-end against a real pull request.
|
|
77
|
+
You can do this locally with `danger pr`, which evaluates a `Dangerfile` against an existing PR's diff and prints the result to your terminal only, without posting upstream.
|
|
78
|
+
|
|
79
|
+
Point a `Gemfile` at your branch and add the check you want to exercise to a `Dangerfile`:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
# Gemfile
|
|
83
|
+
source 'https://rubygems.org'
|
|
84
|
+
gem 'danger-dangermattic', git: 'https://github.com/Automattic/dangermattic', branch: 'my-branch'
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# Dangerfile
|
|
89
|
+
my_new_plugin_checker.check_method
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
After a `bundle install`, run it against a PR from inside a checkout of that PR's repository:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
DANGER_GITHUB_API_TOKEN="$(gh auth token)" \
|
|
96
|
+
BUNDLE_GEMFILE=/path/to/Gemfile \
|
|
97
|
+
bundle exec danger pr https://github.com/<org>/<repo>/pull/<number> \
|
|
98
|
+
--dangerfile=/path/to/Dangerfile
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A failure prints as an `Errors:` block. `danger pr` only fetches and creates temporary refs that it cleans up afterwards, so it won't change your current branch or working tree.
|
|
74
102
|
## Releasing a new version
|
|
75
103
|
|
|
76
104
|
To create a new release of the Dangermattic gem, use the `new_release` Rake task:
|
data/danger-dangermattic.gemspec
CHANGED
|
@@ -7,6 +7,10 @@ module Danger
|
|
|
7
7
|
#
|
|
8
8
|
# android_strings_checker.check_strings_do_not_refer_resource
|
|
9
9
|
#
|
|
10
|
+
# @example Check that existing translatable strings are not modified in place
|
|
11
|
+
#
|
|
12
|
+
# android_strings_checker.check_existing_strings_not_modified
|
|
13
|
+
#
|
|
10
14
|
# @see Automattic/dangermattic
|
|
11
15
|
# @tags android, localization
|
|
12
16
|
#
|
|
@@ -14,6 +18,18 @@ module Danger
|
|
|
14
18
|
MESSAGE = "This PR adds a translatable entry which references another string resource; this usually causes issues with translations.\n" \
|
|
15
19
|
'Please make sure to set the `translatable="false"` attribute.'
|
|
16
20
|
|
|
21
|
+
STRING_MODIFIED_MESSAGE = 'This PR changes the value of an existing translatable string. Existing string keys must stay immutable so that ' \
|
|
22
|
+
'in-progress and existing translations remain valid: please add a **new** string key for the new copy instead of ' \
|
|
23
|
+
'editing the existing one (old translations stay attached to the old key).'
|
|
24
|
+
|
|
25
|
+
# Default file selector: only the app's source (English) strings, living at `…/res/values/strings.xml`.
|
|
26
|
+
# This deliberately excludes localized `values-<locale>/strings.xml` files (whose values legitimately change
|
|
27
|
+
# when translations are pulled in) and any generated/frozen copies that live outside a `res` directory.
|
|
28
|
+
DEFAULT_SOURCE_STRINGS_SELECTOR = ->(path) { path.match?(%r{(^|/)res/values/strings\.xml$}) }
|
|
29
|
+
|
|
30
|
+
# Matches a single-line `<string name="…">value</string>` entry, capturing its name and value.
|
|
31
|
+
STRING_LINE_REGEX = %r{<string\b[^>]*\sname="(?<name>[^"]+)"[^>]*>(?<value>.*)</string>}
|
|
32
|
+
|
|
17
33
|
# Check if translatable strings reference another string resource in 'strings.xml' files in a pull request.
|
|
18
34
|
#
|
|
19
35
|
# @param report_type [Boolean] (optional) Type of report (:error, :warning, :message) whenever a line matches the criteria. Default is :warning.
|
|
@@ -27,5 +43,65 @@ module Danger
|
|
|
27
43
|
report_type: report_type
|
|
28
44
|
)
|
|
29
45
|
end
|
|
46
|
+
|
|
47
|
+
# Check that the value of an existing translatable string is not modified in place.
|
|
48
|
+
#
|
|
49
|
+
# In a continuous-localization setup, source strings flow to the translation system constantly, so changing
|
|
50
|
+
# the English value of an existing key silently invalidates the translations already done for that key. The
|
|
51
|
+
# safe convention is to leave existing keys untouched and introduce a new key for any new copy. This check
|
|
52
|
+
# enforces that convention by failing when a `<string>` entry present in the base is modified (rather than
|
|
53
|
+
# added under a new key). Adding new keys, removing keys, and renaming keys are all allowed.
|
|
54
|
+
#
|
|
55
|
+
# @param report_type [Symbol] (optional) Type of report (:error, :warning, :message). Default is :error.
|
|
56
|
+
# @param file_selector [Proc] (optional) Selects which files to check. Defaults to the app's source
|
|
57
|
+
# `…/res/values/strings.xml` files, excluding localized and generated copies.
|
|
58
|
+
#
|
|
59
|
+
# @return [void]
|
|
60
|
+
def check_existing_strings_not_modified(report_type: :error, file_selector: DEFAULT_SOURCE_STRINGS_SELECTOR)
|
|
61
|
+
files = git_utils.added_and_modified_files.select(&file_selector)
|
|
62
|
+
|
|
63
|
+
files.each do |file|
|
|
64
|
+
diff = danger.git.diff_for_file(file)
|
|
65
|
+
next if diff.nil?
|
|
66
|
+
|
|
67
|
+
removed = translatable_strings_by_name(git_utils.removed_lines(diff_patch: diff.patch))
|
|
68
|
+
added = translatable_strings_by_name(git_utils.added_lines(diff_patch: diff.patch))
|
|
69
|
+
|
|
70
|
+
modified_keys = removed.keys & added.keys
|
|
71
|
+
modified_keys.each do |name|
|
|
72
|
+
next if removed[name].value == added[name].value
|
|
73
|
+
|
|
74
|
+
final_message = <<~MESSAGE
|
|
75
|
+
#{STRING_MODIFIED_MESSAGE}
|
|
76
|
+
File `#{file}`, string `#{name}`:
|
|
77
|
+
```diff
|
|
78
|
+
-#{removed[name].line.chomp}
|
|
79
|
+
+#{added[name].line.chomp}
|
|
80
|
+
```
|
|
81
|
+
MESSAGE
|
|
82
|
+
|
|
83
|
+
reporter.report(message: final_message, type: report_type)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
ParsedString = Struct.new(:value, :line)
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# Parses `<string>` entries out of a set of diff content lines, keyed by their `name` attribute.
|
|
93
|
+
# `translatable="false"` entries are ignored, as they are never sent for translation.
|
|
94
|
+
#
|
|
95
|
+
# @param lines [String] Newline-separated content lines (with the diff `+`/`-` markers already stripped).
|
|
96
|
+
#
|
|
97
|
+
# @return [Hash{String => ParsedString}] A mapping of string name to its parsed value and originating line.
|
|
98
|
+
def translatable_strings_by_name(lines)
|
|
99
|
+
lines.each_line.with_object({}) do |line, result|
|
|
100
|
+
next if line.include?('translatable="false"')
|
|
101
|
+
|
|
102
|
+
match = STRING_LINE_REGEX.match(line)
|
|
103
|
+
result[match[:name]] = ParsedString.new(match[:value], line) unless match.nil?
|
|
104
|
+
end
|
|
105
|
+
end
|
|
30
106
|
end
|
|
31
107
|
end
|
|
@@ -115,13 +115,19 @@ module Danger
|
|
|
115
115
|
private
|
|
116
116
|
|
|
117
117
|
def check_manifest_lock_updated(file_name:, lock_file_name:, instruction:, report_type: :warning)
|
|
118
|
+
all_files = git_utils.all_changed_files
|
|
119
|
+
|
|
118
120
|
# Find all the modified manifest files
|
|
119
|
-
manifest_modified_files =
|
|
121
|
+
manifest_modified_files = all_files.select { |f| File.basename(f) == file_name }
|
|
122
|
+
|
|
123
|
+
# Build a hash mapping directory -> set of basenames for O(1) lookup
|
|
124
|
+
files_by_dir = all_files.group_by { |f| File.dirname(f) }
|
|
125
|
+
.transform_values { |files| files.to_set { |f| File.basename(f) } }
|
|
120
126
|
|
|
121
127
|
# For each manifest file, check if the corresponding lockfile (in the same dir) was also modified
|
|
122
128
|
manifest_modified_files.each do |manifest_file|
|
|
123
|
-
|
|
124
|
-
next if
|
|
129
|
+
manifest_dir = File.dirname(manifest_file)
|
|
130
|
+
next if files_by_dir[manifest_dir]&.include?(lock_file_name)
|
|
125
131
|
|
|
126
132
|
message = format(MESSAGE, manifest_file, lock_file_name, instruction)
|
|
127
133
|
reporter.report(message: message, type: report_type)
|
|
@@ -129,11 +135,10 @@ module Danger
|
|
|
129
135
|
end
|
|
130
136
|
|
|
131
137
|
def check_manifest_lock_updated_strict(manifest_path:, manifest_lock_path:, instruction:, report_type: :warning)
|
|
132
|
-
|
|
133
|
-
return unless manifest_modified
|
|
138
|
+
all_files_set = git_utils.all_changed_files.to_set
|
|
134
139
|
|
|
135
|
-
|
|
136
|
-
return if
|
|
140
|
+
return unless all_files_set.include?(manifest_path)
|
|
141
|
+
return if all_files_set.include?(manifest_lock_path)
|
|
137
142
|
|
|
138
143
|
message = format(MESSAGE, manifest_path, File.basename(manifest_lock_path), instruction)
|
|
139
144
|
reporter.report(message: message, type: report_type)
|
|
@@ -76,13 +76,12 @@ module Danger
|
|
|
76
76
|
def insertions_size(file_selector: nil)
|
|
77
77
|
return danger.git.insertions unless file_selector
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
# Only check added and modified files - deleted files have 0 insertions
|
|
80
|
+
filtered_files = git_utils.added_and_modified_files.select(&file_selector)
|
|
80
81
|
|
|
81
82
|
filtered_files.sum do |file|
|
|
82
|
-
#
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
danger.git.info_for_file(file)&.[](:insertions).to_i
|
|
83
|
+
# Use cached stats directly instead of calling info_for_file for each file
|
|
84
|
+
danger.git.diff.stats[:files][file]&.[](:insertions).to_i
|
|
86
85
|
end
|
|
87
86
|
end
|
|
88
87
|
|
|
@@ -97,10 +96,8 @@ module Danger
|
|
|
97
96
|
filtered_files = git_utils.all_changed_files.select(&file_selector)
|
|
98
97
|
|
|
99
98
|
filtered_files.sum do |file|
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
danger.git.info_for_file(file)&.[](:deletions).to_i
|
|
99
|
+
# Use cached stats directly instead of calling info_for_file for each file
|
|
100
|
+
danger.git.diff.stats[:files][file]&.[](:deletions).to_i
|
|
104
101
|
end
|
|
105
102
|
end
|
|
106
103
|
|
|
@@ -115,10 +112,11 @@ module Danger
|
|
|
115
112
|
filtered_files = git_utils.all_changed_files.select(&file_selector)
|
|
116
113
|
|
|
117
114
|
filtered_files.sum do |file|
|
|
118
|
-
#
|
|
119
|
-
|
|
115
|
+
# Use cached stats directly instead of calling info_for_file for each file
|
|
116
|
+
stats = danger.git.diff.stats[:files][file]
|
|
117
|
+
next 0 unless stats
|
|
120
118
|
|
|
121
|
-
|
|
119
|
+
stats[:deletions].to_i + stats[:insertions].to_i
|
|
122
120
|
end
|
|
123
121
|
end
|
|
124
122
|
end
|
|
@@ -180,6 +180,224 @@ module Danger
|
|
|
180
180
|
expect(@dangerfile).to not_report
|
|
181
181
|
end
|
|
182
182
|
end
|
|
183
|
+
|
|
184
|
+
context 'when checking that existing strings are not modified' do
|
|
185
|
+
let(:source_strings_xml) { 'WordPress/src/main/res/values/strings.xml' }
|
|
186
|
+
|
|
187
|
+
def stub_diff(path, patch)
|
|
188
|
+
allow(@plugin.git).to receive(:modified_files).and_return([path])
|
|
189
|
+
diff = GitDiffStruct.new('modified', path, patch)
|
|
190
|
+
allow(@plugin.git).to receive(:diff_for_file).with(path).and_return(diff)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'reports an error when the value of an existing string is modified in place' do
|
|
194
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
195
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
196
|
+
index 5794d472..772e2b99 100644
|
|
197
|
+
--- a/#{source_strings_xml}
|
|
198
|
+
+++ b/#{source_strings_xml}
|
|
199
|
+
@@ -1,3 +1,3 @@
|
|
200
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
201
|
+
- <string name="greeting">Hello</string>
|
|
202
|
+
+ <string name="greeting">Hi there</string>
|
|
203
|
+
</resources>
|
|
204
|
+
STRINGS
|
|
205
|
+
|
|
206
|
+
@plugin.check_existing_strings_not_modified
|
|
207
|
+
|
|
208
|
+
expected_error = <<~ERROR
|
|
209
|
+
#{AndroidStringsChecker::STRING_MODIFIED_MESSAGE}
|
|
210
|
+
File `#{source_strings_xml}`, string `greeting`:
|
|
211
|
+
```diff
|
|
212
|
+
- <string name="greeting">Hello</string>
|
|
213
|
+
+ <string name="greeting">Hi there</string>
|
|
214
|
+
```
|
|
215
|
+
ERROR
|
|
216
|
+
|
|
217
|
+
expect(@dangerfile).to report_errors([expected_error])
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
it 'does nothing when a brand new string key is added' do
|
|
221
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
222
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
223
|
+
index 5794d472..772e2b99 100644
|
|
224
|
+
--- a/#{source_strings_xml}
|
|
225
|
+
+++ b/#{source_strings_xml}
|
|
226
|
+
@@ -1,2 +1,3 @@
|
|
227
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
228
|
+
+ <string name="greeting">Hello</string>
|
|
229
|
+
</resources>
|
|
230
|
+
STRINGS
|
|
231
|
+
|
|
232
|
+
@plugin.check_existing_strings_not_modified
|
|
233
|
+
|
|
234
|
+
expect(@dangerfile).to not_report
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'does nothing when an existing string key is removed' do
|
|
238
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
239
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
240
|
+
index 5794d472..772e2b99 100644
|
|
241
|
+
--- a/#{source_strings_xml}
|
|
242
|
+
+++ b/#{source_strings_xml}
|
|
243
|
+
@@ -1,3 +1,2 @@
|
|
244
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
245
|
+
- <string name="greeting">Hello</string>
|
|
246
|
+
</resources>
|
|
247
|
+
STRINGS
|
|
248
|
+
|
|
249
|
+
@plugin.check_existing_strings_not_modified
|
|
250
|
+
|
|
251
|
+
expect(@dangerfile).to not_report
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
it 'does nothing when a string is only reordered (value unchanged)' do
|
|
255
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
256
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
257
|
+
index 5794d472..772e2b99 100644
|
|
258
|
+
--- a/#{source_strings_xml}
|
|
259
|
+
+++ b/#{source_strings_xml}
|
|
260
|
+
@@ -1,4 +1,4 @@
|
|
261
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
262
|
+
- <string name="greeting">Hello</string>
|
|
263
|
+
<string name="other">Other</string>
|
|
264
|
+
+ <string name="greeting">Hello</string>
|
|
265
|
+
</resources>
|
|
266
|
+
STRINGS
|
|
267
|
+
|
|
268
|
+
@plugin.check_existing_strings_not_modified
|
|
269
|
+
|
|
270
|
+
expect(@dangerfile).to not_report
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it 'does nothing when a key is renamed (old key removed, new key added)' do
|
|
274
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
275
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
276
|
+
index 5794d472..772e2b99 100644
|
|
277
|
+
--- a/#{source_strings_xml}
|
|
278
|
+
+++ b/#{source_strings_xml}
|
|
279
|
+
@@ -1,3 +1,3 @@
|
|
280
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
281
|
+
- <string name="greeting">Hello</string>
|
|
282
|
+
+ <string name="greeting_v2">Hello</string>
|
|
283
|
+
</resources>
|
|
284
|
+
STRINGS
|
|
285
|
+
|
|
286
|
+
@plugin.check_existing_strings_not_modified
|
|
287
|
+
|
|
288
|
+
expect(@dangerfile).to not_report
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it 'ignores modifications to non-translatable strings' do
|
|
292
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
293
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
294
|
+
index 5794d472..772e2b99 100644
|
|
295
|
+
--- a/#{source_strings_xml}
|
|
296
|
+
+++ b/#{source_strings_xml}
|
|
297
|
+
@@ -1,3 +1,3 @@
|
|
298
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
299
|
+
- <string name="app_name" translatable="false">WordPress</string>
|
|
300
|
+
+ <string name="app_name" translatable="false">WordPress.com</string>
|
|
301
|
+
</resources>
|
|
302
|
+
STRINGS
|
|
303
|
+
|
|
304
|
+
@plugin.check_existing_strings_not_modified
|
|
305
|
+
|
|
306
|
+
expect(@dangerfile).to not_report
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
it 'ignores localized strings.xml files by default' do
|
|
310
|
+
localized_path = 'WordPress/src/main/res/values-fr/strings.xml'
|
|
311
|
+
stub_diff(localized_path, <<~STRINGS)
|
|
312
|
+
diff --git a/#{localized_path} b/#{localized_path}
|
|
313
|
+
index 5794d472..772e2b99 100644
|
|
314
|
+
--- a/#{localized_path}
|
|
315
|
+
+++ b/#{localized_path}
|
|
316
|
+
@@ -1,3 +1,3 @@
|
|
317
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
318
|
+
- <string name="greeting">Bonjour</string>
|
|
319
|
+
+ <string name="greeting">Salut</string>
|
|
320
|
+
</resources>
|
|
321
|
+
STRINGS
|
|
322
|
+
|
|
323
|
+
@plugin.check_existing_strings_not_modified
|
|
324
|
+
|
|
325
|
+
expect(@dangerfile).to not_report
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it 'reports an error for each modified string across multiple source files' do
|
|
329
|
+
wp_path = 'WordPress/src/main/res/values/strings.xml'
|
|
330
|
+
jp_path = 'WordPress/src/jetpack/res/values/strings.xml'
|
|
331
|
+
allow(@plugin.git).to receive(:modified_files).and_return([wp_path, jp_path])
|
|
332
|
+
|
|
333
|
+
wp_diff = GitDiffStruct.new('modified', wp_path, <<~STRINGS)
|
|
334
|
+
diff --git a/#{wp_path} b/#{wp_path}
|
|
335
|
+
index 5794d472..772e2b99 100644
|
|
336
|
+
--- a/#{wp_path}
|
|
337
|
+
+++ b/#{wp_path}
|
|
338
|
+
@@ -1,3 +1,3 @@
|
|
339
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
340
|
+
- <string name="greeting">Hello</string>
|
|
341
|
+
+ <string name="greeting">Hi</string>
|
|
342
|
+
</resources>
|
|
343
|
+
STRINGS
|
|
344
|
+
allow(@plugin.git).to receive(:diff_for_file).with(wp_path).and_return(wp_diff)
|
|
345
|
+
|
|
346
|
+
jp_diff = GitDiffStruct.new('modified', jp_path, <<~STRINGS)
|
|
347
|
+
diff --git a/#{jp_path} b/#{jp_path}
|
|
348
|
+
index 5794d472..772e2b99 100644
|
|
349
|
+
--- a/#{jp_path}
|
|
350
|
+
+++ b/#{jp_path}
|
|
351
|
+
@@ -1,3 +1,3 @@
|
|
352
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
353
|
+
- <string name="farewell">Bye</string>
|
|
354
|
+
+ <string name="farewell">Goodbye</string>
|
|
355
|
+
</resources>
|
|
356
|
+
STRINGS
|
|
357
|
+
allow(@plugin.git).to receive(:diff_for_file).with(jp_path).and_return(jp_diff)
|
|
358
|
+
|
|
359
|
+
@plugin.check_existing_strings_not_modified
|
|
360
|
+
|
|
361
|
+
wp_error = <<~ERROR
|
|
362
|
+
#{AndroidStringsChecker::STRING_MODIFIED_MESSAGE}
|
|
363
|
+
File `#{wp_path}`, string `greeting`:
|
|
364
|
+
```diff
|
|
365
|
+
- <string name="greeting">Hello</string>
|
|
366
|
+
+ <string name="greeting">Hi</string>
|
|
367
|
+
```
|
|
368
|
+
ERROR
|
|
369
|
+
|
|
370
|
+
jp_error = <<~ERROR
|
|
371
|
+
#{AndroidStringsChecker::STRING_MODIFIED_MESSAGE}
|
|
372
|
+
File `#{jp_path}`, string `farewell`:
|
|
373
|
+
```diff
|
|
374
|
+
- <string name="farewell">Bye</string>
|
|
375
|
+
+ <string name="farewell">Goodbye</string>
|
|
376
|
+
```
|
|
377
|
+
ERROR
|
|
378
|
+
|
|
379
|
+
expect(@dangerfile.status_report[:errors]).to contain_exactly(wp_error, jp_error)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it 'can be configured to report a warning instead of an error' do
|
|
383
|
+
stub_diff(source_strings_xml, <<~STRINGS)
|
|
384
|
+
diff --git a/#{source_strings_xml} b/#{source_strings_xml}
|
|
385
|
+
index 5794d472..772e2b99 100644
|
|
386
|
+
--- a/#{source_strings_xml}
|
|
387
|
+
+++ b/#{source_strings_xml}
|
|
388
|
+
@@ -1,3 +1,3 @@
|
|
389
|
+
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
390
|
+
- <string name="greeting">Hello</string>
|
|
391
|
+
+ <string name="greeting">Hi there</string>
|
|
392
|
+
</resources>
|
|
393
|
+
STRINGS
|
|
394
|
+
|
|
395
|
+
@plugin.check_existing_strings_not_modified(report_type: :warning)
|
|
396
|
+
|
|
397
|
+
expect(@dangerfile.status_report[:warnings].count).to eq(1)
|
|
398
|
+
expect(@dangerfile.status_report[:errors]).to be_empty
|
|
399
|
+
end
|
|
400
|
+
end
|
|
183
401
|
end
|
|
184
402
|
end
|
|
185
403
|
end
|
|
@@ -146,19 +146,20 @@ module Danger
|
|
|
146
146
|
allow(@plugin.git).to receive_messages(added_files: [added_config, added_file], modified_files: [modified_file1, modified_file2, added_test_file, modified_strings], deleted_files: [deleted_file1, deleted_test_file, deleted_strings, deleted_file2])
|
|
147
147
|
|
|
148
148
|
allow(@plugin.git).to receive(:diff).and_return(instance_double(Git::Diff))
|
|
149
|
-
|
|
149
|
+
# Populate stats hash directly with insertions/deletions data for the optimized code path
|
|
150
|
+
expected_files = {
|
|
151
|
+
added_test_file => { insertions: 201 },
|
|
152
|
+
added_config => { insertions: 311 },
|
|
153
|
+
added_file => { insertions: 13 },
|
|
154
|
+
modified_file1 => { insertions: 127, deletions: 159 },
|
|
155
|
+
modified_file2 => { insertions: 43, deletions: 37 },
|
|
156
|
+
modified_strings => { insertions: 432, deletions: 297 },
|
|
157
|
+
deleted_file1 => { deletions: 246 },
|
|
158
|
+
deleted_file2 => { deletions: 493 },
|
|
159
|
+
deleted_test_file => { deletions: 222 },
|
|
160
|
+
deleted_strings => { deletions: 593 }
|
|
161
|
+
}
|
|
150
162
|
allow(@plugin.git.diff).to receive(:stats).and_return({ files: expected_files })
|
|
151
|
-
|
|
152
|
-
allow(@plugin.git).to receive(:info_for_file).with(added_test_file).and_return({ insertions: 201 })
|
|
153
|
-
allow(@plugin.git).to receive(:info_for_file).with(added_config).and_return({ insertions: 311 })
|
|
154
|
-
allow(@plugin.git).to receive(:info_for_file).with(added_file).and_return({ insertions: 13 })
|
|
155
|
-
allow(@plugin.git).to receive(:info_for_file).with(modified_file1).and_return({ insertions: 127, deletions: 159 })
|
|
156
|
-
allow(@plugin.git).to receive(:info_for_file).with(modified_file2).and_return({ insertions: 43, deletions: 37 })
|
|
157
|
-
allow(@plugin.git).to receive(:info_for_file).with(modified_strings).and_return({ insertions: 432, deletions: 297 })
|
|
158
|
-
allow(@plugin.git).to receive(:info_for_file).with(deleted_file1).and_return({ deletions: 246 })
|
|
159
|
-
allow(@plugin.git).to receive(:info_for_file).with(deleted_file2).and_return({ deletions: 493 })
|
|
160
|
-
allow(@plugin.git).to receive(:info_for_file).with(deleted_test_file).and_return({ deletions: 222 })
|
|
161
|
-
allow(@plugin.git).to receive(:info_for_file).with(deleted_strings).and_return({ deletions: 593 })
|
|
162
163
|
end
|
|
163
164
|
end
|
|
164
165
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: danger-dangermattic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Automattic
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-06-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: danger
|
|
@@ -16,14 +16,20 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '9.
|
|
19
|
+
version: '9.5'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 9.5.3
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
27
|
- - "~>"
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '9.
|
|
29
|
+
version: '9.5'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 9.5.3
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: danger-plugin-api
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -201,6 +207,7 @@ files:
|
|
|
201
207
|
- ".buildkite/gem-push.sh"
|
|
202
208
|
- ".buildkite/pipeline.yml"
|
|
203
209
|
- ".bundle/config"
|
|
210
|
+
- ".github/dependabot.yml"
|
|
204
211
|
- ".github/workflows/README.md"
|
|
205
212
|
- ".github/workflows/reusable-check-labels-on-issues.yml"
|
|
206
213
|
- ".github/workflows/reusable-retry-buildkite-step-on-events.yml"
|
|
@@ -209,7 +216,9 @@ files:
|
|
|
209
216
|
- ".rubocop.yml"
|
|
210
217
|
- ".ruby-version"
|
|
211
218
|
- ".yardopts"
|
|
219
|
+
- AGENTS.md
|
|
212
220
|
- CHANGELOG.md
|
|
221
|
+
- CLAUDE.md
|
|
213
222
|
- Gemfile
|
|
214
223
|
- Gemfile.lock
|
|
215
224
|
- Guardfile
|