Skip to content

Add GitHub Action to automate Monaco Editor updates#46692

Open
crutkas wants to merge 2 commits intomainfrom
crutkas/automate-monaco-editor-update
Open

Add GitHub Action to automate Monaco Editor updates#46692
crutkas wants to merge 2 commits intomainfrom
crutkas/automate-monaco-editor-update

Conversation

@crutkas
Copy link
Copy Markdown
Member

@crutkas crutkas commented Apr 1, 2026

Summary of the Pull Request

Adds a GitHub Action workflow that automates the Monaco Editor update process currently documented in FilePreviewCommon.md.

PR Checklist

  • Communication: I've discussed this with core contributors already.
  • Tests: Added/updated and all pass
  • Dev docs: Added/updated

Detailed Description of the Pull Request / Additional comments

What it does

When triggered (manually via workflow_dispatch), the action:

  1. Downloads the latest (or specified) Monaco Editor version from npm
  2. Replaces src/Monaco/monacoSRC/min/ with the new version
  3. Uses Puppeteer (headless browser) to run the existing generateLanguagesJson.html and regenerate monaco_languages.json — identical code path to the manual process
  4. Runs ~60 validation assertions across 7 test groups to ensure correctness
  5. Creates a PR with the changes

A commented-out weekly cron schedule is included, ready to enable.

Files added/modified

File Purpose
.github/workflows/update-monaco-editor.yml Workflow definition (workflow_dispatch trigger)
.github/scripts/update-monaco-editor.ps1 Orchestration: npm download, replace min/, invoke Puppeteer
.github/scripts/generate-monaco-languages.js Puppeteer script: local HTTP server + headless Chromium running generateLanguagesJson.html
.github/scripts/tests/validate-monaco-update.tests.ps1 Validation: directory structure, core files, JSON validity, 32 built-in languages, 3 custom languages, 7 extension mappings, version consistency
.github/actions/spell-check/allow/code.txt Added Monaco/Puppeteer terms (cdp, crdownload, networkidle) and Monaco language identifiers (kotlin, ksh, pde) to the spell-check allow list

Design decisions

  • Puppeteer for JSON generation (not raw Node.js) because Monaco's editor.main.js requires browser globals (document, window, etc.)
  • PowerShell for orchestration & tests (available on ubuntu-latest runners)
  • Only monacoSRC/min/ is replaced; custom languages, monacoSpecialLanguages.js, index.html etc. are preserved

Validation Steps Performed

  • All 4 files pass syntax validation (YAML structure, PowerShell parser, Node.js --check)
  • Code review performed covering security (path traversal hardening), race conditions, and error handling
  • Spell-check allow list updated to resolve CI spelling alerts for domain-specific terms used in the scripts

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>
@crutkas crutkas requested a review from a team as a code owner April 1, 2026 04:31
@github-actions

This comment has been minimized.

@crutkas
Copy link
Copy Markdown
Member Author

crutkas commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 1, 2026

@copilot fix spelling https://github.com/microsoft/PowerToys/blob/main/.github/actions/spell-check/allow/code.txt

Added all flagged terms to .github/actions/spell-check/allow/code.txt in commit 4edef48:

  • cdp / Cdp — Chrome DevTools Protocol (Puppeteer)
  • crdownload — Chrome partial download file extension
  • networkidle — Puppeteer network idle state
  • kotlin, ksh, pde — Monaco Editor language identifiers

@niels9001
Copy link
Copy Markdown
Collaborator

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 Editor workflow (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.html to regenerate monaco_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.

Comment on lines +57 to +61
$version = '${{ inputs.version }}'
if ([string]::IsNullOrWhiteSpace($version)) {
$version = 'latest'
}
$output = & ./.github/scripts/update-monaco-editor.ps1 -Version $version -RepoRoot $env:GITHUB_WORKSPACE
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +149 to +159
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)
) {
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +112 to +127
} 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 });
}
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +135 to +162
# 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
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +8
# 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
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants