diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 4cd921796..578223b8e 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -2,6 +2,9 @@ name: Style Checks on: [push, pull_request] +env: + PYTHON_VERSION: '3.11' + permissions: contents: read @@ -24,6 +27,10 @@ jobs: with: fetch-depth: 0 - run: git branch -a + - name: Set up Python ${{ env.PYTHON_VERSION }} + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: ${{ env.PYTHON_VERSION }} - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 with: diff --git a/tools/README.md b/tools/README.md index 6b80e94f5..7fab9ab61 100644 --- a/tools/README.md +++ b/tools/README.md @@ -4,6 +4,11 @@ The clang-format checking tools is designed to check changed lines of code compared to given git-refs. +The tool requires Python 3 to run `git-clang-format`. It first tries the +executable specified by the `PYTHON` environment variable, when set. On +Windows it then tries the Python launcher (`py -3`), followed by `python3` and +`python`. On other platforms it tries `python3` and then `python`. + ## Migration Script The migration tool is designed to reduce repetitive work in the migration process. However, the script is not aiming to convert every thing for you. There are usually some small fixes and major reconstruction required. diff --git a/tools/clang-format.js b/tools/clang-format.js index e4bb4f52e..60cbbc514 100644 --- a/tools/clang-format.js +++ b/tools/clang-format.js @@ -1,10 +1,52 @@ #!/usr/bin/env node -const spawn = require('child_process').spawnSync; +const spawnSync = require('child_process').spawnSync; const path = require('path'); const filesToCheck = ['*.h', '*.cc']; const FORMAT_START = process.env.FORMAT_START || 'main'; +const pythonVersionCheck = [ + '-c', + 'import sys; raise SystemExit(sys.version_info[0] != 3)' +]; + +function findPython () { + const candidates = []; + + if (process.env.PYTHON) { + candidates.push({ + command: process.env.PYTHON, + args: [], + name: process.env.PYTHON + }); + } + + if (process.platform === 'win32') { + candidates.push({ command: 'py', args: ['-3'], name: 'py -3' }); + } + + candidates.push( + { command: 'python3', args: [], name: 'python3' }, + { command: 'python', args: [], name: 'python' } + ); + + for (const candidate of candidates) { + const result = spawnSync( + candidate.command, + [...candidate.args, ...pythonVersionCheck], + { stdio: 'ignore' } + ); + if (!result.error && result.status === 0) { + return candidate; + } + } + + throw new Error([ + 'Could not find a usable Python 3 executable.', + `Tried: ${candidates.map(({ name }) => name).join(', ')}.`, + 'Set the PYTHON environment variable to the path of a Python 3 executable.' + ].join('\n')); +} function main (args) { let fix = false; @@ -31,17 +73,46 @@ function main (args) { } const gitClangFormatPath = path.join(clangFormatPath, 'bin/git-clang-format'); - const result = spawn( - 'python', - [gitClangFormatPath, ...options, '--', ...filesToCheck], + let python; + try { + python = findPython(); + } catch (error) { + console.error(error.message); + return 2; + } + + const result = spawnSync( + python.command, + [ + ...python.args, + gitClangFormatPath, + ...options, + '--', + ...filesToCheck + ], { encoding: 'utf-8' } ); - if (result.stderr) { - console.error('Error running git-clang-format:', result.stderr); + if (result.error) { + console.error('Error running git-clang-format:', result.error.message); return 2; } + if (result.status !== 0 && result.status !== 1) { + const message = ( + result.stderr || + result.stdout || + result.signal || + `exit code ${result.status}` + ).trim(); + console.error(`Error running git-clang-format: ${message}`); + return 2; + } + + if (result.stderr) { + process.stderr.write(result.stderr); + } + const clangFormatOutput = result.stdout.trim(); // Bail fast if in fix mode. if (fix) {