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
35 changes: 35 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# EditorConfig houdt de opmaak gelijk tussen editors en IDE's.
# VS Code, JetBrains en anderen lezen dit bestand vanzelf.
# Meer info: https://editorconfig.org
#
# Deze versie is organisatiebreed gelijk. Wijk hier niet per repository van af:
# vijf licht verschillende varianten leverden alleen ruis op, geen voordeel.
#
# De configbestanden die zelf geen comments kunnen dragen omdat het JSON is,
# staan hier genoemd zodat er ergens een aanwijzing is wat ze doen:
#
# .htmlhintrc - HTML-linting (HTMLHint). Structuur- en toegankelijkheidsregels.
# renovate.json - Renovate-bot. Automatische dependency-updates via pull requests.

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.md]
# Trailing whitespace betekent iets in Markdown (regeleinde)
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

[*.py]
indent_size = 4

[*.sh]
indent_size = 4
24 changes: 24 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# .gitattributes zorgt voor consistente regeleindes tussen Windows, Mac en Linux.
# Zonder dit kunnen regeleindes per ontwikkelaar of OS verschillen, wat leidt
# tot onnodige git-diffs en merge-conflicten.
#
# Deze versie is organisatiebreed gelijk. Wijk hier niet per repository van af:
# vier licht verschillende varianten leverden alleen ruis op, geen voordeel.

# Standaard: forceer LF voor alle tekstbestanden
* text=auto eol=lf

# Binaire bestanden: geen regeleindeconversie
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.avif binary
*.pdf binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.zip binary
23 changes: 16 additions & 7 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@

## Type of change

- [ ] Bug fix
- [ ] New feature / script
- [ ] Refactor / cleanup
- [ ] CI / workflow change
- [ ] Documentation
- [ ] `feat` — new feature or script
- [ ] `fix` — bug fix
- [ ] `content` — update or improve the site's landing page
- [ ] `docs` — changes to README, CONTRIBUTING, or meta documentation
- [ ] `chore` — maintenance (dependencies, config, CI/CD)
- [ ] `refactor` — restructuring without behaviour changes
- [ ] `style` — formatting, whitespace, typos
- [ ] `revert` — reverting a previous commit

## Testing
> [PR title and commit types must follow these standards — view the contributing guide](https://github.com/THectic-NL/WinDeploy/blob/main/CONTRIBUTING.md#commit-messages)

- [ ] Tested on Windows 11 25H2
## Checklist

- [ ] PR title follows the commit convention
- [ ] Tested on a real Windows 11 25H2 machine (required for anything under `Scripts/` or `Docs/` — CI cannot exercise BitLocker, Sysprep, WinGet or Defender)
- [ ] PSScriptAnalyzer passes locally
- [ ] No hardcoded IPs, credentials, or company-specific data
- [ ] Both EN (`*.md`) and NL (`*.nl.md`) versions updated, if `src/content/` changed
- [ ] No broken internal links, if `src/` changed
- [ ] Site tested locally with `cd src && hugo server`, if `src/` changed

## Notes

Expand Down
74 changes: 74 additions & 0 deletions .github/scripts/check-renovate-patterns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# Copyright (C) 2026 Sten Tijhuis
# SPDX-License-Identifier: MIT
"""Flag Renovate file patterns that look like a regex but are not delimited.

managerFilePatterns and matchFileNames accept "RegEx (re2) and glob patterns".
A value counts as a regex only when it is wrapped in slashes; everything else
is read as a glob. So a pattern like

"^\\.github/workflows/.*\\.ya?ml$"

matches no file at all, and the custom manager around it never fires. Nothing
reports this: renovate-config-validator says the config is valid, because it
is -- it just silently does nothing. The only visible symptom is a dependency
that stops receiving updates, which is easy to miss for months.

Usage: check-renovate-patterns.py [config.json ...]
Missing files are skipped, so the same call works in every repository.
"""
import json
import pathlib
import re
import sys

# Constructs that carry meaning in a regex but not in a glob.
REGEXY = re.compile(r"^\^|\$$|\\\.|\.\*|\.\+|\(\?|\[\^|\\d|\\w|\\s|[)?]\|")

# Renovate options whose values are matched as "regex or glob".
PATTERN_KEYS = {
"managerFilePatterns",
"matchFileNames",
"fileMatch",
"matchPackageNames",
}

problems = []


def walk(node, path, source):
if isinstance(node, dict):
for key, value in node.items():
if key in PATTERN_KEYS and isinstance(value, list):
for index, pattern in enumerate(value):
if not isinstance(pattern, str):
continue
# A trailing "i" flag is allowed: /pattern/i
delimited = pattern.startswith("/") and pattern.rstrip("i").endswith("/")
if REGEXY.search(pattern) and not delimited:
problems.append((source, f"{path}.{key}[{index}]", pattern))
walk(value, f"{path}.{key}", source)
elif isinstance(node, list):
for index, item in enumerate(node):
walk(item, f"{path}[{index}]", source)


files = [path for path in (pathlib.Path(a) for a in sys.argv[1:]) if path.is_file()]
if not files:
print("No Renovate config found to check.")
sys.exit(0)

for config in files:
walk(json.loads(config.read_text()), "$", str(config))

if problems:
print("Renovate file patterns that look like a regex but are not wrapped in slashes.")
print("Renovate reads these as globs, so they match nothing and the rule never fires.\n")
for source, where, pattern in problems:
print(f"::error file={source}::{where}: {pattern!r} is read as a glob, not a regex")
print(f" {source} {where}")
print(f" found: {pattern!r}")
print(f" expect: '/{pattern}/'\n")
sys.exit(1)

print(f"Checked {len(files)} Renovate config file(s): all file patterns are well formed.")
158 changes: 158 additions & 0 deletions .github/scripts/update-tool-checksums.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#!/usr/bin/env bash
#
# Recalculate and optionally apply the SHA-256 checksums of the pinned CI tools.
#
# Renovate bumps the version numbers but cannot compute a checksum, so without
# this the pinned hash keeps pointing at the previous release and every bump
# fails the build with "computed checksum did NOT match". The workflow in
# .github/workflows/update-checksums.yml runs this on Renovate's own pull
# requests and commits the result back onto the branch.
#
# The hash is not simply taken from whatever the download happened to return.
# Each project publishes its own checksum file next to the release; the
# download is verified against that first, and only a verified hash is written
# into the repository.
#
# Usage:
# .github/scripts/update-tool-checksums.sh # show, then ask
# .github/scripts/update-tool-checksums.sh --apply # write without asking
#

set -euo pipefail

readonly RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m'

Write-Log() {
local level=$1; shift
local color=$NC
case $level in
INFO) color=$BLUE ;;
SUCCESS) color=$GREEN ;;
WARN) color=$YELLOW ;;
ERROR) color=$RED ;;
esac
if [[ $level == ERROR ]]; then
echo -e "${color}[$level]${NC} $*" >&2
else
echo -e "${color}[$level]${NC} $*"
fi
}

Stop-Script() {
Write-Log ERROR "$1"
exit 1
}

Show-Usage() {
cat <<'EOF'
Usage: update-tool-checksums.sh [--apply]

Options:
--apply Write the checksums without prompting
-h, --help Show this help
EOF
}

APPLY=false
while [[ $# -gt 0 ]]; do
case "$1" in
--apply) APPLY=true; shift ;;
-h|--help) Show-Usage; exit 0 ;;
*) Write-Log ERROR "Unknown argument: $1"; Show-Usage; exit 1 ;;
esac
done

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
readonly REPO_ROOT
cd "$REPO_ROOT"

readonly CONFIG_VALIDATION=".github/workflows/config-validation.yml"
readonly PR_CHECKS=".github/workflows/pr-checks.yml"

# ── Reading and writing the pinned values ───────────────────────────────────

# Usage: Get-KeyValue <file> <KEY> -> value of `KEY: "value"`
Get-KeyValue() {
sed -n "s/^[[:space:]]*$2:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$1" | head -n1
}

# Usage: Set-KeyValue <file> <KEY> <value>
Set-KeyValue() {
sed -i "s|^\([[:space:]]*$2:[[:space:]]*\"\)[^\"]*\"|\1$3\"|" "$1"
}

# ── Fetching and verifying ──────────────────────────────────────────────────

TEMP_DIR="$(mktemp -d)"
trap 'rm -rf -- "$TEMP_DIR"' EXIT

# Usage: Get-VerifiedHash <name> <artifact-url> <expected-sha256>
# Downloads the artifact, checks it against the hash the project published, and
# echoes that hash. Refuses to return anything if the two disagree.
Get-VerifiedHash() {
local name=$1 url=$2 expected=$3
local file="$TEMP_DIR/$name"

[[ "$expected" =~ ^[a-f0-9]{64}$ ]] || Stop-Script "$name: no valid checksum published upstream (got: '$expected')"

curl -sSL --fail-with-body --retry 5 --retry-delay 3 --retry-all-errors -o "$file" "$url" \
|| Stop-Script "$name: download failed ($url)"

local actual
actual="$(sha256sum "$file" | awk '{print $1}')"

if [[ "$actual" != "$expected" ]]; then
Stop-Script "$name: download does not match the published checksum. published=$expected downloaded=$actual"
fi

echo "$actual"
}

# Usage: Get-PublishedHash <url> <grep-pattern>
# Pulls one line out of a checksums file and returns the hash on it.
Get-PublishedHash() {
curl -sSL --fail-with-body --retry 5 --retry-delay 3 --retry-all-errors "$1" \
| grep -- "$2" | awk '{print $1}' | head -n1
}

# ── The tools ───────────────────────────────────────────────────────────────

ACTIONLINT_VERSION="$(Get-KeyValue "$CONFIG_VALIDATION" ACTIONLINT_VERSION)"
LYCHEE_VERSION="$(Get-KeyValue "$PR_CHECKS" LYCHEE_VERSION)"

for pair in "actionlint:$ACTIONLINT_VERSION" "lychee:$LYCHEE_VERSION"; do
[[ -n "${pair#*:}" ]] || Stop-Script "Could not read the ${pair%%:*} version. Did the file layout change?"
done

Write-Log INFO "Versions found in the repository:"
echo " actionlint: $ACTIONLINT_VERSION"
echo " lychee: $LYCHEE_VERSION"
echo

Write-Log INFO "Downloading and verifying against the published checksums..."

ACTIONLINT_SHA256="$(Get-VerifiedHash "actionlint.tar.gz" \
"https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" \
"$(Get-PublishedHash "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_checksums.txt" "linux_amd64.tar.gz")")"
Write-Log SUCCESS "actionlint: $ACTIONLINT_SHA256"

LYCHEE_SHA256="$(Get-VerifiedHash "lychee.tar.gz" \
"https://github.com/lycheeverse/lychee/releases/download/lychee-v${LYCHEE_VERSION}/lychee-x86_64-unknown-linux-gnu.tar.gz" \
"$(Get-PublishedHash "https://github.com/lycheeverse/lychee/releases/download/lychee-v${LYCHEE_VERSION}/lychee-x86_64-unknown-linux-gnu.tar.gz.sha256" "")")"
Write-Log SUCCESS "lychee: $LYCHEE_SHA256"

echo
if [[ "$APPLY" != true ]]; then
read -rp "Write these checksums into the repository? [y/N] " response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
Write-Log INFO "No changes made"
exit 0
fi
fi

Set-KeyValue "$CONFIG_VALIDATION" ACTIONLINT_SHA256 "$ACTIONLINT_SHA256"
Set-KeyValue "$PR_CHECKS" LYCHEE_SHA256 "$LYCHEE_SHA256"

Write-Log SUCCESS "Updated:"
echo " - $CONFIG_VALIDATION"
echo " - $PR_CHECKS"
44 changes: 0 additions & 44 deletions .github/workflows/codeql.yml

This file was deleted.

Loading
Loading