From b94e2f8a28b7ce3c77d78e14565e69657fdc4efc Mon Sep 17 00:00:00 2001 From: Shun FUJIOKA Date: Thu, 22 Oct 2026 01:42:31 +0900 Subject: [PATCH 1/3] Upgrade unbash to 6.0.1 It decodes ANSI-C byte escapes as UTF-8. The adapter does not read decoded values, so nothing changes in the explanations. Co-Authored-By: Claude Sonnet 5.5 --- docs/design/syntax-tree.md | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/design/syntax-tree.md b/docs/design/syntax-tree.md index 597a2667..15958b86 100644 --- a/docs/design/syntax-tree.md +++ b/docs/design/syntax-tree.md @@ -80,7 +80,7 @@ The move was made in steps, each a PR with the test suite as the safety net. Mos ### Known gaps to cover in the unbash adapter -These come from a survey of unbash 6.0.0. The adapter fills them; most are small. +These come from a survey of unbash 6.0.0 (6.0.1 only changed ANSI-C byte escapes). The adapter fills them; most are small. - **No positions for comments, keywords and operators.** The token scanner recovers them (estimated at 400–700 lines with tests). - **`[ ]` and `test` are plain commands.** The adapter makes `[ … ]` a `test_command` with its words as a flat list; testexpr does the grouping. diff --git a/package-lock.json b/package-lock.json index 915248fd..ca925665 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.6.0", "license": "MIT", "dependencies": { - "unbash": "6.0.0" + "unbash": "6.0.1" }, "devDependencies": { "@codemirror/commands": "6.11.1", @@ -5738,9 +5738,9 @@ "license": "MIT" }, "node_modules/unbash": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-6.0.0.tgz", - "integrity": "sha512-vD5CvvFLVQDhXdNyNEPPa/tHPCK3HLySddOine+OZLDYIH1cMImGNgnw9+NkizToaPsFsQlwiyPGMiK+uwtYVw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-6.0.1.tgz", + "integrity": "sha512-0db9eS5iqybux41zLkX69nk90KTci7F7eZ+gXzEG/Q7E0tAqEBhRh5fkZvk1N7jT7QkGWqq34i8SFGPFEe1V8A==", "license": "ISC", "engines": { "node": ">=14" diff --git a/package.json b/package.json index 733b1b37..327372d7 100644 --- a/package.json +++ b/package.json @@ -622,7 +622,7 @@ "images": "node scripts/capture-readme-images.mjs" }, "dependencies": { - "unbash": "6.0.0" + "unbash": "6.0.1" }, "devDependencies": { "@codemirror/commands": "6.11.1", From 5557d0fd79f5fda69f14d09a4d98672314de15f8 Mon Sep 17 00:00:00 2001 From: Shun FUJIOKA Date: Thu, 22 Oct 2026 01:42:31 +0900 Subject: [PATCH 2/3] List unbash first in the third-party notices The generator puts the parser first in every table and under License texts, then the rest alphabetically; a test checks it. Co-Authored-By: Claude Sonnet 5.5 --- THIRD_PARTY_NOTICES.md | 46 +++++++++++++++++++++--------------------- scripts/notices.ts | 13 +++++++++--- test/licenses.test.ts | 11 +++++++++- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 36cbd38f..afddbfca 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -8,12 +8,13 @@ Bash Explainer includes the following third-party software (ISC, MIT). Their lic | Component | Version | License | Included as | | --- | --- | --- | --- | -| [unbash](https://github.com/webpro-nl/unbash) | 6.0.0 | ISC | bundled into `dist/extension.js`; bundled into `dist/web/extension.js` | +| [unbash](https://github.com/webpro-nl/unbash) | 6.0.1 | ISC | bundled into `dist/extension.js`; bundled into `dist/web/extension.js` | ## In the web version | Component | Version | License | Included as | | --- | --- | --- | --- | +| [unbash](https://github.com/webpro-nl/unbash) | 6.0.1 | ISC | bundled into `app.js` | | [@codemirror/commands](https://code.haverbeke.berlin/codemirror/commands) | 6.11.1 | MIT | bundled into `app.js` | | [@codemirror/language](https://code.haverbeke.berlin/codemirror/language) | 6.13.1 | MIT | bundled into `app.js` | | [@codemirror/legacy-modes](https://code.haverbeke.berlin/codemirror/legacy-modes) | 6.5.5 | MIT | bundled into `app.js` | @@ -26,17 +27,36 @@ Bash Explainer includes the following third-party software (ISC, MIT). Their lic | [@marijn/find-cluster-break](https://code.haverbeke.berlin/marijn/find-cluster-break) | 1.0.4 | MIT | bundled into `app.js` | | [crelt](https://code.haverbeke.berlin/marijn/crelt) | 1.0.7 | MIT | bundled into `app.js` | | [style-mod](https://code.haverbeke.berlin/marijn/style-mod) | 4.1.4 | MIT | bundled into `app.js` | -| [unbash](https://github.com/webpro-nl/unbash) | 6.0.0 | ISC | bundled into `app.js` | | [w3c-keyname](https://github.com/marijnh/w3c-keyname#readme) | 2.2.8 | MIT | bundled into `app.js` | ## In the command-line tool (npm package bash-explainer) | Component | Version | License | Included as | | --- | --- | --- | --- | -| [unbash](https://github.com/webpro-nl/unbash) | 6.0.0 | ISC | bundled into `dist/cli.js` | +| [unbash](https://github.com/webpro-nl/unbash) | 6.0.1 | ISC | bundled into `dist/cli.js` | ## License texts +### unbash + +```text +ISC License + +Copyright (c) Lars Kappert + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +``` + ### @codemirror/commands, @codemirror/language, @codemirror/legacy-modes, @codemirror/lint, @codemirror/state, @codemirror/streamparser, @codemirror/view ```text @@ -163,26 +183,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -### unbash - -```text -ISC License - -Copyright (c) Lars Kappert - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -``` - ### w3c-keyname ```text diff --git a/scripts/notices.ts b/scripts/notices.ts index b40c8dce..a4ad86f0 100644 --- a/scripts/notices.ts +++ b/scripts/notices.ts @@ -26,6 +26,13 @@ export interface Target { uses: Map; } +/** The parser every product is built on: listed first in every table and under "License texts"; the rest follow in alphabetical order. */ +export const FIRST = 'unbash'; + +/** Package names in the order the notices list them: FIRST, then the others alphabetically. */ +export const order = (names: Iterable): string[] => + [...names].sort((a, b) => Number(b === FIRST) - Number(a === FIRST) || a.localeCompare(b, 'en')); + const packageDir = (name: string) => path.join(root, 'node_modules', name); export function packageInfo(name: string): Package { @@ -80,7 +87,7 @@ export function collect(): Target[] { } export function render(targets: Target[]): string { - const names = [...new Set(targets.flatMap((t) => [...t.uses.keys()]))].sort(); + const names = order(new Set(targets.flatMap((t) => [...t.uses.keys()]))); const packages = new Map(names.map((n) => [n, packageInfo(n)])); const licenses = [...new Set([...packages.values()].map((p) => p.license))].sort(); const out: string[] = [ @@ -92,7 +99,7 @@ export function render(targets: Target[]): string { ]; for (const t of targets) { out.push('', `## ${t.title}`, '', '| Component | Version | License | Included as |', '| --- | --- | --- | --- |'); - for (const name of [...t.uses.keys()].sort()) { + for (const name of order(t.uses.keys())) { const p = packages.get(name)!; out.push(`| [${name}](${p.url}) | ${p.version} | ${p.license} | ${t.uses.get(name)!.join('; ')} |`); } @@ -101,7 +108,7 @@ export function render(targets: Target[]): string { // Packages with the very same license text share one copy. const byText = new Map(); for (const p of packages.values()) byText.set(p.licenseText, [...(byText.get(p.licenseText) ?? []), p.name]); - for (const [text, group] of [...byText].sort((a, b) => a[1][0].localeCompare(b[1][0]))) { + for (const [text, group] of [...byText].sort((a, b) => order([a[1][0], b[1][0]])[0] === a[1][0] ? -1 : 1)) { out.push('', `### ${group.join(', ')}`, '', '```text', text, '```'); } return out.join('\n') + '\n'; diff --git a/test/licenses.test.ts b/test/licenses.test.ts index 16a69485..640b7eae 100644 --- a/test/licenses.test.ts +++ b/test/licenses.test.ts @@ -6,7 +6,7 @@ import { execFileSync } from 'child_process'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; -import { ALLOWED_LICENSES, collect, NOTICES, packageInfo, render, Target } from '../scripts/notices'; +import { ALLOWED_LICENSES, collect, FIRST, NOTICES, order, packageInfo, render, Target } from '../scripts/notices'; const root = path.join(__dirname, '..'); @@ -21,6 +21,15 @@ describe('licenses', function () { assert.strictEqual(fs.readFileSync(NOTICES, 'utf8'), render(targets), 'THIRD_PARTY_NOTICES.md is out of date: run `npm run notices` and check the changes'); }); + it('lists unbash, the parser, first in every table and under "License texts", then the rest alphabetically', () => { + assert.deepStrictEqual(order(['w3c-keyname', 'unbash', '@codemirror/view', 'crelt']), [FIRST, '@codemirror/view', 'crelt', 'w3c-keyname']); + const text = fs.readFileSync(NOTICES, 'utf8'); + for (const section of text.split(/^## /m).slice(1)) { + const first = /^\| \[([^\]]+)\]|^### (.+)$/m.exec(section.replace(/^[^\n]*\n/, '')); + assert.strictEqual(first?.[1] ?? first?.[2], FIRST, section.split('\n')[0]); + } + }); + it('includes only software under licenses whose terms reproducing the notice meets', () => { for (const t of targets) { for (const name of t.uses.keys()) { From b96b5800a20f776b063b865ff254d07973b775e9 Mon Sep 17 00:00:00 2001 From: Shun FUJIOKA Date: Thu, 22 Oct 2026 01:42:31 +0900 Subject: [PATCH 3/3] Watch unbash releases weekly A workflow opens an issue when npm has a newer unbash than package.json pins. Co-Authored-By: Claude Sonnet 5.5 --- .github/workflows/unbash-release.yml | 56 ++++++++++++++++++++++++++++ README.md | 1 + 2 files changed, 57 insertions(+) create mode 100644 .github/workflows/unbash-release.yml diff --git a/.github/workflows/unbash-release.yml b/.github/workflows/unbash-release.yml new file mode 100644 index 00000000..545421d7 --- /dev/null +++ b/.github/workflows/unbash-release.yml @@ -0,0 +1,56 @@ +name: Watch unbash releases + +# unbash is the parser every product is built on and is pinned exactly in package.json. +# This opens an issue when a newer version is on npm, so an upgrade is not left to the monthly Dependabot run. +on: + schedule: + - cron: '17 6 * * 1' + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + watch: + name: Compare with npm + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Open an issue for a newer version + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + current=$(node -p "require('./package.json').dependencies.unbash") + latest=$(npm view unbash version) + echo "pinned: $current, latest: $latest" + # Only a newer version counts (sort -V puts the highest last); never an older or equal one. + if [ "$current" = "$latest" ] || [ "$(printf '%s\n%s\n' "$current" "$latest" | sort -V | tail -n 1)" != "$latest" ]; then + echo "Up to date." + exit 0 + fi + title="unbash $latest is out" + if [ -n "$(gh issue list --state open --search "\"$title\" in:title" --json title --jq ".[] | select(.title == \"$title\") | .title")" ]; then + echo "An issue already exists." + exit 0 + fi + body=$(cat <`. The [Release workflow](.github/workflows/release.yml) tests and packages it, waits for approval in the `release` environment, then publishes to the VS Code Marketplace, Open VSX and npm, creates a GitHub Release, moves the `v0` tag the GitHub Action is used by, and updates the web version; see [docs/publishing.md](docs/publishing.md) for the steps and the one-time setup.