From 17bf7f1f0fd19207674a09106b2b10ae3952a63f Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Sun, 19 Feb 2023 05:33:59 -0800 Subject: [PATCH] feat(ci): add workflow to check license header (#6315) --- .github/workflows/check-license-header.yml | 28 ++++++ check-license-header.js | 102 +++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 .github/workflows/check-license-header.yml create mode 100644 check-license-header.js diff --git a/.github/workflows/check-license-header.yml b/.github/workflows/check-license-header.yml new file mode 100644 index 000000000000..fb2055c10d00 --- /dev/null +++ b/.github/workflows/check-license-header.yml @@ -0,0 +1,28 @@ +# Copyright 2019-2023 Tauri Programme within The Commons Conservancy +# SPDX-License-Identifier: Apache-2.0 +# SPDX-License-Identifier: MIT + +name: Check generated files + +on: + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dorny/paths-filter@v2 + id: filter + with: + list-files: shell + filters: | + added: + - added: '**' + - name: check header license on new files + if: ${{ steps.filter.outputs.added == 'true' }} + run: node check-license-header.js ${{ steps.filter.outputs.added_files }} diff --git a/check-license-header.js b/check-license-header.js new file mode 100644 index 000000000000..c0a52e844fcc --- /dev/null +++ b/check-license-header.js @@ -0,0 +1,102 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +const fs = require('fs') +const path = require('path') +const readline = require('readline') + +const header = `Copyright 2019-2023 Tauri Programme within The Commons Conservancy +SPDX-License-Identifier: Apache-2.0 +SPDX-License-Identifier: MIT` +const bundlerLicense = '// Copyright 2016-2019 Cargo-Bundle developers ' + +const extensions = ['.rs', '.js', '.ts', '.yml'] +const ignore = ['target', 'templates', 'node_modules', 'gen', 'dist', 'bundle.js', 'bundle.global.js'] + +async function checkFile(file) { + if (extensions.some(e => file.endsWith(e))) { + const fileStream = fs.createReadStream(file) + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity + }) + + let contents = `` + let i = 0 + for await (let line of rl) { + // ignore empty lines, allow shebang and bundler license + if (line.length === 0 || line.startsWith("#!") || line === bundlerLicense) { + continue + } + + // strip comment marker + if (line.startsWith('// ')) { + line = line.substring(3) + } else if (line.startsWith('# ')) { + line = line.substring(2) + } + + contents += line + if (++i === 3) { + break + } + contents += '\n' + } + if (contents !== header) { + return true + } + } + return false +} + +async function check(src) { + const missingHeader = [] + + for (const entry of fs.readdirSync(src, { withFileTypes: true })) { + const p = path.join(src, entry.name) + + if (entry.isSymbolicLink() || ignore.includes(entry.name)) { + continue + } + + if (entry.isDirectory()) { + const missing = await check(p) + missingHeader.push(...missing) + } else { + const isMissing = await checkFile(p) + if (isMissing) { + missingHeader.push(p) + } + } + } + + return missingHeader +} + +const [_bin, _script, ...files] = process.argv + +if (files.length > 0) { + async function run() { + const missing = [] + for (const f of files) { + const isMissing = await checkFile(f) + if (isMissing) { + missing.push(f) + } + } + if (missing.length > 0) { + console.log(missing.join('\n')) + process.exit(1) + } + } + + run() +} else { + check('.').then(missing => { + if (missing.length > 0) { + console.log(missing.join('\n')) + process.exit(1) + } + }) +}