Skip to content

Use shared localization submission and pull-only release imports - #609

Merged
Bill Schnurr (bschnurr) merged 4 commits into
mainfrom
users/bschnurr/shared-localization
Nov 5, 2026
Merged

Bill Schnurr (bschnurr) merged 4 commits into
mainfrom
users/bschnurr/shared-localization

Conversation

@bschnurr

Copy link
Copy Markdown
Member

Summary

  • Use the shared Python localization core and composite action rather than copying helper scripts into this extension.
  • Keep GitHub English submission separate from pull-only DevDiv stable/pre-release imports.
  • Use drop branch dev and canonical extension ID ms-python.flake8; remove tracked generated locale snapshots where present and keep generated files in VSIX selection.
  • Preserve existing runtime metadata, extraction sources, feeds, platform matrices, and version/validation behavior.
  • Validate readiness independently in every required real-language manifest/runtime table; allow individual untranslated entries and reject missing/wholly untranslated tables before packaging.

Shared dependencies

The GitHub action and Azure tooling checkout are pinned to d1877b84bb10ff7caa6bd12ab4e75498ba760790. Azure templates temporarily reference the shared feature branch for verification; switch to Pylance-Eng main after the dependency merges, before merging this caller.

Verification

Executable guards read the actual YAML and preserve existing triggers, build steps, publication defaults, validation contacts, version flags, and matrices. Both release YAMLs expanded with publishing explicitly disabled (environments used the enabled validation definition because its native definitions are disabled). Actual English export used the pinned CLI through the internal feed. Changed YAML/JSON formatting and whitespace checks passed.

Required setup and release blockers

Current canonical handback lacks required runtime tables; submit complete English and wait for runtime translations.

No repository-level PYTHON_LOCALIZATION_APP_ID variable or PYTHON_LOCALIZATION_PRIVATE_KEY secret is currently visible here. Organization-level access could not be verified with current permissions. Configure or expose them through the owners; do not copy credentials between repositories. Authorize Github-Pylance for both release resources/pipelines.

Keep this PR draft while dependencies/setup/handbacks are pending. No merge or publication is requested.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Eduardo Villalpando Mello (edvilme) pushed a commit to microsoft/vscode-python-ci that referenced this pull request Oct 30, 2026
…lation checkout support

## Shared pull-only release localization
This PR replaces the unused unmerged Azure English extraction/submission machinery with an opt-in pullLocalization interface on both public release wrappers. It invokes the same Python core as the public GitHub composite action in microsoft/vscode-common-python-lsp#125 rather than maintaining a second importer.

- Consumers declare translations (private drop dev) and localizationTools (reviewed immutable common-LSP SHA), with literal Github-Pylance endpoints.
- Source stays at workspace/s; tools and translations are sibling checkouts outside extension source.
- Run the shared importer after caller build/pre-built overlays and before final VSIX packaging.
- Keep the default self SDL scan, explicitly include executable tooling, and exclude translation data. Additional caller exclusions remain supported; sourceAnalysisPool preserves original caller pools.
- Derive canonical IDs and optional runtime metadata. Require all locales and per-table real-language translation evidence before copying; allow individual untranslated strings.
- No release-side English extraction, app write-token acquisition, Git pushes, or translation-update PRs. Existing signing/setup PowerShell is intentionally unchanged.
- Preserve opt-out callers, platform/version parameters, stable manual validation, and publication gates. Azure expansion can persist checkout credentials; no claim is made otherwise.

## Verification and rollout
Actual YAML-reading guards and shared Python/Git fixtures passed. Both release entry points for seven enabled repositories expanded successfully with publishing explicitly disabled. Both environments definitions are disabled; their exact YAML also expanded through the enabled isort definition without enabling or modifying those definitions. All expanded platform jobs preserve import order, source paths, SDL scanning, and publication gates. Shared core CI and the actual isort GitHub submission action passed.

Shared dependency: microsoft/vscode-common-python-lsp#125
Pilot: microsoft/vscode-isort#777
Sibling draft PRs:
- debugger: microsoft/vscode-python-debugger#1134
- environments: microsoft/vscode-python-environments#1971
- pylint: microsoft/vscode-pylint#915
- flake8: microsoft/vscode-flake8#609
- autopep8 (including runtime l10n): microsoft/vscode-autopep8#415
- mypy: microsoft/vscode-mypy#651
- black-formatter: microsoft/vscode-black-formatter#833

Keep this PR and callers draft pending dependencies, credential setup, and real handbacks. Current drop blocks isort (English-only), environments (missing canonical output), and flake8/mypy/black (missing runtime tables). Debugger, pylint, and autopep8 passed actual manifest/runtime imports from current drop. Autopep8 runtime ...
@bschnurr
Bill Schnurr (bschnurr) marked this pull request as ready for review November 2, 2026 23:33
@rchiodo

Rich Chiodo (rchiodo) commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

@rchiodo

Copy link
Copy Markdown
Contributor

Result: ⚠️ partially-verified

Verification details

Verification: Automated verification is best effort. The PR author remains responsible for testing.

Summary: Changed-YAML formatting, three executable configuration guards, and VSCE selection of all 28 generated locale files passed in the container. The PR adds no tests. Python version tests could not start because of reviewer-environment dependency gaps; no PR-caused failure was demonstrated. Actual English submission, Azure template expansion, translation-readiness validation, and release imports remain unverified, so confidence is limited to local configuration and packaging selection. **Reviewer dependency follow-up (nonblocking):** Suggest a separate PR adding `freezegun (no version constraint declared)`, `pytest==9.1.1` to the official reviewer dependency declarations, using `noxfile.py`, `src/test/python_tests/requirements.txt` as the dependency/version evidence. Do not automatically create that PR. This is reviewer environment maintenance, not a required change to the reviewed PR.

Test runs: 3 passed, 2 failed, 1 not run

  • ⚠️ Not run | Extension version regression tests | .venv/bin/python -m pytest -q build/test_update_ext_version.py
  • ❌ Failed | unrelated to this PR | Dependency and test discovery | printf 'PROFILE=%s\n' "$AUTOMATION_SANDBOX_PROFILE"; uname -s; python --version; node --version; npm --version; git status --short; git log -3 --oneline; git diff HEAD~1 HEAD --stat; git ls-files 'test' 'localiz' 'requirements' 'lock'; printf '\nDependency/submodule presence\n'; test ! -d node_modules || echo root_node_modules_present; ls -la external/vscode-common-python-lsp; python -c "import sys, importlib.util; print('interpreter:',sys.executable); print({m:importlib.util.find_spec(m) is not None for m in ['pytest','yaml','freezegun']})"; printf '\nTest and build metadata\n'; cat .gitmodules .nvmrc .npmrc src/test/python_tests/requirements.in requirements.in; git submodule status
  • ❌ Failed | unrelated to this PR | Offline dependency preparation | cat external/vscode-common-python-lsp/typescript/package.json; npm ci --offline --ignore-scripts && npm --prefix external/vscode-common-python-lsp/typescript ci --offline --ignore-scripts && python -m venv --system-site-packages .venv && .venv/bin/python -m pip install --no-index --find-links=/opt/python-wheelhouse freezegun
  • ✅ Passed | Changed YAML formatting | node ./node_modules/prettier/bin/prettier.cjs --check .github/workflows/localization.yml build/azure-devdiv-pipeline.stable.yml build/azure-devdiv-pipeline.pre-release.yml
  • ✅ Passed | Localization workflow and release-caller contracts | python - <<'PY'
    import json
    from pathlib import Path
    import unittest
    import yaml

PIN = 'd1877b84bb10ff7caa6bd12ab4e75498ba760790'
def load(path):
return yaml.load(Path(path).read_text(), Loader=yaml.BaseLoader)

class LocalizationContracts(unittest.TestCase):
def test_submission_workflow(self):
workflow = load('.github/workflows/localization.yml')
self.assertEqual(workflow['permissions'], {'contents': 'read'})
self.assertEqual(workflow['on']['push']['branches'], ['main'])
self.assertIn('workflow_dispatch', workflow['on'])
self.assertTrue({'package.json', 'package.nls.json', 'src/**/*.ts', 'external/vscode-common-python-lsp', '.gitmodules', '.nvmrc', '.github/workflows/localization.yml'} <= set(workflow['on']['push']['paths']))
self.assertEqual(workflow['concurrency']['cancel-in-progress'], 'false')
job = workflow['jobs']['localization']
self.assertEqual(job['if'], "github.repository == 'microsoft/vscode-flake8'")
self.assertEqual(len(job['steps']), 1)
step = job['steps'][0]
self.assertEqual(step['uses'], 'microsoft/vscode-common-python-lsp/.github/actions/localization@' + PIN)
inputs = step['with']
self.assertEqual(inputs['app-id'], '${{ vars.PYTHON_LOCALIZATION_APP_ID }}')
self.assertEqual(inputs['private-key'], '${{ secrets.PYTHON_LOCALIZATION_PRIVATE_KEY }}')
self.assertEqual(inputs['submodules'], 'recursive')
self.assertEqual(json.loads(inputs['source-paths']), ['src', 'external/vscode-common-python-lsp/typescript/src'])
self.assertEqual(inputs['node-version'], Path('.nvmrc').read_text().strip())

def test_release_callers(self):
    for kind in [&#x27;stable&#x27;, &#x27;pre-release&#x27;]:
        with self.subTest(kind=kind):
            pipeline = load(f&#x27;build/azure-devdiv-pipeline.{kind}.yml&#x27;)
            self.assertEqual(pipeline[&#x27;pr&#x27;], &#x27;none&#x27;)
            resources = {r[&#x27;repository&#x27;]: r for r in pipeline[&#x27;resources&#x27;][&#x27;repositories&#x27;]}
            self.assertEqual(resources[&#x27;translations&#x27;], {&#x27;repository&#x27;: &#x27;translations&#x27;, &#x27;type&#x27;: &#x27;github&#x27;, &#x27;name&#x27;: &#x27;microsoft/vscode-python-extensions-loc&#x27;, &#x27;ref&#x27;: &#x27;refs/heads/dev&#x27;, &#x27;endpoint&#x27;: &#x27;Github-Pylance&#x27;})
            self.assertEqual(resources[&#x27;localizationTools&#x27;][&#x27;ref&#x27;], PIN)
            self.assertEqual(resources[&#x27;localizationTools&#x27;][&#x27;endpoint&#x27;], &#x27;Github-Pylance&#x27;)
            self.assertEqual(pipeline[&#x27;extends&#x27;][&#x27;template&#x27;], f&#x27;azure-pipelines/extension/{kind}.yml@templates&#x27;)
            params = pipeline[&#x27;extends&#x27;][&#x27;parameters&#x27;]
            self.assertEqual(params[&#x27;pullLocalization&#x27;], &#x27;true&#x27;)
            self.assertEqual(params[&#x27;standardizedVersioning&#x27;], &#x27;true&#x27;)
            self.assertEqual(params[&#x27;publishExtension&#x27;], &#x27;${{ parameters.publishExtension }}&#x27;)
            self.assertEqual(params[&#x27;sourceAnalysisPool&#x27;], &#x27;VSEng-MicroBuildVSStable&#x27;)
            self.assertEqual(params[&#x27;customNPMRegistry&#x27;], &#x27;$(AZURE_ARTIFACTS_FEED)&#x27;)
            self.assertEqual(params[&#x27;teamName&#x27;], &#x27;$(TeamName)&#x27;)
            for flag in [&#x27;ghCreateTag&#x27;, &#x27;ghCreateRelease&#x27;, &#x27;ghReleaseAddChangeLog&#x27;]:
                self.assertEqual(params[flag], &#x27;true&#x27;)
            definitions = {p[&#x27;name&#x27;]: p for p in pipeline[&#x27;parameters&#x27;]}
            self.assertEqual(definitions[&#x27;publishExtension&#x27;][&#x27;default&#x27;], &#x27;true&#x27;)
            steps = definitions[&#x27;buildSteps&#x27;][&#x27;default&#x27;]
            version = &#x27;python ./build/update_ext_version.py &#x27; + (&#x27;--release &#x27; if kind == &#x27;stable&#x27; else &#x27;&#x27;) + &#x27;--for-publishing&#x27;
            self.assertEqual([s[&#x27;script&#x27;] for s in steps], [&#x27;git submodule update --init --recursive&#x27;, &#x27;npm ci&#x27;, &#x27;python -m pip install -U pip&#x27;, &#x27;python -m pip install wheel&#x27;, &#x27;python -m pip install nox&#x27;, &#x27;python -m nox --session install_bundled_libs&#x27;, version, &#x27;npm run package&#x27;])
            self.assertFalse(any(&#x27;submit&#x27; in key.lower() for key in params))
            if kind == &#x27;stable&#x27;:
                self.assertEqual(params[&#x27;validationNotifyUsers&#x27;], &#x27;plseng@microsoft.com&#x27;)
                self.assertEqual(pipeline[&#x27;trigger&#x27;][&#x27;branches&#x27;][&#x27;include&#x27;], [&#x27;release/*&#x27;])
            else:
                self.assertEqual(pipeline[&#x27;trigger&#x27;], &#x27;none&#x27;)
                self.assertEqual(pipeline[&#x27;schedules&#x27;][0][&#x27;cron&#x27;], &#x27;0 10 * * 1-5&#x27;)
                self.assertEqual(pipeline[&#x27;schedules&#x27;][0][&#x27;branches&#x27;][&#x27;include&#x27;], [&#x27;main&#x27;])

def test_runtime_identity_and_english_source(self):
    package = json.loads(Path(&#x27;package.json&#x27;).read_text())
    self.assertEqual(package[&#x27;publisher&#x27;] + &#x27;.&#x27; + package[&#x27;name&#x27;], &#x27;ms-python.flake8&#x27;)
    self.assertEqual(package[&#x27;l10n&#x27;], &#x27;./l10n&#x27;)
    self.assertTrue(json.loads(Path(&#x27;package.nls.json&#x27;).read_text()))
    ignores = Path(&#x27;.gitignore&#x27;).read_text().splitlines()
    self.assertIn(&#x27;/package.nls.*.json&#x27;, ignores)
    self.assertIn(&#x27;/l10n/&#x27;, ignores)

unittest.main(verbosity=2)
PY

  • ✅ Passed | Generated localization files remain in VSCE selection | python - <<'PY'
    import json
    from pathlib import Path
    import subprocess

locales = 'cs de es fr it ja ko pl pt-br qps-ploc ru tr zh-cn zh-tw'.split()
created = []
runtime = Path('l10n')
assert not runtime.exists(), 'Expected a clean checkout without generated l10n/'
try:
runtime.mkdir()
for locale in locales:
manifest = Path(f'package.nls.{locale}.json')
bundle = runtime / f'bundle.l10n.{locale}.json'
assert not manifest.exists(), str(manifest)
manifest.write_text(json.dumps({'extension.description': f'fixture-{locale}'}))
created.append(manifest)
bundle.write_text(json.dumps({'Open logs': f'fixture-{locale}'}))
created.append(bundle)
command = ['node', './node_modules/@vscode/vsce/vsce', 'ls', '--no-dependencies']
result = subprocess.run(command, capture_output=True, text=True)
print('VSCE selection exit:', result.returncode)
if result.stderr:
print(result.stderr[-4000:])
assert result.returncode == 0, result.stdout[-4000:]
selected = set(result.stdout.splitlines())
required = {str(path) for path in created} | {'package.nls.json', 'package.json'}
missing = required - selected
assert not missing, f'Generated locales excluded from VSIX: {sorted(missing)}'
print(f'PASS: all {len(created)} generated locale fixtures plus English and package manifests selected by VSCE despite .gitignore.')
print('Fixtures check packaging selection only, not translation readiness or a built VSIX.')
finally:
for path in created:
path.unlink()
if runtime.exists():
runtime.rmdir()
PY

⚠️ Extension version regression tests diagnostic output
/workspace/.venv/bin/python: No module named pytest
[container exit=1]
The writable environment created with --system-site-packages did not inherit the prepared interpreter's pytest. src/test/python_tests/requirements.txt declares pytest==9.1.1. The dependency bootstrap had already failed on unavailable freezegun; no further bootstrap was attempted within the six-command budget.
❌ Dependency and test discovery diagnostic output
PROFILE=typescript
Linux
Python 3.13.18
v24.17.0
11.13.0
fatal: not a git repository (or any parent up to mount point /)
interpreter: /opt/automation-venv/bin/python
{'pytest': True, 'yaml': True, 'freezegun': False}
[container exit=128]
Git metadata probes failed in the copied workspace; repository navigation tools supplied file discovery instead. Separately, build/test_update_ext_version.py imports freezegun, declared without a version constraint by noxfile.py:142. Offline preparation was attempted in the following command and found no cached distribution.
❌ Offline dependency preparation diagnostic output
Root npm restore: added 535 packages, and audited 537 packages; found 0 vulnerabilities.
Shared TypeScript npm restore: added 188 packages, and audited 189 packages; found 0 vulnerabilities.
Looking in links: /opt/python-wheelhouse, /opt/python-wheelhouse
ERROR: Could not find a version that satisfies the requirement freezegun (from versions: none)
ERROR: No matching distribution found for freezegun
[container exit=1]
Installation targeted the writable .venv using the noxfile.py test dependency declaration; the offline wheelhouse lacked freezegun.

@rchiodo Rich Chiodo (rchiodo) added the review-auto:changes-requested Automated review: posted blocking findings to address. label Nov 3, 2026
@rchiodo

Copy link
Copy Markdown
Contributor

Result: ⚠️ partially-verified

Verification details

Verification: Automated verification is best effort. The PR author remains responsible for testing.

Summary: Caller-level YAML guards passed for the shared action pin, pull-only release configuration, preserved build steps, triggers, feeds, and version flags. Formatting passed, and VSCE selected all 28 synthetic generated locale files despite Git ignores. The PR adds no tests. Release-version tests could not run because the declared freezegun dependency was unavailable offline. Actual shared-action execution, Azure template expansion, translation import, and readiness validation remain unverified. **Reviewer dependency follow-up (nonblocking):** Suggest a separate PR adding `freezegun (no version constraint declared)` to the official reviewer dependency declarations, using `noxfile.py` as the dependency/version evidence. Do not automatically create that PR. This is reviewer environment maintenance, not a required change to the reviewed PR.

Test runs: 4 passed, 1 failed, 2 not run

  • ⚠️ Not run | Release-version regression tests | python -m pytest build/test_update_ext_version.py -q
  • ⚠️ Not run | Shared localization and Azure release integration
  • ❌ Failed | unrelated to this PR | Offline dependency preparation | npm ci --offline --ignore-scripts > /tmp/flake8-root-install.log 2>&1; root_status=$?; tail -15 /tmp/flake8-root-install.log; npm --prefix external/vscode-common-python-lsp/typescript ci --offline --ignore-scripts > /tmp/flake8-shared-install.log 2>&1; shared_status=$?; tail -15 /tmp/flake8-shared-install.log; python -m venv --system-site-packages .venv && .venv/bin/python -m pip install --no-index --find-links=/opt/python-wheelhouse freezegun > /tmp/flake8-python-install.log 2>&1; python_status=$?; tail -15 /tmp/flake8-python-install.log; printf 'setup statuses: root=%s shared=%s python=%s\n' "$root_status" "$shared_status" "$python_status"; test "$root_status" -eq 0 && test "$shared_status" -eq 0 && test "$python_status" -eq 0
  • ✅ Passed | Changed YAML formatting | ./node_modules/.bin/prettier --check .github/workflows/localization.yml build/azure-devdiv-pipeline.stable.yml build/azure-devdiv-pipeline.pre-release.yml
  • ✅ Passed | Dependency and test discovery | printf 'PROFILE=%s\n' "$AUTOMATION_SANDBOX_PROFILE"; git log -3 --oneline; git status --short; git diff --name-status HEAD~1 HEAD; node --version; npm --version; python --version; python -c "import sys, importlib.util; print('interpreter:', sys.executable); print({m: importlib.util.find_spec(m) is not None for m in ['pytest','yaml','freezegun']})"; for p in node_modules external/vscode-common-python-lsp/typescript/node_modules external/vscode-common-python-lsp/.github/actions/localization/action.yml external/vscode-common-python-lsp/python/pyproject.toml; do if test -e "$p"; then printf 'present %s\n' "$p"; else printf 'missing %s\n' "$p"; fi; done; find build src/test external/vscode-common-python-lsp -maxdepth 4 -type f ( -name 'localiz' -o -name 'test' -o -name 'package.json' -o -name 'pyproject.toml' ) | head -65
  • ✅ Passed | Localization caller contract guards | python - <<'PY'
    import json
    from pathlib import Path
    import yaml
    pin = 'd1877b84bb10ff7caa6bd12ab4e75498ba760790'
    w = yaml.load(Path('.github/workflows/localization.yml').read_text(), Loader=yaml.BaseLoader)
    assert set(w['on']) == {'workflow_dispatch', 'push'}
    assert w['on']['push']['branches'] == ['main']
    assert set(w['on']['push']['paths']) == {'package.json','package.nls.json','src/**/.ts','external/vscode-common-python-lsp','.gitmodules','.nvmrc','.github/workflows/localization.yml'}
    assert w['permissions'] == {'contents': 'read'} and w['concurrency']['cancel-in-progress'] == 'false'
    j = w['jobs']['localization']
    assert j['if'] == "github.repository == 'microsoft/vscode-flake8'" and j['timeout-minutes'] == '20'
    a, = j['steps']
    assert a['uses'] == 'microsoft/vscode-common-python-lsp/.github/actions/localization@' + pin
    assert a['with'] == {'app-id':'${{ vars.PYTHON_LOCALIZATION_APP_ID }}','private-key':'${{ secrets.PYTHON_LOCALIZATION_PRIVATE_KEY }}','submodules':'recursive','source-paths':'["src", "external/vscode-common-python-lsp/typescript/src"]','node-version':Path('.nvmrc').read_text().strip()}
    print('PASS: GitHub submission triggers, credentials wiring, permissions, source paths and immutable pin')
    for channel in ('stable', 'pre-release'):
    p = yaml.safe_load(Path(f'build/azure-devdiv-pipeline.{channel}.yml').read_text())
    r = {x['repository']: x for x in p['resources']['repositories']}
    assert r['translations'] == {'repository':'translations','type':'github','name':'microsoft/vscode-python-extensions-loc','ref':'refs/heads/dev','endpoint':'Github-Pylance'}
    assert r['localizationTools'] == {'repository':'localizationTools','type':'github','name':'microsoft/vscode-common-python-lsp','ref':pin,'endpoint':'Github-Pylance'}
    assert r['templates']['name'] == 'DevDiv/Pylance-Eng' and r['templates']['ref'] == 'refs/heads/users/bschnurr/localization-wrapper'
    assert p['extends']['template'] == f'azure-pipelines/extension/{channel}.yml@templates'
    q = p['extends']['parameters']
    expected = {'buildSteps':'${{ parameters.buildSteps }}','standardizedVersioning':True,'customNPMRegistry':'$(AZURE_ARTIFACTS_FEED)','publishExtension':'${{ parameters.publishExtension }}','teamName':'$(TeamName)','ghCreateTag':True,'ghCreateRelease':True,'ghReleaseAddChangeLog':True,'sourceAnalysisPool':'VSEng-MicroBuildVSStable','pullLocalization':True}
    if channel == 'stable': expected['validationNotifyUsers'] = 'plseng@microsoft.com'
    assert q == expected
    params = {x['name']: x for x in p['parameters']}
    assert params['publishExtension']['default'] is True
    scripts = [x['script'] for x in params['buildSteps']['default']]
    flags = '--release --for-publishing' if channel == 'stable' else '--for-publishing'
    assert scripts == ['git submodule update --init --recursive','npm ci','python -m pip install -U pip','python -m pip install wheel','python -m pip install nox','python -m nox --session install_bundled_libs',f'python ./build/update_ext_version.py {flags}','npm run package']
    assert p['pr'] == 'none'
    if channel == 'stable': assert p['trigger']['branches']['include'] == ['release/
    ']
    else: assert p['trigger'] == 'none' and p['schedules'] == [{'cron':'0 10 * * 1-5','displayName':'Nightly Pre-Release Schedule','always':False,'branches':{'include':['main']}}]
    assert p['variables'] == [{'name':'TeamName','value':'VSCode-flake8'},{'name':'VsixName','value':'flake8.vsix'},{'name':'AZURE_ARTIFACTS_FEED','value':'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/'}]
    print(f'PASS: {channel} caller resources, pull-only parameters, publication defaults, version flags, build steps, triggers and feed')
    pkg = json.loads(Path('package.json').read_text())
    assert pkg['publisher'] + '.' + pkg['name'] == 'ms-python.flake8' and pkg['l10n'] == './l10n'
    assert Path('package.nls.json').is_file() and not list(Path('.').glob('package.nls..json')) and not Path('l10n').exists()
    assert {'/package.nls.
    .json','/l10n/'} <= set(Path('.gitignore').read_text().splitlines())
    print('PASS: canonical extension ID, runtime path, English source retention and generated snapshot removal')
    PY
  • ✅ Passed | Generated locale VSCE selection | python - <<'PY'
    import json, subprocess
    from pathlib import Path
    locales = 'cs de es fr it ja ko pl pt-br qps-ploc ru tr zh-cn zh-tw'.split()
    created = []
    try:
    Path('l10n').mkdir()
    for locale in locales:
    for path, data in [(Path(f'package.nls.{locale}.json'), {'extension.description':'Traduction de test'}), (Path(f'l10n/bundle.l10n.{locale}.json'), {'Open logs':'Journaux'})]:
    path.write_text(json.dumps(data), encoding='utf-8')
    created.append(path)
    result = subprocess.run(['./node_modules/.bin/vsce','ls','--no-dependencies'], text=True, capture_output=True)
    assert result.returncode == 0, result.stdout + result.stderr
    selected = set(result.stdout.splitlines())
    expected = {str(p) for p in created} | {'package.json','package.nls.json'}
    assert expected <= selected, f'Missing VSIX files: {expected - selected}'
    print('PASS: VSCE selects all 28 synthetic generated manifest/runtime locale files and English package.nls.json despite gitignore patterns')
    print('Selection only: no real handback imported, translation-readiness validation or VSIX compilation performed')
    finally:
    for path in created: path.unlink()
    if Path('l10n').exists(): Path('l10n').rmdir()
    PY
⚠️ Release-version regression tests diagnostic output
Dependency discovery used /opt/automation-venv/bin/python (Python 3.13.18): pytest=True, freezegun=False. noxfile.py declares session.install("freezegun") for build tests. Offline setup in .venv failed: ERROR: No matching distribution found for freezegun. The pytest command was not invoked.
⚠️ Shared localization and Azure release integration diagnostic output
The restored submodule does not contain .github/actions/localization/action.yml. The pinned remote action, external Pylance-Eng templates, translation handbacks, and service credentials were not exercised. Static caller checks do not establish template expansion, real export/import, or per-table readiness enforcement.
❌ Offline dependency preparation diagnostic output
Root npm: added 535 packages; found 0 vulnerabilities.
Shared npm: added 188 packages; found 0 vulnerabilities.
Looking in links: /opt/python-wheelhouse, /opt/python-wheelhouse
ERROR: Could not find a version that satisfies the requirement freezegun (from versions: none)
ERROR: No matching distribution found for freezegun
setup statuses: root=0 shared=0 python=1

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) added review-auto:approved Automated review: no blocking findings (approval posted). and removed review-auto:changes-requested Automated review: posted blocking findings to address. labels Nov 3, 2026
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rchiodo

Copy link
Copy Markdown
Contributor

Result: ⚠️ partially-verified

Verification details

Verification: Automated verification is best effort. The PR author remains responsible for testing.

Summary: The production extension build, changed-YAML formatting checks, and targeted configuration assertions passed in the container. Offline Node dependency restoration also succeeded. The generated-locale packaging check failed during VSCE dependency enumeration (`npm ELSPROBLEMS`), before file inclusion could be verified; no PR-caused defect was established. No tests were added by this PR. Shared-action execution, Azure template expansion, and real-handback readiness remain unverified. **Reviewer dependency follow-up (nonblocking):** Suggest a separate PR adding `freezegun (unconstrained)` to the official reviewer dependency declarations, using `noxfile.py` as the dependency/version evidence. Do not automatically create that PR. This is reviewer environment maintenance, not a required change to the reviewed PR.

Test runs: 4 passed, 1 failed, 2 not run

  • ⚠️ Not run | Shared localization action and Azure release integration
  • ⚠️ Not run | Dependency and test discovery | printf 'PROFILE=%s\n' "$AUTOMATION_SANDBOX_PROFILE"; uname -s; python --version; node --version; npm --version; git status --short; git log -3 --oneline; git diff HEAD^ HEAD --name-status; git submodule status; printf '\nLocalization and test files\n'; find build src/test external/vscode-common-python-lsp -maxdepth 4 -type f ( -iname 'localiz' -o -iname 'test' -o -name package.json -o -name pyproject.toml -o -name requirements.txt -o -name action.yml ) | sort; printf '\nAvailable dependencies\n'; test ! -d node_modules || echo root_node_modules_present; test ! -d external/vscode-common-python-lsp/typescript/node_modules || echo shared_node_modules_present; python -c "import sys, importlib.util; print('interpreter:', sys.executable); print({m: importlib.util.find_spec(m) is not None for m in ['pytest', 'yaml', 'freezegun']})"
  • ❌ Failed | relationship unknown | Generated locale VSCE file-selection check | node - <<'JS'
    const fs = require('node:fs');
    const assert = require('node:assert/strict');
    const { listFiles } = require('@vscode/vsce');
    (async () => {
    const locales = ['cs','de','es','fr','it','ja','ko','pl','pt-br','qps-ploc','ru','tr','zh-cn','zh-tw'];
    const generated = locales.flatMap(l => [package.nls.${l}.json, l10n/bundle.l10n.${l}.json]);
    assert.ok(generated.every(p => !fs.existsSync(p)), 'fixtures must not overwrite existing files');
    fs.mkdirSync('l10n');
    try {
    for (const p of generated) fs.writeFileSync(p, JSON.stringify({'test.key':'translated fixture'}));
    const files = await listFiles({cwd:process.cwd(), dependencies:false});
    for (const p of generated) assert.ok(files.includes(p), VSCE omitted ${p});
    assert.ok(files.includes('package.nls.json'), 'VSCE omitted English manifest');
    console.log(PASS: VSCE selected all ${generated.length} generated locale files plus package.nls.json despite Git ignores);
    console.log('LIMITATION: fixture selection only; no real handback import or translation readiness validation');
    } finally {
    for (const p of generated) fs.rmSync(p, {force:true});
    fs.rmdirSync('l10n');
    }
    })().catch(err => {console.error(err); process.exitCode = 1;});
    JS
  • ✅ Passed | Changed YAML formatting | node ./node_modules/prettier/bin/prettier.cjs --check .github/workflows/localization.yml build/azure-devdiv-pipeline.stable.yml build/azure-devdiv-pipeline.pre-release.yml
  • ✅ Passed | Production extension build | npm run package
  • ✅ Passed | Offline Node dependency restoration and shared build | python - <<'PY'
    import json
    from pathlib import Path
    for p in [Path('package.json'), Path('external/vscode-common-python-lsp/typescript/package.json')]:
    data = json.loads(p.read_text())
    print(p, json.dumps({k: data.get(k) for k in ['scripts', 'dependencies', 'devDependencies']}, indent=2))
    PY
    npm ci --offline --ignore-scripts && npm ci --offline --ignore-scripts --prefix external/vscode-common-python-lsp/typescript && npm --prefix external/vscode-common-python-lsp/typescript run build
  • ✅ Passed | Localization caller configuration assertions | python - <<'PY'
    import json
    from pathlib import Path
    import yaml
    load = lambda p: yaml.load(Path(p).read_text(), Loader=yaml.BaseLoader)
    pin = 'd1877b84bb10ff7caa6bd12ab4e75498ba760790'
    w = load('.github/workflows/localization.yml')
    assert w['on']['push']['branches'] == ['main'] and 'workflow_dispatch' in w['on']
    assert set(w['on']['push']['paths']) == {'package.json','package.nls.json','src/**/.ts','external/vscode-common-python-lsp','.gitmodules','.nvmrc','.github/workflows/localization.yml'}
    assert w['permissions'] == {'contents':'read'} and w['concurrency']['cancel-in-progress'] == 'false'
    j = w['jobs']['localization']
    assert j['if'] == "github.repository == 'microsoft/vscode-flake8'" and j['timeout-minutes'] == '20'
    assert len(j['steps']) == 1
    s = j['steps'][0]
    assert s['uses'] == 'microsoft/vscode-common-python-lsp/.github/actions/localization@' + pin
    assert s['with']['app-id'] == '${{ vars.PYTHON_LOCALIZATION_APP_ID }}'
    assert s['with']['private-key'] == '${{ secrets.PYTHON_LOCALIZATION_PRIVATE_KEY }}'
    assert s['with']['node-version'] == Path('.nvmrc').read_text().strip()
    assert s['with']['submodules'] == 'recursive'
    assert json.loads(s['with']['source-paths']) == ['src','external/vscode-common-python-lsp/typescript/src']
    for flavor in ['stable','pre-release']:
    p = load('build/azure-devdiv-pipeline.' + flavor + '.yml')
    r = {x['repository']:x for x in p['resources']['repositories']}
    assert r['translations'] == {'repository':'translations','type':'github','name':'microsoft/vscode-python-extensions-loc','ref':'refs/heads/dev','endpoint':'Github-Pylance'}
    assert r['localizationTools'] == {'repository':'localizationTools','type':'github','name':'microsoft/vscode-common-python-lsp','ref':pin,'endpoint':'Github-Pylance'}
    assert r['templates']['name'] == 'DevDiv/Pylance-Eng'
    assert p['extends']['template'] == 'azure-pipelines/extension/' + flavor + '.yml@templates'
    e = p['extends']['parameters']
    for key in ['pullLocalization','standardizedVersioning','ghCreateTag','ghCreateRelease','ghReleaseAddChangeLog']:
    assert e[key] == 'true'
    assert e['sourceAnalysisPool'] == 'VSEng-MicroBuildVSStable'
    assert e['buildSteps'] == '${{ parameters.buildSteps }}'
    assert e['publishExtension'] == '${{ parameters.publishExtension }}'
    assert e['customNPMRegistry'] == '$(AZURE_ARTIFACTS_FEED)'
    params = {x['name']:x for x in p['parameters']}
    assert params['publishExtension']['default'] == 'true'
    steps = [x['script'] for x in params['buildSteps']['default']]
    assert steps[-1] == 'npm run package'
    expected = 'python ./build/update_ext_version.py ' + ('--release ' if flavor == 'stable' else '') + '--for-publishing'
    assert steps[-2] == expected
    assert p['pr'] == 'none'
    if flavor == 'stable':
    assert p['trigger']['branches']['include'] == ['release/
    ']
    assert e['validationNotifyUsers'] == 'plseng@microsoft.com'
    else:
    assert p['trigger'] == 'none'
    assert p['schedules'][0]['cron'] == '0 10 * * 1-5'
    assert p['schedules'][0]['branches']['include'] == ['main']
    print('PASS:', flavor, 'caller resources, pull-only flag, triggers, version flags and publication settings')
    pkg = json.loads(Path('package.json').read_text())
    assert pkg['publisher'] + '.' + pkg['name'] == 'ms-python.flake8'
    assert pkg['l10n'] == './l10n' and Path('package.nls.json').is_file()
    assert '/package.nls..json' in Path('.gitignore').read_text().splitlines()
    assert '/l10n/' in Path('.gitignore').read_text().splitlines()
    assert not list(Path('.').glob('package.nls.
    .json')) and not Path('l10n').exists()
    print('PASS: GitHub workflow wiring, canonical extension ID, English source and generated-output policy')
    print('LIMITATION: external composite action and Azure templates not expanded or executed')
    PY
⚠️ Shared localization action and Azure release integration diagnostic output
External composite-action execution and Pylance-Eng template expansion were unavailable in this offline container. No English submission, real-handback import, readiness rejection, or release publication was executed. Static caller assertions do not establish these end-to-end behaviors.
⚠️ Dependency and test discovery diagnostic output
Discovery completed with exit 0: PROFILE=typescript; Linux; Python 3.13.18; Node v24.17.0; npm 11.13.0. Git metadata was unavailable inside the container. Interpreter: /opt/automation-venv/bin/python; pytest=True, yaml=True, freezegun=False. The optional build/test_update_ext_version.py suite imports freezegun, declared without a version constraint by noxfile.py:142. That suite was not run; Python setup was not attempted because the selected localization/configuration checks did not require it. Node dependencies were restored by the following bootstrap.
❌ Generated locale VSCE file-selection check diagnostic output
Error: Command failed: npm list --production --parseable --depth=99999 --loglevel=error
npm error code ELSPROBLEMS
npm error extraneous: @eslint/eslintrc@2.1.4 /workspace/node_modules/@eslint/eslintrc
npm error extraneous: vscode-languageclient@8.1.0 /workspace/node_modules/vscode-languageclient
npm error invalid: @vscode/python-environments@1.0.0 /workspace/external/vscode-common-python-lsp/typescript/node_modules/@vscode/python-environments
The check stopped before locale-inclusion assertions. Temporary fixtures were removed in finally. PR causality was not established.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rchiodo

Copy link
Copy Markdown
Contributor

Result: ⚠️ partially-verified

Verification details

Verification: Automated verification is best effort. The PR author remains responsible for testing.

Summary: Targeted YAML checks passed for the pinned action, pull-only release configuration, preserved build steps, feeds, version flags, and publication parameters. VSCE included all 28 generated locale files plus the English manifest, and changed YAML formatting passed. Existing versioning tests could not collect because the reviewer environment lacks the declared freezegun dependency; no PR-caused failure was demonstrated. No tests were added by this PR, and credentialed submission, remote Azure template execution, and translation-readiness enforcement remain unverified. **Reviewer dependency follow-up (nonblocking):** Suggest a separate PR adding `freezegun` to the official reviewer dependency declarations, using `noxfile.py` as the dependency/version evidence. Do not automatically create that PR. This is reviewer environment maintenance, not a required change to the reviewed PR.

Test runs: 5 passed, 1 failed, 1 not run

  • ❌ Failed | unrelated to this PR | Extension versioning tests | python -m pytest -q build/test_update_ext_version.py
  • ⚠️ Not run | Credentialed submission and release localization gates
  • ✅ Passed | Changed YAML formatting | node ./node_modules/prettier/bin/prettier.cjs --check .github/workflows/localization.yml build/azure-devdiv-pipeline.stable.yml build/azure-devdiv-pipeline.pre-release.yml
  • ✅ Passed | Environment and dependency discovery | printf 'PROFILE=%s\n' "$AUTOMATION_SANDBOX_PROFILE"; uname -s; git status --short; git log -3 --oneline; git diff HEAD~1 HEAD --stat; printf '\nTool availability\n'; command -v python; python --version; command -v node; node --version; command -v npm; npm --version; printf '\nDependencies and tests\n'; test ! -d node_modules || printf 'node_modules exists\n'; ls -la external/vscode-common-python-lsp; git submodule status; git ls-files 'test' 'localization' 'requirements' 'lock'; python -m pip show pytest PyYAML; printf '\nDeclared Python requirements\n'; cat requirements.in requirements.txt; printf '\nShared npm metadata\n'; if test -f external/vscode-common-python-lsp/typescript/package.json; then cat external/vscode-common-python-lsp/typescript/package.json; fi
  • ✅ Passed | Offline Node dependency preparation | npm ci --offline --ignore-scripts; root_status=$?; npm --prefix external/vscode-common-python-lsp/typescript ci --offline --ignore-scripts; shared_status=$?; printf '\nOffline dependency preparation: root=%s shared=%s\n' "$root_status" "$shared_status"; test "$root_status" -eq 0 && test "$shared_status" -eq 0
  • ✅ Passed | Localization YAML contract checks | python - <<'PY'
    import json
    from pathlib import Path
    import yaml

root = Path('.')
pin = 'd1877b84bb10ff7caa6bd12ab4e75498ba760790'
w = yaml.load((root / '.github/workflows/localization.yml').read_text(), Loader=yaml.BaseLoader)
assert w['on']['push']['branches'] == ['main']
assert set(w['on']['push']['paths']) == {'package.json', 'package.nls.json', 'src/**/.ts', 'external/vscode-common-python-lsp', '.gitmodules', '.nvmrc', '.github/workflows/localization.yml'}
assert 'workflow_dispatch' in w['on']
assert w['permissions'] == {'contents': 'read'}
assert w['concurrency'] == {'group': 'localization-${{ github.ref }}', 'cancel-in-progress': 'false'}
job = w['jobs']['localization']
assert job['if'] == "github.repository == 'microsoft/vscode-flake8'"
assert job['runs-on'] == 'ubuntu-latest' and job['timeout-minutes'] == '20'
assert len(job['steps']) == 1
step = job['steps'][0]
assert step['uses'] == 'microsoft/vscode-common-python-lsp/.github/actions/localization@' + pin
assert step['with'] == {'app-id': '${{ vars.PYTHON_LOCALIZATION_APP_ID }}', 'private-key': '${{ secrets.PYTHON_LOCALIZATION_PRIVATE_KEY }}', 'submodules': 'recursive', 'source-paths': '["src", "external/vscode-common-python-lsp/typescript/src"]', 'node-version': '24.17.0'}
print('PASS: GitHub submission triggers, permissions, concurrency, pinned action and extraction inputs')
for kind in ['stable', 'pre-release']:
p = root / f'build/azure-devdiv-pipeline.{kind}.yml'
y = yaml.safe_load(p.read_text())
repos = {r['repository']: r for r in y['resources']['repositories']}
assert repos['templates'] == {'repository': 'templates', 'type': 'git', 'name': 'DevDiv/Pylance-Eng', 'ref': 'refs/heads/main'}
assert repos['translations'] == {'repository': 'translations', 'type': 'github', 'name': 'microsoft/vscode-python-extensions-loc', 'ref': 'refs/heads/dev', 'endpoint': 'Github-Pylance'}
assert repos['localizationTools'] == {'repository': 'localizationTools', 'type': 'github', 'name': 'microsoft/vscode-common-python-lsp', 'ref': pin, 'endpoint': 'Github-Pylance'}
params = {v['name']: v for v in y['parameters']}
assert params['publishExtension']['type'] == 'boolean' and params['publishExtension']['default'] is True
scripts = [s['script'] for s in params['buildSteps']['default']]
version_flags = '--release --for-publishing' if kind == 'stable' else '--for-publishing'
assert scripts == ['git submodule update --init --recursive', 'npm ci', 'python -m pip install -U pip', 'python -m pip install wheel', 'python -m pip install nox', 'python -m nox --session install_bundled_libs', f'python ./build/update_ext_version.py {version_flags}', 'npm run package']
assert y['extends']['template'] == f'azure-pipelines/extension/{kind}.yml@templates'
expected = {'buildSteps': '${{ parameters.buildSteps }}', 'standardizedVersioning': True, 'customNPMRegistry': '$(AZURE_ARTIFACTS_FEED)', 'publishExtension': '${{ parameters.publishExtension }}', 'teamName': '$(TeamName)', 'ghCreateTag': True, 'ghCreateRelease': True, 'ghReleaseAddChangeLog': True, 'sourceAnalysisPool': 'VSEng-MicroBuildVSStable', 'pullLocalization': True}
if kind == 'stable':
expected['validationNotifyUsers'] = 'plseng@microsoft.com'
assert y['trigger'] == {'branches': {'include': ['release/
']}}
else:
assert y['trigger'] == 'none'
assert y['schedules'] == [{'cron': '0 10 * * 1-5', 'displayName': 'Nightly Pre-Release Schedule', 'always': False, 'branches': {'include': ['main']}}]
assert y['pr'] == 'none'
assert y['extends']['parameters'] == expected
assert {v['name']: v['value'] for v in y['variables']} == {'TeamName': 'VSCode-flake8', 'VsixName': 'flake8.vsix', 'AZURE_ARTIFACTS_FEED': 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/&#x27;}
print(f'PASS: {kind} pull-only wiring, resource pins, build steps, feed, version flags and publication/validation parameters')
package = json.loads((root / 'package.json').read_text())
assert package['publisher'] + '.' + package['name'] == 'ms-python.flake8'
assert package['l10n'] == './l10n'
assert (root / 'package.nls.json').is_file()
assert not list(root.glob('package.nls..json'))
assert not list((root / 'l10n').glob('
.json'))
assert '/package.nls..json' in (root / '.gitignore').read_text().splitlines()
assert '/l10n/' in (root / '.gitignore').read_text().splitlines()
print('PASS: canonical extension ID, runtime metadata, tracked English and generated-snapshot removal')
shared = root / 'external/vscode-common-python-lsp'
localization_files = [str(p) for p in shared.rglob('
') if p.is_file() and 'node_modules' not in p.parts and ('localization' in str(p).lower() or 'l10n' in p.name.lower())]
print('Restored submodule localization files:', localization_files)
print('LIMITATION: pinned action/tool checkout and remote DevDiv template execution are not present in this offline environment; no submission or handback-readiness validation was executed.')
PY

  • ✅ Passed | VSCE generated localization file selection | node <<'JS'
    const fs = require('fs');
    const assert = require('assert/strict');
    const { execFileSync } = require('child_process');
    const locales = ['cs', 'de', 'es', 'fr', 'it', 'ja', 'ko', 'pl', 'pt-br', 'qps-ploc', 'ru', 'tr', 'zh-cn', 'zh-tw'];
    const expected = locales.flatMap(locale => [package.nls.${locale}.json, l10n/bundle.l10n.${locale}.json]);
    const hadL10n = fs.existsSync('l10n');
    assert(expected.every(file => !fs.existsSync(file)), 'Fixtures must not overwrite existing translations');
    try {
    fs.mkdirSync('l10n', { recursive: true });
    for (const file of expected) fs.writeFileSync(file, JSON.stringify({ 'fixture': 'translated fixture' }));
    const output = execFileSync(process.execPath, ['./node_modules/@vscode/vsce/vsce', 'ls', '--no-dependencies'], { encoding: 'utf8' });
    const files = new Set(output.trim().split(/\r?\n/));
    for (const file of expected) assert(files.has(file), VSCE omitted generated translation: ${file});
    assert(files.has('package.nls.json'), 'VSCE omitted English manifest');
    console.log(PASS: actual VSCE file selection includes all ${expected.length} generated manifest/runtime files across ${locales.length} locales, plus English.);
    console.log('Fixture tables test inclusion only, not translation readiness.');
    } finally {
    for (const file of expected) if (fs.existsSync(file)) fs.unlinkSync(file);
    if (!hadL10n && fs.existsSync('l10n')) fs.rmdirSync('l10n');
    }
    JS
❌ Extension versioning tests diagnostic output
Selected interpreter: /opt/automation-venv/bin/python, Python 3.13.18.
ERROR collecting build/test_update_ext_version.py
build/test_update_ext_version.py:6: import freezegun
ModuleNotFoundError: No module named 'freezegun'
1 error in 0.37s
noxfile.py:142 explicitly declares session.install("freezegun") without a version constraint. The single dependency-bootstrap allowance had already been used for offline Node setup; no second bootstrap was attempted.
⚠️ Credentialed submission and release localization gates diagnostic output
The offline environment does not contain the pinned shared localization action/tool checkout or remote DevDiv templates. Credentialed English submission, Azure expansion/execution, actual handback import, and per-table readiness rejection were not exercised.

@bschnurr
Bill Schnurr (bschnurr) merged commit 3437eb3 into main Nov 5, 2026
19 of 21 checks passed
@bschnurr
Bill Schnurr (bschnurr) deleted the users/bschnurr/shared-localization branch November 5, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

debt review-auto:approved Automated review: no blocking findings (approval posted).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants