Add GitHub Action to automate Monaco Editor updates#46692
Add GitHub Action to automate Monaco Editor updates#46692
Conversation
Add a workflow_dispatch-triggered GitHub Action that automates the Monaco Editor update process documented in FilePreviewCommon.md: - Downloads latest (or specified) Monaco version from npm - Replaces monacoSRC/min/ with the new version - Uses Puppeteer to run generateLanguagesJson.html and regenerate monaco_languages.json (identical code path to manual process) - Runs ~60 validation assertions across 7 test groups - Creates a PR with the changes via peter-evans/create-pull-request Files added: - .github/workflows/update-monaco-editor.yml - .github/scripts/update-monaco-editor.ps1 - .github/scripts/generate-monaco-languages.js - .github/scripts/tests/validate-monaco-update.tests.ps1 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/755156df-f3b9-43b8-b696-94b18a48c257 Co-authored-by: crutkas <1462282+crutkas@users.noreply.github.com>
Added all flagged terms to
|
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
There was a problem hiding this comment.
Pull request overview
Adds a GitHub Actions-based automation to update the vendored Monaco Editor assets under src/Monaco/, regenerate monaco_languages.json using a headless browser, validate the update, and open a PR with the results.
Changes:
- Introduces
Update Monaco Editorworkflow (workflow_dispatch) that runs the update + validation and opens an automated PR. - Adds PowerShell orchestration + PowerShell validation tests for Monaco file structure, language registrations, and version consistency.
- Adds a Puppeteer-based generator that runs the existing
generateLanguagesJson.htmlto regeneratemonaco_languages.json.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/update-monaco-editor.yml |
New workflow to run the update pipeline and create a PR. |
.github/scripts/update-monaco-editor.ps1 |
Orchestrates npm download/copy and invokes JSON generation. |
.github/scripts/generate-monaco-languages.js |
Starts a local static server and uses Puppeteer to run the HTML generator and capture the downloaded JSON. |
.github/scripts/tests/validate-monaco-update.tests.ps1 |
Adds validation assertions to catch broken Monaco updates early. |
.github/actions/spell-check/allow/code.txt |
Allows Monaco/Puppeteer-specific terms and identifiers used by the scripts. |
| $version = '${{ inputs.version }}' | ||
| if ([string]::IsNullOrWhiteSpace($version)) { | ||
| $version = 'latest' | ||
| } | ||
| $output = & ./.github/scripts/update-monaco-editor.ps1 -Version $version -RepoRoot $env:GITHUB_WORKSPACE |
There was a problem hiding this comment.
The PowerShell step interpolates ${{ inputs.version }} inside single quotes. If the workflow input contains a ', this will break the script (and can enable PowerShell injection). Safer pattern is to pass the value via env: (and read $env:...) or pass it as a quoted argument to the script without embedding it in a PowerShell string literal.
| const server = http.createServer((req, res) => { | ||
| const urlPath = decodeURIComponent(req.url.split("?")[0]); | ||
| const filePath = path.join(rootDir, urlPath); | ||
|
|
||
| // Security: ensure we don't serve files outside rootDir | ||
| const resolvedRoot = path.resolve(rootDir); | ||
| const resolvedPath = path.resolve(rootDir, urlPath); | ||
| if ( | ||
| resolvedPath !== resolvedRoot && | ||
| !resolvedPath.startsWith(resolvedRoot + path.sep) | ||
| ) { |
There was a problem hiding this comment.
The static file server treats req.url (which starts with /) as a path segment. With path.join(rootDir, urlPath) / path.resolve(rootDir, urlPath), a leading / makes the path absolute on Linux and bypasses rootDir, so the security check will return 403 for normal requests like /generateLanguagesJson.html. Strip the leading slash (and normalize) before joining/resolving to ensure files under the Monaco directory can be served.
| } catch (err) { | ||
| console.error("Failed to generate monaco_languages.json:", err.message); | ||
| process.exit(1); | ||
| } finally { | ||
| if (browser) { | ||
| await browser.close().catch(() => {}); | ||
| } | ||
| if (server) { | ||
| server.close(); | ||
| } | ||
| // Clean up temp download directory AFTER browser is closed | ||
| const downloadDir = path.join(absMonacoDir, ".monaco-download-tmp"); | ||
| if (fs.existsSync(downloadDir)) { | ||
| fs.rmSync(downloadDir, { recursive: true, force: true }); | ||
| } | ||
| } |
There was a problem hiding this comment.
process.exit(1) inside the catch will prevent the finally block from running, so the browser/server may not close and .monaco-download-tmp may not be cleaned up on failures. Prefer setting process.exitCode = 1 and returning, or rethrowing the error, so cleanup in finally always executes.
| # Set NODE_PATH so the generate script can find puppeteer from the temp dir | ||
| $env:NODE_PATH = Join-Path $tempDir "node_modules" | ||
| node $generateScript $monacoDir | ||
|
|
||
| if ($LASTEXITCODE -ne 0) { | ||
| throw "Failed to generate monaco_languages.json (exit code: $LASTEXITCODE)" | ||
| } | ||
|
|
||
| if (-not (Test-Path $languagesJsonPath)) { | ||
| throw "monaco_languages.json was not generated at: $languagesJsonPath" | ||
| } | ||
|
|
||
| Write-Host "`n=== Monaco Editor update complete ===" | ||
| Write-Host "Updated to version: $newVersion" | ||
| Write-Host "Languages JSON: $languagesJsonPath" | ||
|
|
||
| # Output the new version for the workflow to use | ||
| Write-Output "MONACO_VERSION=$newVersion" | ||
| } | ||
| finally { | ||
| # Clean up temp directory | ||
| if (Test-Path $tempDir) { | ||
| Remove-Item -Recurse -Force $tempDir -ErrorAction SilentlyContinue | ||
| } | ||
|
|
||
| # Remove NODE_PATH override | ||
| Remove-Item Env:\NODE_PATH -ErrorAction SilentlyContinue | ||
| } |
There was a problem hiding this comment.
The script unconditionally deletes Env:\NODE_PATH in the finally block. If the caller already had NODE_PATH set, this will remove it rather than restoring the previous value. Consider saving the prior value and restoring it (or scoping the change to the node invocation) to avoid surprising side effects when running locally.
| # Automates the Monaco Editor update process described in | ||
| # doc/devdocs/common/FilePreviewCommon.md: | ||
| # 1. Downloads the latest (or specified) Monaco Editor from npm | ||
| # 2. Replaces src/Monaco/monacoSRC/min with the new version | ||
| # 3. Regenerates monaco_languages.json via Puppeteer (headless browser) | ||
| # 4. Runs validation tests |
There was a problem hiding this comment.
The workflow references doc/devdocs/common/FilePreviewCommon.md as the source of truth for the update process, but that doc currently only describes the manual npm/copy/HTML steps and doesn’t mention this new GitHub Action automation. If the PR intends to update dev docs (per PR description/checklist), consider updating the doc to include the workflow entry point and expected usage.
Summary of the Pull Request
Adds a GitHub Action workflow that automates the Monaco Editor update process currently documented in FilePreviewCommon.md.
PR Checklist
Detailed Description of the Pull Request / Additional comments
What it does
When triggered (manually via
workflow_dispatch), the action:src/Monaco/monacoSRC/min/with the new versiongenerateLanguagesJson.htmland regeneratemonaco_languages.json— identical code path to the manual processA commented-out weekly cron schedule is included, ready to enable.
Files added/modified
.github/workflows/update-monaco-editor.ymlworkflow_dispatchtrigger).github/scripts/update-monaco-editor.ps1.github/scripts/generate-monaco-languages.jsgenerateLanguagesJson.html.github/scripts/tests/validate-monaco-update.tests.ps1.github/actions/spell-check/allow/code.txtcdp,crdownload,networkidle) and Monaco language identifiers (kotlin,ksh,pde) to the spell-check allow listDesign decisions
editor.main.jsrequires browser globals (document,window, etc.)ubuntu-latestrunners)monacoSRC/min/is replaced; custom languages,monacoSpecialLanguages.js,index.htmletc. are preservedValidation Steps Performed
--check)