Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .browserslistrc
100755 → 100644
Empty file.
Empty file modified .editorconfig
100755 → 100644
Empty file.
Empty file modified .gitattributes
100755 → 100644
Empty file.
Empty file modified .github/ISSUE_TEMPLATE/1-bug-report.md
100755 → 100644
Empty file.
Empty file modified .github/PULL_REQUEST_TEMPLATE.md
100755 → 100644
Empty file.
Empty file modified .github/dependabot.yml
100755 → 100644
Empty file.
Empty file modified .github/workflows/lint-and-test.yml
100755 → 100644
Empty file.
Empty file modified .gitignore
100755 → 100644
Empty file.
Empty file modified .markdownlint.jsonc
100755 → 100644
Empty file.
Empty file modified .remarkignore
100755 → 100644
Empty file.
Empty file modified .remarkrc.mjs
100755 → 100644
Empty file.
Empty file modified .renovaterc.json5
100755 → 100644
Empty file.
Empty file modified .stylelintrc.json
100755 → 100644
Empty file.
Empty file modified .vscode/extensions.json
100755 → 100644
Empty file.
Empty file modified .vscode/keybindings.json
100755 → 100644
Empty file.
Empty file modified README.md
100755 → 100644
Empty file.
Empty file modified _includes/footer.liquid
100755 → 100644
Empty file.
Empty file modified _includes/head.liquid
100755 → 100644
Empty file.
Empty file modified _includes/header.liquid
100755 → 100644
Empty file.
Empty file modified _layouts/default.liquid
100755 → 100644
Empty file.
Empty file modified _layouts/docs.liquid
100755 → 100644
Empty file.
Empty file modified _layouts/page.liquid
100755 → 100644
Empty file.
75 changes: 75 additions & 0 deletions build/tasks/verify/verify-file-modes.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* @file Verify only the files meant to be run are marked executable.
* @author The OpenINF Authors & Friends
* @license MIT OR Apache-2.0 OR BlueOak-1.0.0
* @module {type ES6Module} build/tasks/verify/verify-file-modes
*/

import { execFileSync } from 'node:child_process';
import { open } from 'node:fs/promises';

// The mode git records, rather than the mode on disk: that is what other
// clones receive, and it is the only one a checkout on a filesystem without
// permission bits still reports faithfully.
const tracked = execFileSync('git', ['ls-files', '--stage', '-z'], {
encoding: 'utf8',
})
.split('\0')
.filter(Boolean)
.map((entry) => {
const [mode] = entry.split(' ');

return { mode, path: entry.slice(entry.indexOf('\t') + 1) };
});

/**
* Reads the first two bytes, which is all it takes to know whether a file
* expects to be run as a program.
* @param {string} path The file to inspect.
* @returns {Promise<boolean>} Whether the file opens with `#!`.
*/
const hasShebang = async (path: string) => {
// Tracked but not on disk, which is what a half-finished `git rm` or an
// interrupted checkout leaves behind. Nothing to read, and the mode of a
// file that is not there is not this task's argument to make.
const file = await open(path).catch(() => undefined);

if (file === undefined) return undefined;

try {
const { buffer, bytesRead } = await file.read(Buffer.alloc(2), 0, 2, 0);

return bytesRead === 2 && buffer.toString('latin1') === '#!';
} finally {
await file.close();
}
};

const offenders: string[] = [];

for (const { mode, path } of tracked) {
// Symlinks (120000) and submodules (160000) carry neither the bit nor a
// shebang to read, so only the two regular-file modes are of interest.
if (mode !== '100755' && mode !== '100644') continue;

const executable = mode === '100755';
const runnable = await hasShebang(path);

if (runnable === undefined) continue;

if (executable && !runnable) {
offenders.push(` ${path} is executable but has no \`#!\` line`);
} else if (runnable && !executable) {
offenders.push(` ${path} opens with \`#!\` but is not executable`);
}
}

if (offenders.length > 0) {
console.error(
`File modes disagree with what the files are:\n${offenders.join('\n')}\n\n` +
'Run `chmod +x` or `chmod -x` to settle it. Nothing else in the ' +
'pipeline looks at modes, which is how 23 files came to claim they ' +
'were programs.'
);
process.exitCode = 1;
}
5 changes: 3 additions & 2 deletions package-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ scripts:
siteifyHealthFiles: node build/tasks/compile/siteify-health-files.mts
verify:
# Announces each task and names the ones that failed. Without this the
# output is a wall of tool chatter with no indication of which of the
# thirteen produced it.
# output is a wall of tool chatter with no indication of which task
# produced it.
all: 'rc=0; failed=; for i in build/tasks/verify/*.mts; do echo "==> $i"; node "$i" || { rc=1; failed="$failed $i"; }; done; [ -z "$failed" ] || echo "FAILED:$failed" >&2; exit $rc'
browserslist: node build/tasks/verify/verify-browserslist.mts
scss: node build/tasks/verify/verify-scss.mts
dockerfile: node build/tasks/verify/verify-dockerfile.mts
fileModes: node build/tasks/verify/verify-file-modes.mts
htmlValidForVNU: node build/tasks/verify/verify-html-valid-for-vnu.mts
js: node build/tasks/verify/verify-js.mts
json: node build/tasks/verify/verify-json.mts
Expand Down
Empty file modified project-terms.txt
100755 → 100644
Empty file.