fix(code-server,vscode-web): parse JSONC in extensions.json with sed#798
fix(code-server,vscode-web): parse JSONC in extensions.json with sed#798rodmk wants to merge 3 commits into
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves auto-installation of VS Code/code-server extensions by attempting to preprocess JSONC workspace/extension files (comments + trailing commas) before feeding them to jq, and updates the module README version pins accordingly.
Changes:
- Add a
strip_jsonc_for_extensions()helper in bothvscode-webandcode-servermodules, then use it when extracting extension recommendations viajq. - Make
jqparsing tolerant of missing recommendations arrays by defaulting to[]. - Bump module versions referenced in READMEs and add a
typosdictionary entry to avoid false positives.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| registry/coder/modules/vscode-web/run.sh | Adds JSONC preprocessing helper and uses it for workspace/extensions parsing before jq. |
| registry/coder/modules/vscode-web/README.md | Updates example module version pins to 1.5.1. |
| registry/coder/modules/code-server/run.sh | Adds JSONC preprocessing helper and uses it for .vscode/extensions.json parsing before jq. |
| registry/coder/modules/code-server/README.md | Updates example module version pins to 1.4.4. |
| .github/typos.toml | Adds ba to the allowed word list (sed label pattern). |
DevelopmentCats
left a comment
There was a problem hiding this comment.
I agree with you that the Copilot Comments were wrong.
This LGTM though thanks for the fix and quick response!! 😸
|
@rodmk can you resolve the conflicts. |
|
@35C4n0r Whoa, I didn't realize this hadn't been merged yet, it was approved months ago 😅 I'll resolve the conflicts promptly. |
# Conflicts: # registry/coder/modules/code-server/README.md # registry/coder/modules/code-server/run.sh # registry/coder/modules/vscode-web/README.md
|
Resolved the conflicts against the latest Validation: 🤖 Generated with Claude Code using Claude Opus 4.8 |
35C4n0r
left a comment
There was a problem hiding this comment.
Two P1 bugs found in the strip_jsonc_for_extensions shell function introduced by this PR. Both are verified with live tests. Each inline comment above includes a suggested fix or a plain diff.
Summary of required changes:
| # | File | Issue | Severity |
|---|---|---|---|
| 1 | code-server/run.sh lines 119–128 |
Line-comment pass runs before block-comment slurp — corrupts URLs in comments and settings; single-line files pass through unprocessed | P1 |
| 2 | vscode-web/run.sh lines 153–162 |
Same stage-ordering bug | P1 |
| 3 | code-server/run.sh line 137 |
RECOMMENDATIONS_QUERY not null-safe (vscode-web was fixed, code-server was not) |
P1 |
Generated by Coder Agents on behalf of @35C4n0r.
| # Strip JSONC features (block/line comments, trailing commas) for jq parsing | ||
| strip_jsonc_for_extensions() { | ||
| sed 's|//.*||g' "$1" | sed -E ' | ||
| :a | ||
| N | ||
| $!ba | ||
| s#/[*]([^*]|[*]+[^*/])*[*]+/##g | ||
| s/,[^]}"]*([]}])/\1/g | ||
| ' | ||
| } |
There was a problem hiding this comment.
P1 — sed 's|//.*||g' runs before block-comment removal; two silent failures result.
-
Inline block comment containing
://— a line like/* https://open-vsx.org */has//open-vsx.org */stripped by stage 1, leaving/* https:in the buffer. Stage 2 finds no closing*/and passes the fragment through.jqthen fails withparse error: Invalid numeric literal— zero extensions installed, nothing printed to the user. Verified locally. -
URL-valued settings in
.code-workspacefiles —"http.proxy": "https://proxy.corp:8080"becomes"http.proxy": "https:(unclosed string literal).jqexits withInvalid string: control characters from U+0000 through U+001F must be escaped. Same silent failure. Verified locally.
The suggestion merges into a single sed -E pass with block comments removed first. s#//[^\n]*##g then handles line comments on the slurped buffer — [^\n]* stops at each embedded newline, so it is equivalent to per-line stripping without a separate process. This fixes failure #1. Failure #2 (URL string values) is a fundamental limit of the regex approach — only a JSONC-aware parser can reliably distinguish // inside a string from // in a comment.
| # Strip JSONC features (block/line comments, trailing commas) for jq parsing | |
| strip_jsonc_for_extensions() { | |
| sed 's|//.*||g' "$1" | sed -E ' | |
| :a | |
| N | |
| $!ba | |
| s#/[*]([^*]|[*]+[^*/])*[*]+/##g | |
| s/,[^]}"]*([]}])/\1/g | |
| ' | |
| } | |
| # Strip JSONC features (block/line comments, trailing commas) for jq parsing | |
| strip_jsonc_for_extensions() { | |
| sed -E ' | |
| :a | |
| N | |
| $!ba | |
| s#/[*]([^*]|[*]+[^*/])*[*]+/##g | |
| s#//[^\n]*##g | |
| s/,[^]}"]*([]}])/\1/g | |
| ' "$1" | |
| } |
Generated by Coder Agents on behalf of @35C4n0r.
| # Strip JSONC features (block/line comments, trailing commas) for jq parsing | ||
| strip_jsonc_for_extensions() { | ||
| sed 's|//.*||g' "$1" | sed -E ' | ||
| :a | ||
| N | ||
| $!ba | ||
| s#/[*]([^*]|[*]+[^*/])*[*]+/##g | ||
| s/,[^]}"]*([]}])/\1/g | ||
| ' | ||
| } |
There was a problem hiding this comment.
P1 — URL corruption / single-line file: stage ordering is wrong
The current implementation pipes through a line-comment stripper before slurping into a multi-line buffer:
sed 's|//.*||g' "$1" | sed -E '
:a
N
$!ba
s#/[*]([^*]|[*]+[^*/])*[*]+/##g
...Two verified failure modes:
-
URL inside a block comment —
/* https://open-vsx.org */: stage 1 strips://open-vsx.org */, leaving/* https:unclosed. Stage 2's block-comment regex can't match, jq receives garbage and exits non-zero — zero extensions installed, no error surfaced. -
URL-valued setting —
"http.proxy": "https://proxy.corp": stage 1 strips://proxy.corp", producing an unclosed string literal. jq exits 5, zero extensions installed silently. -
Single-line input —
:a;N;$!baexits immediately at EOF without running any substitution (GNU sed behaviour on last-lineN). A one-line{"recommendations":["ext1",]}is passed through unchanged and jq fails on the trailing comma.
Fix: slurp the whole file first, then strip block comments, then line comments — in a single sed invocation:
| # Strip JSONC features (block/line comments, trailing commas) for jq parsing | |
| strip_jsonc_for_extensions() { | |
| sed 's|//.*||g' "$1" | sed -E ' | |
| :a | |
| N | |
| $!ba | |
| s#/[*]([^*]|[*]+[^*/])*[*]+/##g | |
| s/,[^]}"]*([]}])/\1/g | |
| ' | |
| } | |
| strip_jsonc_for_extensions() { | |
| sed -E ' | |
| :a | |
| N | |
| $!ba | |
| s#/[*]([^*]|[*]+[^*/])*[*]+/##g | |
| s#//[^\n]*##g | |
| s/,[^]}"]*([]}])/\1/g | |
| ' "$1" | |
| } |
s#//[^\n]*##g is safe on the slurped buffer because [^\n]* stops at each embedded newline, so it only strips to end-of-line.
Generated by Coder Agents on behalf of @35C4n0r.
| # Use sed to remove single-line comments before parsing with jq | ||
| extensions=$(sed 's|//.*||g' "$RECOMMENDATIONS_FILE" | jq -r "$RECOMMENDATIONS_QUERY") | ||
| # Strip JSONC comments and trailing commas before parsing with jq | ||
| extensions=$(strip_jsonc_for_extensions "$RECOMMENDATIONS_FILE" | jq -r "$RECOMMENDATIONS_QUERY") |
There was a problem hiding this comment.
P1 — code-server null-safety gap in RECOMMENDATIONS_QUERY
This PR upgrades vscode-web to use a null-safe query:
# vscode-web/run.sh (this PR)
RECOMMENDATIONS_QUERY='(.recommendations // [])[]'but code-server was not updated in parallel. The query at line 137 of code-server/run.sh is still:
RECOMMENDATIONS_QUERY=".recommendations[]"A valid extensions.json with no recommendations key at all (permitted by the VS Code schema) causes jq to emit null | .[] which exits non-zero — silently discarding all extension installations on code-server while vscode-web handles the same file cleanly.
Fix (line 137, not in the diff hunk above — apply manually):
- RECOMMENDATIONS_QUERY=".recommendations[]"
+ RECOMMENDATIONS_QUERY='(.recommendations // [])[]'Note the single-quote change: the expression contains no shell variables, so single quotes are correct and prevent accidental expansion.
Generated by Coder Agents on behalf of @35C4n0r.
Fixes #708. Supersedes #707.
Description
auto_install_extensionsfails to parse.vscode/extensions.jsonfiles containing valid JSONC (block comments, trailing commas). VS Code officially supports these via its built-in JSONC parser with default options.This PR replaces the existing
sed | jqpipeline with a portable two-stagesed | sed | jqpipeline that handles all JSONC features — no new dependencies, no GNU-only extensions.Type of Change
Module Information
Path:
registry/coder/modules/code-serverandregistry/coder/modules/vscode-webNew version: code-server
1.4.4, vscode-web1.5.1Breaking change: No
Problem
The
sed 's|//.*||g' | jqpipeline fails on block comments and trailing commas:registry/registry/coder/modules/code-server/run.sh
Line 133 in f1748c8
registry/registry/coder/modules/vscode-web/run.sh
Line 161 in f1748c8
registry/registry/coder/modules/vscode-web/run.sh
Line 173 in f1748c8
Fix
Extracts a
strip_jsonchelper function (defined once per script) that uses a two-stagesed | sedpipeline:// ...) — a separate line-by-linesedpass where.*naturally stops at the end of each input line:a;N;$!ba) — reads the entire file into sed's pattern space for multi-line matching/* ... */) — ERE regex handles nested stars, multi-line comments]or}—[^]}"]*matches only whitespace/newlines between a trailing comma and its closing bracketThis is safe for
extensions.jsonbecause extension IDs are restricted to[a-z0-9\-.]by vsce'snameRegex, so comment markers (//,/*) cannot appear inside string values, and the trailing-comma regex[^]}"]*will only ever match whitespace between a comma and its closing bracket. The function is not a general-purpose JSONC parser, but a full parser would be overengineered for this use case.Cross-platform compatibility
The original
sed 's|//.*||g'pipeline only handled line comments. A natural fix would usesed -zto slurp the file for multi-line block comment matching, but-zis a GNU sed extension that does not work on macOS (BSD sed). Since Coder workspaces support macOS (thevscode-webmodule acceptsplatform = "darwin"), the fix must be portable.Line comment stripping is done in a separate first-stage
sed(before slurping) because after slurping with:a;N;$!ba,.matches\nin the pattern space, so.*would greedily eat across lines. A single-sed approach using[^\n]*instead of.*works on GNU sed but fails on BSD sed (macOS), where\ninside a bracket expression is interpreted as literal backslash +nper POSIX — causing silent corruption of extension IDs containing the lettern.This implementation uses only:
sedandsed -E— POSIX-standard flags, supported by both GNU and BSD sed:a;N;$!ba— standard sed idiom for slurping, works on both platforms\+,\s,[^\n], or other GNU-only regex extensionsAlternatives considered
sed -z-zis a GNU sed extension — breaks on macOS BSD sedsed -Ewith[^\n]*\nin bracket expressions is not portable — BSD sed treats it as literal\+n, silently corrupting outputperl -0777 -pecommand -vguardsed | sed(this PR)Testing & Validation
bun test) — 490 pass, 4 pre-existing failures unrelated to this PRbun fmt)version-bump.shterraform applywithauto_install_extensions = true+ JSONCextensions.json(line comments, block comments, trailing commas) correctly extracts and installs all extensions on bothubuntu:22.04andalpine:latestTested the sed+jq pipeline against 19 cases:
Standalone test script (requires jq)
Related Issues
Generated with Claude Code using Claude Opus 4.6