From 8b4a2e195ee3607485eab01d661006e2e9619df0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 27 Jun 2024 09:58:24 -0700 Subject: [PATCH] Add validation scripts Make sure that all samples are listed properly and the readme is updated --- .github/workflows/ci.yml | 14 ++++++++ .scripts/samples.js | 35 +++++++++++++++--- .scripts/update-readme.js | 74 +++++++++++++++++++++++++-------------- .scripts/validate.js | 43 +++++++++++++++++++++++ README.md | 8 +++-- 5 files changed, 141 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .scripts/validate.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b9ca537b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,14 @@ +name: CI + +on: + pull_request: + branches: [ $default-branch ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + - run: node ./.scripts/validate.js \ No newline at end of file diff --git a/.scripts/samples.js b/.scripts/samples.js index 4767b57c..f81914d6 100644 --- a/.scripts/samples.js +++ b/.scripts/samples.js @@ -164,16 +164,14 @@ const samples = [ { description: 'webpack-sample', path: 'webpack-sample', - excludeFromReadme: true, - guide: null, + guide: '/api/working-with-extensions/bundling-extension', apis: [], contributions: [] }, { description: 'esbuild-sample', path: 'esbuild-sample', - excludeFromReadme: true, - guide: null, + guide: '/api/working-with-extensions/bundling-extension', apis: [], contributions: [] }, @@ -276,6 +274,20 @@ const samples = [ apis: [], contributions: [] }, + { + description: 'Chat Sample', + path: 'chat-sample', + guide: null, + apis: [], + contributions: [] + }, + { + description: 'Notifications Sample', + path: 'notifications-sample', + guide: null, + apis: [], + contributions: [] + }, { description: 'configuration-sample', excludeFromReadme: true, path: 'configuration-sample', guide: null, apis: [], contributions: [] }, { description: 'contentprovider-sample', excludeFromReadme: true, path: 'contentprovider-sample', guide: null, apis: [], contributions: [] }, { description: 'nodefs-provider-sample', excludeFromReadme: true, path: 'nodefs-provider-sample', guide: null, apis: [], contributions: [] }, @@ -285,10 +297,14 @@ const samples = [ { description: 'fsconsumer-sample', excludeFromReadme: true, path: 'fsconsumer-sample', guide: null, apis: [], contributions: [] }, { description: 'github-authentication-sample', excludeFromReadme: true, path: 'github-authentication-sample', guide: null, apis: [], contributions: [] }, { description: 'helloworld-sample', excludeFromReadme: true, path: 'helloworld-sample', guide: null, apis: [], contributions: [] }, + { description: 'helloworld-minimal-sample', excludeFromReadme: true, path: 'helloworld-minimal-sample', guide: null, apis: [], contributions: [] }, { description: 'helloworld-test-sample', excludeFromReadme: true, path: 'helloworld-test-sample', guide: null, apis: [], contributions: [] }, { description: 'helloworld-web-sample', excludeFromReadme: true, path: 'helloworld-web-sample', guide: null, apis: [], contributions: [] }, + { description: 'helloworld-test-cli-sample', excludeFromReadme: true, path: 'helloworld-test-cli-sample', guide: null, apis: [], contributions: [] }, { description: 'inline-completions', excludeFromReadme: true, path: 'inline-completions', guide: null, apis: [], contributions: [] }, { description: 'notebook-renderer-react-sample', excludeFromReadme: true, path: 'notebook-renderer-react-sample', guide: null, apis: [], contributions: [] }, + { description: 'notebook-format-code-action-sample', excludeFromReadme: true, path: 'notebook-format-code-action-sample', guide: null, apis: [], contributions: [] }, + { description: 'notebook-serializer-sample', excludeFromReadme: true, path: 'notebook-serializer-sample', guide: null, apis: [], contributions: [] }, { description: 'proposed-api-sample', excludeFromReadme: true, path: 'proposed-api-sample', guide: null, apis: [], contributions: [] }, { description: 'virtual-document-sample', excludeFromReadme: true, path: 'virtual-document-sample', guide: null, apis: [], contributions: [] }, { description: 'welcome-view-content-sample', excludeFromReadme: true, path: 'welcome-view-content-sample', guide: null, apis: [], contributions: [] }, @@ -296,6 +312,10 @@ const samples = [ { description: 'drop-on-document', excludeFromReadme: true, path: 'drop-on-document', guide: null, apis: [], contributions: [] }, { description: 'uri-handler-sample', excludeFromReadme: true, path: 'uri-handler-sample', guide: null, apis: [], contributions: [] }, { description: 'authenticationprovider-sample', excludeFromReadme: true, path: 'authenticationprovider-sample', guide: null, apis: [], contributions: [] }, + { description: 'jupyter-kernel-execution-sample', excludeFromReadme: true, path: 'jupyter-kernel-execution-sample', guide: null, apis: [], contributions: [] }, + { description: 'shell-integration-sample', excludeFromReadme: true, path: 'shell-integration-sample', guide: null, apis: [], contributions: [] }, + { description: 'tabs-api-sample', excludeFromReadme: true, path: 'tabs-api-sample', guide: null, apis: [], contributions: [] }, + { description: 'telemetry-sample', excludeFromReadme: true, path: 'telemetry-sample', guide: null, apis: [], contributions: [] }, ] /** LSP specific samples */ @@ -344,6 +364,13 @@ const lspSamples = [ apis: [], contributions: [] }, + { + description: 'LSP User Input Sample', + path: 'lsp-user-input-sample', + guide: null, + apis: [], + contributions: [] + }, ] /** * LSP specific samples diff --git a/.scripts/update-readme.js b/.scripts/update-readme.js index 436c6dad..440176f1 100644 --- a/.scripts/update-readme.js +++ b/.scripts/update-readme.js @@ -1,5 +1,5 @@ +// @ts-check const fs = require('fs') - const { samples, lspSamples } = require('./samples') const TABLE_HEAD = ` @@ -12,46 +12,66 @@ const LSP_TABLE_HEAD = ` const LSP_TABLE_END = `` const getTableRow = sample => { - const descriptionCell = `[${sample.description}](https://github.com/Microsoft/vscode-extension-samples/tree/main/${sample.path})` - let guideCell - if (!sample.guide) { - guideCell = 'N/A' - } else if (sample.guide && sample.guide.startsWith('http')) { - guideCell = sample.guide - } else { - guideCell = `[${sample.guide}](https://code.visualstudio.com${sample.guide})` - } + const descriptionCell = `[${sample.description}](https://github.com/Microsoft/vscode-extension-samples/tree/main/${sample.path})` + let guideCell + if (!sample.guide) { + guideCell = 'N/A' + } else if (sample.guide && sample.guide.startsWith('http')) { + guideCell = sample.guide + } else { + guideCell = `[${sample.guide}](https://code.visualstudio.com${sample.guide})` + } - const apis = sample.apis.map(api => { - return `[${api}](https://code.visualstudio.com/api/references/vscode-api#${api})` - }) - const contributions = sample.contributions.map(c => { - return `[contributes.${c}](https://code.visualstudio.com/api/references/contribution-points#contributes.${c})` - }) - const apiAndContributionCell = apis.concat(contributions).join('
') + const apis = sample.apis.map(api => { + return `[${api}](https://code.visualstudio.com/api/references/vscode-api#${api})` + }) + const contributions = sample.contributions.map(c => { + return `[contributes.${c}](https://code.visualstudio.com/api/references/contribution-points#contributes.${c})` + }) + const apiAndContributionCell = apis.concat(contributions).join('
') - return `| ${descriptionCell} | ${guideCell} | ${apiAndContributionCell} |` + return `| ${descriptionCell} | ${guideCell} | ${apiAndContributionCell} |` } const getSamplesTable = samples => { - const samplesMd = samples.map(s => getTableRow(s)).join('\n') + const samplesMd = samples.map(s => getTableRow(s)).join('\n') - return `${TABLE_HEAD.trim()} + return `${TABLE_HEAD.trim()} ${samplesMd} ${TABLE_END.trim()}` } const getLSPSamplesTable = samples => { - const samplesMd = samples.map(s => getTableRow(s)).join('\n') + const samplesMd = samples.map(s => getTableRow(s)).join('\n') - return `${LSP_TABLE_HEAD.trim()} + return `${LSP_TABLE_HEAD.trim()} ${samplesMd} ${LSP_TABLE_END.trim()}` } -const readme = fs.readFileSync('README.md', 'utf-8') -const newReadme = readme - .replace(/(.|\n)*/gm, getSamplesTable(samples.filter(x => !x.excludeFromReadme))) - .replace(/(.|\n)*/gm, getLSPSamplesTable(lspSamples)) +/** + * Update the README with the latest samples. + * + * @returns {boolean} true if the README was updated, false otherwise. + */ +function updateReadme(dryRun = false) { + const readme = fs.readFileSync('README.md', 'utf-8') + const newReadme = readme + .replace(/(.|\n)*/gm, getSamplesTable(samples.filter(x => !x.excludeFromReadme))) + .replace(/(.|\n)*/gm, getLSPSamplesTable(lspSamples)) -fs.writeFileSync('README.md', newReadme) + if (readme !== newReadme) { + if (!dryRun) { + fs.writeFileSync('README.md', newReadme) + } + return true; + } else { + return false; + } +} + +exports.updateReadme = updateReadme; + +if (require.main === module) { + updateReadme(); +} diff --git a/.scripts/validate.js b/.scripts/validate.js new file mode 100644 index 00000000..10b8294e --- /dev/null +++ b/.scripts/validate.js @@ -0,0 +1,43 @@ +// @ts-check +const path = require('path'); + +const { samples, lspSamples } = require('./samples'); +const fs = require('fs'); + +const root = path.join(__dirname, '..'); + +/** + * Validates that all samples are correctly listed in `.scripts/samples.js`. + */ +function validateSamplesAreListed() { + const allSamples = samples.concat(lspSamples); + + const samplesByPath = new Map(); + for (const sample of allSamples) { + samplesByPath.set(sample.path, sample); + } + + for (const fileName of fs.readdirSync(root)) { + if (!fileName.endsWith('-sample') || fileName.startsWith('.')) { + continue; + } + + const sampleEntry = samplesByPath.get(fileName); + if (!sampleEntry) { + throw new Error(`Sample '${fileName}' is not listed in samples.js`); + } + } +} + +/** + * Validates that all samples are correctly listed in `.scripts/samples.js`. + */ +function validateReadmeUpdated() { + const { updateReadme } = require('./update-readme'); + if (updateReadme(true)) { + throw new Error(`Readme not updated. Run 'node .scripts/update-readme.js' to update the readme.`); + } +} + +validateSamplesAreListed(); +validateReadmeUpdated(); \ No newline at end of file diff --git a/README.md b/README.md index 749073dc..7f38a057 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ You need to have [node](https://nodejs.org/en/) and [npm](https://nodejs.org/en/ | [Color Theme Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/theme-sample) | [/api/extension-guides/color-theme](https://code.visualstudio.com/api/extension-guides/color-theme) | [contributes.themes](https://code.visualstudio.com/api/references/contribution-points#contributes.themes) | | [Product Icon Theme Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/product-icon-theme-sample) | [/api/extension-guides/product-icon-theme](https://code.visualstudio.com/api/extension-guides/product-icon-theme) | [contributes.productIconThemes](https://code.visualstudio.com/api/references/contribution-points#contributes.productIconThemes) | | [Vim Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/vim-sample) | N/A | [commands](https://code.visualstudio.com/api/references/vscode-api#commands)
[StatusBarItem](https://code.visualstudio.com/api/references/vscode-api#StatusBarItem)
[window.createStatusBarItem](https://code.visualstudio.com/api/references/vscode-api#window.createStatusBarItem)
[TextEditorCursorStyle](https://code.visualstudio.com/api/references/vscode-api#TextEditorCursorStyle)
[window.activeTextEditor](https://code.visualstudio.com/api/references/vscode-api#window.activeTextEditor)
[Position](https://code.visualstudio.com/api/references/vscode-api#Position)
[Range](https://code.visualstudio.com/api/references/vscode-api#Range)
[Selection](https://code.visualstudio.com/api/references/vscode-api#Selection)
[TextEditor](https://code.visualstudio.com/api/references/vscode-api#TextEditor)
[TextEditorRevealType](https://code.visualstudio.com/api/references/vscode-api#TextEditorRevealType)
[TextDocument](https://code.visualstudio.com/api/references/vscode-api#TextDocument) | -| [webpack-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/webpack-sample) | [api/working-with-extensions/bundling-extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension) | | -| [esbuild-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/esbuild-sample) | [api/working-with-extensions/bundling-extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension) | | +| [webpack-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/webpack-sample) | [/api/working-with-extensions/bundling-extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension) | | +| [esbuild-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/esbuild-sample) | [/api/working-with-extensions/bundling-extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension) | | | [Source Control Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/source-control-sample) | [/api/extension-guides/scm-provider](https://code.visualstudio.com/api/extension-guides/scm-provider) | [workspace.workspaceFolders](https://code.visualstudio.com/api/references/vscode-api#workspace.workspaceFolders)
[SourceControl](https://code.visualstudio.com/api/references/vscode-api#SourceControl)
[SourceControlResourceGroup](https://code.visualstudio.com/api/references/vscode-api#SourceControlResourceGroup)
[scm.createSourceControl](https://code.visualstudio.com/api/references/vscode-api#scm.createSourceControl)
[TextDocumentContentProvider](https://code.visualstudio.com/api/references/vscode-api#TextDocumentContentProvider)
[contributes.menus](https://code.visualstudio.com/api/references/contribution-points#contributes.menus) | | [Commenting API Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/comment-sample) | N/A | | | [Document Editing Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/document-editing-sample) | N/A | [commands](https://code.visualstudio.com/api/references/vscode-api#commands) | @@ -65,6 +65,9 @@ You need to have [node](https://nodejs.org/en/) and [npm](https://nodejs.org/en/ | [Getting Started Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/getting-started-sample) | N/A | | | [notebook-renderer-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/notebook-renderer-sample) | [/api/extension-guides/notebook#notebook-renderer](https://code.visualstudio.com/api/extension-guides/notebook#notebook-renderer) | [contributes.notebookRenderer](https://code.visualstudio.com/api/references/contribution-points#contributes.notebookRenderer) | | [notebook-extend-markdown-renderer-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/notebook-extend-markdown-renderer-sample) | [/api/extension-guides/notebook#notebook-renderer](https://code.visualstudio.com/api/extension-guides/notebook#notebook-renderer) | [contributes.notebookRenderer](https://code.visualstudio.com/api/references/contribution-points#contributes.notebookRenderer) | +| [jupyter-server-provider-sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/jupyter-server-provider-sample) | N/A | | +| [Chat Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/chat-sample) | N/A | | +| [Notifications Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/notifications-sample) | N/A | | ### Language Server Protocol Samples @@ -78,6 +81,7 @@ You need to have [node](https://nodejs.org/en/) and [npm](https://nodejs.org/en/ | [LSP Log Streaming Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/lsp-log-streaming-sample) | N/A | | | [LSP Multi Root Server Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/lsp-multi-server-sample) | https://github.com/Microsoft/vscode/wiki/Extension-Authoring:-Adopting-Multi-Root-Workspace-APIs#language-client--language-server | | | [LSP Web Extension Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/lsp-web-extension-sample) | [/api/language-extensions/language-server-extension-guide](https://code.visualstudio.com/api/language-extensions/language-server-extension-guide) | | +| [LSP User Input Sample](https://github.com/Microsoft/vscode-extension-samples/tree/main/lsp-user-input-sample) | N/A | | ## License