diff --git a/.github/workflows/integrity.yml b/.github/workflows/integrity.yml new file mode 100644 index 0000000..e600d3c --- /dev/null +++ b/.github/workflows/integrity.yml @@ -0,0 +1,30 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow + +name: "Integrity" +# This workflow prevents earthquakes. + +on: # yamllint disable-line rule:truthy + pull_request: null + push: + branches: + - "master" + +permissions: {} # yamllint disable-line rule:braces + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true + +jobs: + call_workflow_integrity: + name: "Integrity" + uses: "./.github/workflows/reusable-integrity.yml" + with: + not-printable-ascii-paths: >- + index.js + export-excludes: "" + exported-paths: >- + LICENSE + README.md + index.js + package.json diff --git a/.github/workflows/reusable-integrity.yml b/.github/workflows/reusable-integrity.yml new file mode 100644 index 0000000..178998b --- /dev/null +++ b/.github/workflows/reusable-integrity.yml @@ -0,0 +1,190 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow + +name: "Shared Integrity" +# This action prevents earthquakes. + +on: # yamllint disable-line rule:truthy + workflow_call: + inputs: + executables: + description: "List of executable files" + type: "string" + default: "" + required: false + not-printable-ascii-paths: + description: "Paths to be searched for characters outside the printable ASCII range" + type: "string" + default: "src/ tests/" + required: false + export-excludes: + description: "List of the expected exported directories" + type: "string" + default: "--exclude='src' --exclude='src/*'" + required: false + exported-paths: + description: "List of the expected exported files" + type: "string" + default: "LICENSE README.md composer.json" + required: false + +permissions: {} # yamllint disable-line rule:braces +#permissions: "read-all" +#permissions: +# contents: "read" # Private repositories need read permission + +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}-reusable-integrity" + cancel-in-progress: true + +jobs: + file: + name: "File" + runs-on: "ubuntu-22.04" + timeout-minutes: 1 + steps: + - + name: "Checkout repository" + uses: "actions/checkout@v3.6.0" + - + name: "Check file names" + run: | + ! git ls-tree -r -t -z --name-only HEAD \ + | xargs --null --max-args=1 -- basename \ + | grep --perl-regexp '[^-.0-9A-Z_a-z]' + - + name: "Check file permissions" + run: | + test "$( + git ls-files --stage \ + | grep --invert-match '^100644' \ + | cut --characters='51-' \ + | paste --serial --delimiters=' ' + )" = "${{ inputs.executables }}" + # Filesystem-based + #test "$( + # find ./ -type f -not -path './.git/*' \ + # -executable \ + # -exec echo -n '{}' '+' + #)" = "${{ inputs.executables }}" + - + # https://github.com/greut/eclint/issues/6 + name: "Check for byte order mark (BOM)" + run: | + ! git grep --perl-regexp -I -e '^\xEF\xBB\xBF' + - + # https://html.spec.whatwg.org/multipage/named-characters.html + name: "Search for characters outside the printable ASCII range" + run: | + ! LC_ALL=C.UTF-8 git grep --perl-regexp --line-number -I -e '[^ -~]' \ + -- ${{ inputs.not-printable-ascii-paths }} + # Filesystem-based + #! LC_ALL=C.UTF-8 find src/ resources/ tests/ -type f -print0 \ + # | xargs --null -- grep --perl-regexp --with-filename --line-number '[^ -~]' + # Ignore lines + # | grep -v '// @ignore-non-ascii$' + - + name: "Check EditorConfig configuration" + run: "test -f .editorconfig" + - + name: "Check adherence to EditorConfig" + uses: "greut/eclint-action@v0.5.0" + - + name: "Look for TAB characters in the middle of the line 🐌" + run: | + ! git grep --perl-regexp --line-number -I -e '^(?!//)[^\t]+\t' + - + name: "Look for multiple space characters in the middle of the line 🐌" + run: | + # Exclude comment lines and docblocks + ! git grep --perl-regexp --line-number -I \ + -e '\S\s\s' --and --not -e '^(#|//)' --and --not -e ' \* @' \ + -- ':!:*.lock' ':!:*.md' ':!:.github/**.yml' + - + # Move TODO-s into GitHub issues! + name: "Search for TODO-s and FIXME-s 🐌" + run: | + ! git grep --extended-regexp --ignore-case -I -e '\b(TODO|FIXME)\b' \ + -- ':!:.github/workflows/reusable-integrity.yml' ':!:.github/workflows/back-end.yml' + - + name: "Remove blank first lines and multiple blank lines 🐌" + run: | + # Exclude binary files, empty files and ones with linguist-generated attribute set + git ls-files --cached \ + | git check-attr --stdin --all \ + | sed -n -e 's#^\(.\+\): linguist-generated: set$#":!:\1"#p' \ + | xargs -- git grep --null --files-with-matches -I -e '.' -- \ + | xargs --null -n 1 -- sed -i -e '/./,$!d' -e '/^$/N;/^\n$/D' + - + name: "Check differences to repository" + run: "git diff --exit-code" + + cloc: + name: "Lines of Code" + runs-on: "ubuntu-22.04" + timeout-minutes: 1 + steps: + - + name: "Checkout repository" + uses: "actions/checkout@v3.6.0" + - + name: "Count Lines of Code" + env: + GH_TOKEN: "${{ github.token }}" + run: | + mkdir -p "${{ runner.temp }}/cloc" + RELEASE_ASSET_URL="$( + # v1.98 + gh api /repos/AlDanial/cloc/releases/117882376 \ + --jq '."assets"[] | select(."name" | test("^cloc-.+\\.pl$")) | ."browser_download_url"' + )" + wget --secure-protocol=TLSv1_3 --max-redirect=1 --retry-on-host-error --retry-connrefused --tries=3 \ + --no-verbose --output-document="${{ runner.temp }}/cloc/cloc" "${RELEASE_ASSET_URL}" + { + git ls-files -- ':!:LICENSE' ':!:package-lock.json' >"${{ runner.temp }}/cloc/include-list" + echo '```' + perl "${{ runner.temp }}/cloc/cloc" --hide-rate \ + --list-file="${{ runner.temp }}/cloc/include-list" \ + --ignored="${{ runner.temp }}/cloc/.clocignored" + cat "${{ runner.temp }}/cloc/.clocignored" + echo '```' + } >>"${GITHUB_STEP_SUMMARY}" + + commit: + name: "Commit" + runs-on: "ubuntu-22.04" + timeout-minutes: 1 + steps: + - + name: "Checkout repository" + uses: "actions/checkout@v3.6.0" + - + name: "Search for conflict markers 🐌" + run: | + ! git grep --line-number -e '^\(<<<<<<<\s\|=======\s\|=======$\|>>>>>>>\s\||||||||\s\)' + + git_archive: + name: "Git archive" + runs-on: "ubuntu-22.04" + timeout-minutes: 1 + steps: + - + name: "Checkout repository" + uses: "actions/checkout@v3.6.0" + - + name: "Check for ignored files in the index 🐌" + run: | + # Add negated files: ':!:path/to/negated' + IGNORED_FILES="$(git ls-files --cached --ignored --exclude-standard)" + test -z "${IGNORED_FILES}" + - + name: "Check exported files" + run: | + EXPECTED="${{ inputs.exported-paths }}" + CURRENT="$( + git archive HEAD \ + | tar --list ${{ inputs.export-excludes }} \ + | paste --serial --delimiters=" " + )" + echo "CURRENT =${CURRENT}" + echo "EXPECTED=${EXPECTED}" + test "${CURRENT}" = "${EXPECTED}"