Skip to content

Commit 8596074

Browse files
committed
Merge branch 'master' into srest2021/ENG-6194-frontend
2 parents f76d933 + e7e3888 commit 8596074

File tree

1,321 files changed

+22354
-23710
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,321 files changed

+22354
-23710
lines changed

.claude/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"Bash(grep:*)",
1010
"Bash(pnpm run test:*)",
1111
"Bash(pytest:*)",
12+
"Bash(mypy:*)",
1213
"Bash(pre-commit run:*)",
1314
"Bash(gh pr checks:*)",
1415
"Bash(gh pr view:*)",

.github/actions/setup-sentry/action.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ inputs:
1515
description: 'Mode to bring up by new devservices'
1616
required: false
1717
default: 'default'
18+
skip-devservices:
19+
description: 'Skip bringing up devservices'
20+
required: false
21+
default: 'false'
1822

1923
outputs:
2024
matrix-instance-number:
@@ -38,7 +42,7 @@ runs:
3842
# isn't right because job-total will be 3x larger and you'd never run 2/3 of the tests.
3943
# MATRIX_INSTANCE_TOTAL: ${{ strategy.job-total }}
4044
run: |
41-
echo "SENTRY_SKIP_BACKEND_VALIDATION=1" >> $GITHUB_ENV
45+
echo "SENTRY_SKIP_SERVICE_VALIDATION=1" >> $GITHUB_ENV
4246
4347
# for actions/setup-python which always runs pip install
4448
echo "PIP_DISABLE_PIP_VERSION_CHECK=on" >> $GITHUB_ENV
@@ -122,6 +126,7 @@ runs:
122126
python3 -m tools.fast_editable --path .
123127
124128
- name: Start new devservices
129+
if: ${{ inputs.skip-devservices != 'true' }}
125130
shell: bash --noprofile --norc -eo pipefail -ux {0}
126131
env:
127132
WORKDIR: ${{ inputs.workdir }}

.github/workflows/backend.yml

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,34 @@ jobs:
7373
devservices logs
7474
fi
7575
76-
backend-test:
76+
calculate-shards:
7777
if: needs.files-changed.outputs.backend == 'true'
7878
needs: files-changed
79+
name: calculate test shards
80+
runs-on: ubuntu-24.04
81+
timeout-minutes: 5
82+
outputs:
83+
shard-count: ${{ steps.calculate-shards.outputs.shard-count }}
84+
shard-indices: ${{ steps.calculate-shards.outputs.shard-indices }}
85+
86+
steps:
87+
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
88+
89+
- name: Setup sentry env
90+
uses: ./.github/actions/setup-sentry
91+
id: setup
92+
with:
93+
mode: backend-ci
94+
skip-devservices: true
95+
96+
- name: Calculate test shards
97+
id: calculate-shards
98+
run: |
99+
python3 .github/workflows/scripts/calculate-backend-test-shards.py
100+
101+
backend-test:
102+
if: needs.files-changed.outputs.backend == 'true'
103+
needs: [files-changed, calculate-shards]
79104
name: backend test
80105
runs-on: ubuntu-24.04
81106
timeout-minutes: 60
@@ -88,14 +113,12 @@ jobs:
88113
# and reducing the risk that one of many runs would turn red again (read: intermittent tests)
89114
fail-fast: false
90115
matrix:
91-
# XXX: When updating this, make sure you also update MATRIX_INSTANCE_TOTAL.
92-
instance:
93-
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
116+
# Dynamic matrix from calculate-shards
117+
instance: ${{ fromJSON(needs.calculate-shards.outputs.shard-indices) }}
94118

95119
env:
96-
# XXX: `MATRIX_INSTANCE_TOTAL` must be hardcoded to the length of `strategy.matrix.instance`.
97-
# If this increases, make sure to also increase `flags.backend.after_n_builds` in `codecov.yml`.
98-
MATRIX_INSTANCE_TOTAL: 22
120+
# Dynamic total from calculate-shards
121+
MATRIX_INSTANCE_TOTAL: ${{ needs.calculate-shards.outputs.shard-count }}
99122
TEST_GROUP_STRATEGY: roundrobin
100123

101124
steps:
@@ -406,6 +429,7 @@ jobs:
406429
api-docs,
407430
backend-test,
408431
backend-migration-tests,
432+
calculate-shards,
409433
cli,
410434
files-changed,
411435
requirements,

.github/workflows/frontend.yml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,7 @@ jobs:
209209
GITHUB_PR_REF: ${{ github.event.pull_request.head.ref || github.ref }}
210210
# XXX: CI_NODE_TOTAL must be hardcoded to the length of strategy.matrix.instance.
211211
# Otherwise, if there are other things in the matrix, using strategy.job-total
212-
# wouldn't be correct. Also, if this increases, make sure to also increase
213-
# `flags.frontend.after_n_builds` in `codecov.yml`.
212+
# wouldn't be correct.
214213
CI_NODE_TOTAL: 4
215214
CI_NODE_INDEX: ${{ matrix.instance }}
216215
# Disable testing-library from printing out any of of the DOM to
@@ -219,12 +218,7 @@ jobs:
219218
#
220219
# This quiets up the logs quite a bit.
221220
DEBUG_PRINT_LIMIT: 0
222-
run: |
223-
if [ ${{ github.ref }} = 'refs/heads/master' ]; then
224-
pnpm run test-ci --forceExit --coverage
225-
else
226-
pnpm run test-ci --forceExit
227-
fi
221+
run: pnpm run test-ci --forceExit
228222

229223
# This check runs once all dependant jobs have passed
230224
# It symbolizes that all required Frontend checks have succesfully passed (Or skipped)
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env python3
2+
import json
3+
import math
4+
import os
5+
import re
6+
import subprocess
7+
import sys
8+
9+
TESTS_PER_SHARD = 1200
10+
MIN_SHARDS = 1
11+
MAX_SHARDS = 22
12+
DEFAULT_SHARDS = 22
13+
14+
PYTEST_ARGS = [
15+
"pytest",
16+
"--collect-only",
17+
"--quiet",
18+
"tests",
19+
"--ignore=tests/acceptance",
20+
"--ignore=tests/apidocs",
21+
"--ignore=tests/js",
22+
"--ignore=tests/tools",
23+
]
24+
25+
26+
def collect_test_count():
27+
try:
28+
result = subprocess.run(
29+
PYTEST_ARGS,
30+
capture_output=True,
31+
text=True,
32+
check=False,
33+
)
34+
35+
# Parse output for "N tests collected"
36+
# Format: "27000 tests collected in 18.53s"
37+
match = re.search(r"(\d+) tests? collected", result.stdout + result.stderr)
38+
if match:
39+
count = int(match.group(1))
40+
print(f"Collected {count} tests", file=sys.stderr)
41+
return count
42+
43+
# If no match, check if pytest failed
44+
if result.returncode != 0:
45+
print(
46+
f"Pytest collection failed (exit {result.returncode})",
47+
file=sys.stderr,
48+
)
49+
print(result.stderr, file=sys.stderr)
50+
return None
51+
52+
print("No tests collected", file=sys.stderr)
53+
return 0
54+
except Exception as e:
55+
print(f"Error collecting tests: {e}", file=sys.stderr)
56+
return None
57+
58+
59+
def calculate_shards(test_count):
60+
if test_count is None:
61+
print(f"Using default shard count: {DEFAULT_SHARDS}", file=sys.stderr)
62+
return DEFAULT_SHARDS
63+
64+
if test_count == 0:
65+
print(f"No tests to run, using minimum: {MIN_SHARDS}", file=sys.stderr)
66+
return MIN_SHARDS
67+
68+
calculated = math.ceil(test_count / TESTS_PER_SHARD)
69+
bounded = max(MIN_SHARDS, min(calculated, MAX_SHARDS))
70+
71+
if bounded != calculated:
72+
print(
73+
f"Calculated {calculated} shards, bounded to {bounded}",
74+
file=sys.stderr,
75+
)
76+
else:
77+
print(
78+
f"Calculated {bounded} shards ({test_count} tests ÷ {TESTS_PER_SHARD})",
79+
file=sys.stderr,
80+
)
81+
82+
return bounded
83+
84+
85+
def main():
86+
test_count = collect_test_count()
87+
shard_count = calculate_shards(test_count)
88+
# Generate a JSON array of shard indices [0, 1, 2, ..., shard_count-1]
89+
shard_indices = json.dumps(list(range(shard_count)))
90+
91+
github_output = os.getenv("GITHUB_OUTPUT")
92+
if github_output:
93+
with open(github_output, "a") as f:
94+
f.write("\n")
95+
f.write(f"shard-count={shard_count}\n")
96+
f.write(f"shard-indices={shard_indices}\n")
97+
98+
print(f"shard-count={shard_count}")
99+
print(f"shard-indices={shard_indices}")
100+
101+
return 0
102+
103+
104+
if __name__ == "__main__":
105+
sys.exit(main())

.github/workflows/shuffle-tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ jobs:
4040

4141
env:
4242
# XXX: `MATRIX_INSTANCE_TOTAL` must be hardcoded to the length of `strategy.matrix.instance`.
43-
# If this increases, make sure to also increase `flags.backend.after_n_builds` in `codecov.yml`.
4443
MATRIX_INSTANCE_TOTAL: 11
4544

4645
steps:

build-utils/ts-extract-gettext.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
22
import path, {resolve} from 'node:path'; // Added for module check
33
import {fileURLToPath} from 'node:url';
44

5+
import {ATTRIBUTE_METADATA} from '@sentry/conventions';
56
import {po} from 'gettext-parser';
67
import type {GetTextTranslation, GetTextTranslations} from 'gettext-parser';
78
import {glob} from 'tinyglobby';
@@ -185,6 +186,11 @@ function extractTranslationsFromFileContent(
185186
? existingEntry.comments.translator + `\n`
186187
: '') + translate.comments.translator;
187188
}
189+
// Preserve msgid_plural if the new entry has one
190+
if (finalTranslateEntry.msgid_plural && !existingEntry.msgid_plural) {
191+
existingEntry.msgid_plural = finalTranslateEntry.msgid_plural;
192+
existingEntry.msgstr = finalTranslateEntry.msgstr;
193+
}
188194
} else {
189195
currentContext[finalTranslateEntry.msgid] = finalTranslateEntry;
190196
}
@@ -197,6 +203,38 @@ function extractTranslationsFromFileContent(
197203
visit(sourceFile);
198204
}
199205

206+
function extractBriefsFromAttributeMetadata(
207+
gettextData: GetTextTranslations,
208+
nplurals: number
209+
): void {
210+
const context = gettextData.translations[''] ?? {};
211+
212+
for (const [attributeName, metadata] of Object.entries(ATTRIBUTE_METADATA)) {
213+
const brief = metadata.brief;
214+
215+
if (!brief || typeof brief !== 'string') {
216+
continue;
217+
}
218+
219+
// Skip if already exists
220+
if (context[brief]) {
221+
continue;
222+
}
223+
224+
const translate: GetTextTranslation = {
225+
msgid: brief,
226+
msgstr: [''],
227+
comments: {
228+
extracted: `Attribute \`${attributeName}\` metadata description from @sentry/conventions`,
229+
},
230+
};
231+
232+
context[brief] = translate;
233+
}
234+
235+
gettextData.translations[''] = context;
236+
}
237+
200238
const OUTPUT_FILE = 'build/javascript.po';
201239
const BASE_DIRECTORY = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
202240

@@ -248,6 +286,9 @@ async function main() {
248286

249287
await Promise.all(files.map(processFile));
250288

289+
// Extract briefs from ATTRIBUTE_METADATA
290+
extractBriefsFromAttributeMetadata(gettextData, nplurals);
291+
251292
const outputFilePath = path.resolve(BASE_DIRECTORY, OUTPUT_FILE);
252293
const compiledPoBuffer = po.compile(gettextData, {sort: true});
253294
await fs.writeFile(outputFilePath, compiledPoBuffer, 'utf8');

0 commit comments

Comments
 (0)