Skip to content
Closed
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
91 changes: 59 additions & 32 deletions .dotstop.dot

Large diffs are not rendered by default.

32 changes: 29 additions & 3 deletions .dotstop_extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ in case of a custom description.

## TimeVaryingWebReference

The content of a `TimeVaryingWebReference` is given by the content of a changelog, whose default value is `ChangeLog.md`, which mirrors the changelog of nlohmann/json. This reference is intended for websites, whose content is constantly changing, so that a `WebContentReference` makes the item un-reviewable, but whose content at the time of an update influences the trustability. An example is `https://github.com/nlohmann/json/pulse/monthly`, which can be used to demonstrate that nlohmann/json is *up to the most recent version* under active development.
The content of a `TimeVaryingWebReference` is given by the content of a changelog, whose default value is `ChangeLog.md`, which mirrors the changelog of nlohmann/json. This reference is intended for websites whose content is constantly changing, so that a `WebContentReference` makes the item un-reviewable, but whose content at the time of an update influences the trustability. An example is `https://github.com/nlohmann/json/pulse/monthly`, which can be used to demonstrate that nlohmann/json is *up to the most recent version* under active development.

An example of the complete configuration for `TimeVaryingWebReference` is

Expand Down Expand Up @@ -235,6 +235,32 @@ references:
```
Here, the elements of the list `items` must be normative nodes of the trustable graph, otherwise an error is thrown.

## IncludeListReference

The content of an `IncludeListReference` is given by the list of `#include` lines extracted from a specified source/header file in the repository (for example `single_include/nlohmann/json.hpp`). This reference is useful to document which libraries a file depends on without embedding the full file content into the report.

Behaviour:
- content: returns the concatenation of all lines that begin with `#include` in the target file as UTF-8 encoded bytes. If no includes are found, the content is `b"No includes found"`.
- as_markdown: renders the found `#include` lines as a C++ code block (```cpp ... ```). If a `description` was provided when constructing the reference, the description is shown as an indented bullet above the code block.
- If the referenced file does not exist or is not a regular file, accessing `content` raises a ReferenceError.

Usage example:

```
---
...

references:
- type: include_list
path: "single_include/nlohmann/json.hpp"
description: "List of direct includes of the amalgamated header"
---
```

Notes:
- `description` is optional.
- The reference only extracts lines whose first non-whitespace characters are `#include`.

# Validators

Validators are extensions of trudag, used to validate any data that can be reduced to a floating point metric. The resulting scores are used as evidence for the trustability of items in the trustable graph.
Expand Down Expand Up @@ -270,8 +296,8 @@ evidence:
- "https://github.com/nlohmann/json/graphs/commit-activity"
- "https://github.com/nlohmann/json/forks?include=active&page=1&period=&sort_by=last_updated"
```
A response time of at least the five-fold of the acceptable response time is deemed inacceptable and gives an individual score of zero.
Likewise inacceptable is a response code other than `200`, which gives an individual score of zero.
A response time of at least the five-fold of the acceptable response time is deemed unacceptable and gives an individual score of zero.
Likewise unacceptable is a response code other than `200`, which gives an individual score of zero.

The total score is the mean of the individual scores.

Expand Down
57 changes: 57 additions & 0 deletions .dotstop_extensions/references.py
Original file line number Diff line number Diff line change
Expand Up @@ -951,3 +951,60 @@ def __str__(self):
title += "items "
title += ", ".join(self._items)
return title


class IncludeListReference(BaseReference):
"""
Reference that lists all #include lines in a given file (e.g. single_include/nlohmann/json.hpp).
Usage: IncludeListReference("single_include/nlohmann/json.hpp", "optional description")
"""
def __init__(self, path: str, description: str = "") -> None:
self._path = Path(path)
self._description = description

@classmethod
def type(cls) -> str:
return "include_list"

@property
def content(self) -> bytes:
if not self._path.is_file():
raise ReferenceError(f"Cannot get non-existent or non-regular file {self._path}")

text = self._path.read_text(encoding="utf-8")
includes = []

for line in text.splitlines():
# Only process lines that start with #include (ignoring whitespace)
if line.lstrip().startswith("#include"):
# Remove single-line comments
line = line.split("//")[0].rstrip()

# Remove multi-line comments
comment_start = line.find("/*")
if comment_start != -1:
comment_end = line.find("*/", comment_start)
if comment_end != -1:
line = line[:comment_start] + line[comment_end + 2:]

# Add the cleaned include line
includes.append(line.rstrip())

if not includes:
return b"No includes found"
return ("\n".join(includes)).encode("utf-8")

def as_markdown(self, filepath: None | str = None) -> str:
content = self.content.decode("utf-8")
if content == "No includes found":
return make_md_bullet_point(f"No includes found in {self._path}", 1)
md = format_cpp_code_as_markdown(content)
if self._description:
md = make_md_bullet_point(f"Description: {self._description}", 1) + "\n\n" + add_indentation(md, 1)
else:
md = add_indentation(md, 1)
return md

def __str__(self) -> str:
return f"List of included libraries for: {self._path}"

63 changes: 62 additions & 1 deletion .dotstop_extensions/test_references.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import tempfile
from pathlib import Path
from unittest.mock import patch
from references import CPPTestReference, JSONTestsuiteReference, FunctionReference, ItemReference, ListOfTestCases
from references import CPPTestReference, JSONTestsuiteReference, FunctionReference, ItemReference, IncludeListReference, ListOfTestCases
from validators import file_exists


Expand Down Expand Up @@ -856,3 +856,64 @@ def test_str_method():
"""Test __str__ method."""
list_ref = ListOfTestCases(["test_file"])
assert str(list_ref) == "List of all unit-tests"
def test_include_list_init():
ref = IncludeListReference("some/path.hpp", "my desc")
assert ref._path == Path("some/path.hpp")
assert ref._description == "my desc"

def test_type_classmethod_include_list():
assert IncludeListReference.type() == "include_list"

def test_content_includes_found():
content = '#include <iostream>\n #include "local.h"\nint x = 0;\n'
temp = create_temp_file(content, suffix='.hpp')
try:
ref = IncludeListReference(str(temp), "desc")
data = ref.content
assert isinstance(data, bytes)
decoded = data.decode('utf-8')
assert '#include <iostream>' in decoded
assert '#include "local.h"' in decoded
finally:
temp.unlink()

def test_content_no_includes():
temp = create_temp_file('int x = 1;\n// nothing to include\n', suffix='.hpp')
try:
ref = IncludeListReference(str(temp))
assert ref.content == b"No includes found"
finally:
temp.unlink()

def test_content_file_not_found():
ref = IncludeListReference("nonexistent_file_hopefully.hpp")
with pytest.raises(ReferenceError):
_ = ref.content

def test_as_markdown_with_description():
content = '#include <vector>\n#include "a.h"\n'
temp = create_temp_file(content, suffix='.hpp')
try:
ref = IncludeListReference(str(temp), "list of includes")
md = ref.as_markdown()
assert isinstance(md, str)
# starts with an indented bullet for description
assert md.startswith('\t- Description: list of includes')
assert '```cpp' in md
assert '#include <vector>' in md
finally:
temp.unlink()

def test_as_markdown_no_includes():
temp = create_temp_file('void f();\n', suffix='.hpp')
try:
ref = IncludeListReference(str(temp))
md = ref.as_markdown()
# should return a single indented bullet line about no includes
assert md.strip().startswith('- No includes found in')
finally:
temp.unlink()

def test_str_include_list():
ref = IncludeListReference("path/to/file.hpp")
assert str(ref) == f"List of included libraries for: {Path('path/to/file.hpp')}"
96 changes: 96 additions & 0 deletions .github/workflows/SME_review_checker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: SME Review Checker

on:
workflow_call:
inputs:
artifact_id:
description: 'Unique identifier for artifacts'
required: true
type: string

permissions:
contents: read
pull-requests: read

jobs:
check-SME-review:
runs-on: ubuntu-latest

steps:
- name: Harden Runner
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
with:
egress-policy: audit

- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0

- name: Get changed files
id: changed-files
run: |
# Get the base branch
BASE_BRANCH="${{ github.event.pull_request.base.ref || 'main' }}"

# Get all changed files in the PR
CHANGED_FILES=$(git diff --name-only origin/$BASE_BRANCH...HEAD)

# Save changed files to output
echo "files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Check if supported statements are scored
run: |
# Read the changed files
CHANGED_FILES="${{ steps.changed-files.outputs.files }}"

# Process each changed file
while IFS= read -r file; do
# Skip empty lines
if [[ -z "$file" ]]; then
continue
fi

echo "Checking file: $file"

# Check if file is in TSF/trustable folder and ends with .md
if [[ "$file" == TSF/trustable/* && "$file" == *.md ]]; then
# Extract filename without path and extension
filename=$(basename "$file" .md)

# Skip README files
if [[ "$filename" == "README" ]]; then
continue
fi

echo "Checking TSF trustable file: $file (filename: $filename)"

# Check if filename pattern exists in .dotstop.dot
if grep -q "\"$filename\" -> " .dotstop.dot; then
echo " Found reference in .dotstop.dot for: $filename"

# Check if the file contains "score:" substring
if [[ -f "$file" ]] && grep -q "score:" "$file"; then
echo "ERROR: $file - Error: supported statements shall not be scored"
exit 1
fi
else
echo "No reference found in .dotstop.dot for: $filename"
fi
fi
done <<< "$CHANGED_FILES"

echo "All changed TSF items passed validation"

- name: Generate artifact
run: |
mkdir -p SME_review_checker
echo "SME review checker processed for ${{ inputs.artifact_id }}" > SME_review_checker/SME_review_checker.txt

- name: Upload SME review checker artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifact_id }}
path: SME_review_checker/
16 changes: 13 additions & 3 deletions .github/workflows/parent-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ jobs:
with:
artifact_id: "labeler-${{ github.sha }}"

SME_review_checker:
permissions:
contents: read
pull-requests: read
name: Run SME_review_checker Workflow
if: ${{ github.event_name == 'pull_request' }} # only run SME_review_checker for PRs
uses: ./.github/workflows/SME_review_checker.yml
with:
artifact_id: "SME_review_checker-${{ github.sha }}"

check_amalgamation:
name: Run Amalgamation Workflow
if: ${{ github.event_name == 'pull_request' }} # only run check_amalgamation for PRs
Expand Down Expand Up @@ -63,11 +73,11 @@ jobs:
collect_artifacts_pr:
name: "Collect Results & Deploy (PR)"
if: github.event_name == 'pull_request'
needs: [labeler, check_amalgamation, test_trudag_extensions, dependency_review, codeql, ubuntu]
needs: [labeler, SME_review_checker, check_amalgamation, test_trudag_extensions, dependency_review, codeql, ubuntu]
runs-on: ubuntu-latest
strategy:
matrix:
target: [labeler, check_amalgamation, test_trudag_extensions, dependency_review, codeql, ubuntu]
target: [labeler, SME_review_checker, check_amalgamation, test_trudag_extensions, dependency_review, codeql, ubuntu]

steps:
- name: Checkout code
Expand Down Expand Up @@ -96,7 +106,7 @@ jobs:
collect_artifacts_non_pr:
name: "Collect Results & Deploy (Non-PR)"
if: github.event_name != 'pull_request'
needs: [labeler, test_trudag_extensions, codeql, ubuntu] # no check_amalgamation or dependency_review if non PR
needs: [labeler, test_trudag_extensions, codeql, ubuntu] # no check_amalgamation, dependency_review or SME_review_checker if non PR
runs-on: ubuntu-latest
strategy:
matrix:
Expand Down
Loading
Loading