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
3 changes: 2 additions & 1 deletion .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ jobs:
sudo apt-get install clang-format-22
- name: apply formatter
run: |
clang-format-22 -style=google --dry-run --Werror $(find . -name "*.hpp")
git ls-files -z '*.hpp' 'test/verify/*.test.cpp' \
| xargs -0 clang-format-22 -style=google --dry-run --Werror
37 changes: 7 additions & 30 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,14 @@ jobs:
node-version: 22

- name: Check verification template includes
env:
VERIFY_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
VERIFY_ALLOWLIST: scripts/verify-template-include-allowlist.txt
run: |
args=()
if [[ -n "$VERIFY_BASE_SHA" && ! "$VERIFY_BASE_SHA" =~ ^0+$ ]]; then
if git cat-file -e "$VERIFY_BASE_SHA:$VERIFY_ALLOWLIST" 2>/dev/null; then
base_allowlist="$RUNNER_TEMP/verify-template-include-allowlist.txt"
git show "$VERIFY_BASE_SHA:$VERIFY_ALLOWLIST" > "$base_allowlist"
args+=(--base-allowlist "$base_allowlist")
fi
fi
npm run verify:template-check -- "${args[@]}"
run: npm run verify:template-check

- name: Check self-contained headers
env:
VERIFY_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
SELF_CONTAINED_HEADERS: scripts/self-contained-headers.txt
run: |
check_header() {
local path="$1"
if [[ ! -f "$path" ]]; then
echo "$SELF_CONTAINED_HEADERS contains a missing header: $path"
echo "Missing header: $path"
return 1
fi

Expand All @@ -68,20 +53,12 @@ jobs:
g++ -std=c++17 -fsyntax-only -x c++ -include "./$path" /dev/null
}

LC_ALL=C sort -c -u "$SELF_CONTAINED_HEADERS"
while IFS= read -r path; do
[[ -z "$path" || "$path" == \#* ]] && continue
while IFS= read -r -d '' path; do
check_header "$path"
done < "$SELF_CONTAINED_HEADERS"

if [[ -n "$VERIFY_BASE_SHA" && ! "$VERIFY_BASE_SHA" =~ ^0+$ ]]; then
while IFS= read -r -d '' path; do
if ! grep -Fqx -- "$path" "$SELF_CONTAINED_HEADERS"; then
echo "$path must be added to $SELF_CONTAINED_HEADERS"
exit 1
fi
done < <(git diff --name-only --diff-filter=A -z "$VERIFY_BASE_SHA...HEAD" -- '*.hpp')
fi
done < <(git ls-files -z '*.hpp' \
| while IFS= read -r -d '' path; do
[[ "$path" == 'template/template.hpp' ]] || printf '%s\0' "$path"
done)

markdown-style:
runs-on: ubuntu-latest
Expand Down
21 changes: 5 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,13 @@ implementations. Keep each task focused on one feature or one coherent change.
not write `using namespace std;` in a header or rely on an includer declaring
it first. Apply the same rules when deliberately migrating an existing
header; migrate other existing headers incrementally when the task scope
permits. Register every new or migrated header in
`scripts/self-contained-headers.txt` so CI continues compiling it in
isolation. Update `SELF_CONTAINED_HEADER_MIGRATION.md` when a migration batch
changes the recorded progress or history.
permits. CI automatically compiles every tracked `.hpp` except
`template/template.hpp` in isolation.
- Do not include `template/template.hpp` from new `test/verify/*.test.cpp`
files. Include the required standard library headers and declare any aliases
or helper functions used by the verification code explicitly. Existing
exceptions are tracked in
`scripts/verify-template-include-allowlist.txt`; remove a file from this list
after its dependencies are self-contained and the verification file is
migrated, and never add new exceptions.
- Preserve the include order in verification code when a library header depends
on declarations provided earlier in the file. Format such files with include
sorting disabled, for example:

```console
clang-format-22 -style=google --sort-includes=0 -i test/verify/example.test.cpp
```
or helper functions used by the verification code explicitly. CI does not
permit exceptions to this rule. Format verification code with normal include
sorting enabled.

## Tests

Expand Down
200 changes: 0 additions & 200 deletions SELF_CONTAINED_HEADER_MIGRATION.md

This file was deleted.

78 changes: 7 additions & 71 deletions scripts/check-verify-template-includes.mjs
Original file line number Diff line number Diff line change
@@ -1,37 +1,16 @@
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { readFileSync, readdirSync } from "node:fs";
import { dirname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const verifyDirectory = resolve(repositoryRoot, "test/verify");
const allowlistPath = resolve(
repositoryRoot,
"scripts/verify-template-include-allowlist.txt",
);
const templateInclude =
/^\s*#\s*include\s*["<][^">]*template\/template\.hpp[">]/m;

function repositoryPath(path) {
return relative(repositoryRoot, path).replaceAll("\\", "/");
}

function readAllowlist() {
const entries = readFileSync(allowlistPath, "utf8")
.split(/\r?\n/)
.filter((line) => line !== "" && !line.startsWith("#"));
const sortedEntries = [...new Set(entries)].sort();

if (
entries.length !== sortedEntries.length ||
entries.some((entry, index) => entry !== sortedEntries[index])
) {
throw new Error(
`${repositoryPath(allowlistPath)} must be sorted and contain no duplicates`,
);
}
return new Set(entries);
}

function findTemplateIncludes() {
return new Set(
readdirSync(verifyDirectory)
Expand All @@ -42,61 +21,18 @@ function findTemplateIncludes() {
);
}

let baseAllowlistPath;
for (let index = 2; index < process.argv.length; index += 1) {
const option = process.argv[index];
if (option !== "--base-allowlist") {
throw new Error(`unknown option: ${option}`);
}
if (baseAllowlistPath !== undefined) {
throw new Error("--base-allowlist may be specified at most once");
}
if (index + 1 === process.argv.length) {
throw new Error("--base-allowlist requires a value");
}
baseAllowlistPath = process.argv[index + 1];
index += 1;
if (process.argv.length !== 2) {
throw new Error("this check does not accept command-line options");
}

const allowlist = readAllowlist();
const templateIncludes = findTemplateIncludes();
const errors = [];

for (const path of templateIncludes) {
if (!allowlist.has(path)) {
errors.push(`${path}: template/template.hpp is not allowed`);
}
}
for (const path of allowlist) {
if (!existsSync(resolve(repositoryRoot, path))) {
errors.push(`${repositoryPath(allowlistPath)}: missing file: ${path}`);
} else if (!templateIncludes.has(path)) {
errors.push(
`${repositoryPath(allowlistPath)}: remove migrated entry: ${path}`,
);
}
}

if (baseAllowlistPath !== undefined) {
const baseAllowlist = new Set(
readFileSync(baseAllowlistPath, "utf8")
.split(/\r?\n/)
.filter((line) => line !== "" && !line.startsWith("#")),
);
for (const path of allowlist) {
if (!baseAllowlist.has(path)) {
errors.push(
`${repositoryPath(allowlistPath)}: adding an exemption is not allowed: ${path}`,
);
}
}
}
const errors = [...templateIncludes].map(
(path) => `${path}: template/template.hpp is not allowed`,
);

if (errors.length > 0) {
console.error(errors.join("\n"));
process.exitCode = 1;
} else {
console.log(
`Checked verification template includes (${allowlist.size} exemptions remain).`,
);
console.log("Checked verification template includes (no exemptions allowed).");
}
Loading
Loading