Files
WSL/tools/devops/create-change.py
Ben Hillis 1d6133f96f Localize ADMX Group Policy strings via Touchdown (#40515)
Add the WSL ADMX policy strings to the nightly Touchdown localization
pipeline so translated .adml files are produced for every locale already
covered by Resources.resw. Translated files land at
intune/<locale>/WSL.adml, matching the Group Policy on-disk layout (the
language-neutral WSL.admx stays at intune/WSL.admx).

Changes:

* intune/en-US/WSL.adml: new baseline ADML file. Each translatable
  string is annotated with {Locked="..."} XML comments so translators
  preserve product names (Windows Subsystem for Linux, WSL, WSL1, WSL2,
  Store WSL), .wslconfig keys (wsl2.kernel, wsl2.systemDistro,
  wsl2.kernelCommandLine, wsl2.nestedVirtualization,
  wsl2.kernelDebugPort, wsl2.networkingmode, wsl2.firewall), command
  names (wsl.exe, debug-shell, mount), networking-mode enum values
  (None, NAT, Mirrored, VirtioProxy), and the policy state literal
  Disabled.

* .pipelines/wsl-build-nightly-localization.yml: add intune/en-US/WSL.adml
  to the trigger paths and to the TouchdownBuildTask resourceFilePath
  block. The combined ';O:intune\' mapping puts translated files at
  intune/<locale>/WSL.adml.

* tools/devops/validate-localization.py: add validate_adml() which
  enforces the same locked-token invariant as .resw (every {Locked="X"}
  token must appear verbatim in its target string value), validates
  that translated locales have the same string ids as the en-US
  baseline, and is parameterized by the ADML folder name. The argv
  shortcut used in CI to avoid a pip install dependency on click now
  accepts both 3 args (back-compat) and 4 args (with explicit ADML
  folder).

* .pipelines/build-stage.yml: pass 'intune' explicitly to
  validate-localization.py so the validation invocation reads
  'localization/strings en-US intune' -- both folders visible at the
  call site, consistent with the rest of the script's parameterization.

* tools/devops/create-change.py: include untracked files in the
  'Changed files:' report and stage them with 'git add -A' (via
  repo.git.add(A=True)) before commit. Without this, Touchdown's
  first-time ADML outputs land as untracked files in newly created
  intune/<locale>/ directories and are silently dropped by 'git commit
  -a', which only stages modifications and deletions of already-tracked
  files. Confirmed against ADO build 146781281: Touchdown returned
  WSL.adml translations for 20 locales and saved them on disk, but the
  resulting auto-PR (#40511) contained only the resw modifications.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-13 12:40:43 -07:00

67 lines
2.0 KiB
Python

import click
import requests
import json
from git import Repo
COMMITTER_EMAIL = 'noreply@microsoft.com'
REPO = 'microsoft/wsl'
@click.command()
@click.argument('repo_path', required=True)
@click.argument('token', required=True)
@click.argument('committer', required=True)
@click.argument('message', required=True)
@click.argument('branch', required=True)
@click.argument('target_branch', required=True)
@click.option('--debug', default=False, is_flag=True)
def main(repo_path: str, token: str, committer: str, message: str, branch: str, target_branch: str, debug: bool):
try:
repo = Repo(repo_path)
modified_files = [e.a_path for e in repo.index.diff(None)]
untracked_files = list(repo.untracked_files)
changed_files = modified_files + untracked_files
if not changed_files:
print('No files changed, skipping')
return
print(f'Changed files: {",".join(changed_files)}')
repo.create_head(branch).checkout()
with repo.config_writer() as config:
config.set_value("user", "email", COMMITTER_EMAIL)
config.set_value("user", "name", committer)
# 'git add -A' so newly created files in new directories are staged too.
repo.git.add(A=True)
repo.git.commit(m=message)
repo.git.push('origin', branch)
headers = {'Accept': 'application/vnd.github+json', 'Authorization': 'Bearer ' + token}
body = {
'title': message,
'description': 'Automated change',
'head': branch,
'base': target_branch
}
response = requests.post(f'https://api.github.com/repos/{REPO}/pulls', headers=headers, data=json.dumps(body), timeout=30)
response.raise_for_status()
print(f'Created pull request: {response.json()["html_url"]}')
except:
if debug:
import pdb
import traceback
traceback.print_exc()
pdb.post_mortem()
raise
if __name__ == '__main__':
main()