Browse Source

feat(ci): add workflow to check license header (#6315)

Lucas Fernandes Nogueira 2 years ago
parent
commit
17bf7f1f0f
2 changed files with 130 additions and 0 deletions
  1. 28 0
      .github/workflows/check-license-header.yml
  2. 102 0
      check-license-header.js

+ 28 - 0
.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 }}

+ 102 - 0
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 <https://github.com/burtonageo/cargo-bundle>'
+
+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)
+    }
+  })
+}