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
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
20 changes: 20 additions & 0 deletions .github/scripts/check-c.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Compile every production translation unit; warnings are CI failures.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
compiler=${CC:-gcc}
flags=(-std=c23 -O1 -Wall -Wextra -Werror -Iinclude -Isrc -Ilib/include -Ilib/src)
case "${1:-warnings}" in
warnings) ;;
analyze) flags+=(-fanalyzer -fno-analyzer-state-merge) ;;
*) echo "Usage: bash .github/scripts/check-c.sh [warnings|analyze]" >&2; exit 2 ;;
esac
mkdir -p build/ci
object_dir=$(mktemp -d "$PWD/build/ci/check-c.XXXXXX")
trap 'rm -rf "$object_dir"' EXIT
mapfile -d '' sources < <(git ls-files -z -- 'src/*.c' 'lib/src/*.c')
(( ${#sources[@]} > 0 ))
for source in "${sources[@]}"; do
echo "Checking $source"
"$compiler" "${flags[@]}" -c "$source" -o "$object_dir/${source//\//_}.o"
done
13 changes: 13 additions & 0 deletions .github/scripts/configure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Configure the local build entry point for CI or an equivalent local run.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
compiler_path=$(command -v "${CC:-gcc}")
case "$(basename "$compiler_path")" in
gcc*) backend=GCC ;;
clang*) backend=CLANG ;;
*) echo "CC must select GCC or Clang" >&2; exit 2 ;;
esac
sed -i '/^compiler=/d; /^compiler_path=/d; /^sanitize=/d' build.conf
printf '\ncompiler=%s\ncompiler_path=%s\nsanitize=1\n' "$backend" "$compiler_path" >> build.conf
"$compiler_path" --version
20 changes: 20 additions & 0 deletions .github/scripts/install-tools.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Pinned CLI releases, verified before executing; no GitHub secrets required.
set -euo pipefail
destination=${1:?Pass an installation directory}
mkdir -p "$destination"
destination=$(cd "$destination" && pwd)
download_dir=$(mktemp -d)
trap 'rm -rf "$download_dir"' EXIT
install_release() {
local url=$1 checksum=$2 executable=$3
curl --fail --location --silent --show-error --retry 3 "$url" -o "$download_dir/archive.tar.gz"
printf '%s %s\n' "$checksum" "$download_dir/archive.tar.gz" | sha256sum --check
tar -xzf "$download_dir/archive.tar.gz" -C "$destination" "$executable"
}
install_release \
https://github.com/rhysd/actionlint/releases/download/v1.7.7/actionlint_1.7.7_linux_amd64.tar.gz \
023070a287cd8cccd71515fedc843f1985bf96c436b7effaecce67290e7e0757 actionlint
install_release \
https://github.com/gitleaks/gitleaks/releases/download/v8.24.2/gitleaks_8.24.2_linux_x64.tar.gz \
fa0500f6b7e41d28791ebc680f5dd9899cd42b58629218a5f041efa899151a8e gitleaks
184 changes: 184 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

defaults:
run:
shell: bash

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
ASAN_OPTIONS: detect_leaks=1:halt_on_error=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1

jobs:
build-test:
name: Build and test (${{ matrix.cc }})
runs-on: ubuntu-24.04
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
cc: [gcc-14, clang-19]
env:
CC: ${{ matrix.cc }}
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Install both supported toolchains
timeout-minutes: 5
run: |
# The hosted image's Azure mirror can stall on LLVM package downloads.
if test -f /etc/apt/apt-mirrors.txt; then
printf '%s\n' 'https://archive.ubuntu.com/ubuntu' | sudo tee /etc/apt/apt-mirrors.txt > /dev/null
fi
sudo apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 update
sudo env DEBIAN_FRONTEND=noninteractive apt-get \
-o DPkg::Lock::Timeout=120 -o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 -o Acquire::https::Timeout=30 \
install --no-install-recommends -y gcc-14 clang-19 llvm-19 libclang-rt-19-dev binutils pkg-config libpcre2-dev
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
sudo update-alternatives --set gcc /usr/bin/gcc-14
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 190
sudo update-alternatives --set clang /usr/bin/clang-19
- name: Verify compiler versions used by generated builds
run: |
test "$(gcc -dumpversion | cut -d. -f1)" = 14
test "$(clang -dumpversion | cut -d. -f1)" = 19
- name: Configure the selected compiler
run: bash .github/scripts/configure.sh
- name: Reject compiler warnings
run: bash .github/scripts/check-c.sh
- name: Check out pinned Forge build dependency
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
repository: minelogy-dev/forge
ref: b6826ba762a1e9e337895e01ee604b943c682d23
path: .ci-deps/forge
persist-credentials: false
- name: Bootstrap Forge without installing system files
env:
CC: gcc
run: |
sh .ci-deps/forge/bootstrap.sh --no-test
echo "$GITHUB_WORKSPACE/.ci-deps/forge/build/output" >> "$GITHUB_PATH"
echo "CPATH=$GITHUB_WORKSPACE/.ci-deps/forge/build/output/include" >> "$GITHUB_ENV"
- name: Build the shared library with the real build script
run: |
forge .
./make
- name: Run the complete sanitizer suite
run: ./make test -j 2 2>&1 | tee build/ci-tests.log
- name: Validate installation, pkg-config and shared-library exports
run: |
./make install "$RUNNER_TEMP/stringx-install"
export PKG_CONFIG_PATH="$RUNNER_TEMP/stringx-install/lib/pkgconfig"
# Link the existing regex tests to the installed shared library.
read -r -a pkg_flags <<< "$(pkg-config --cflags --libs libstringx)"
"$CC" -std=c23 -fsanitize=address,undefined -fno-sanitize-recover=all \
-fno-omit-frame-pointer tests/reg.c \
"${pkg_flags[@]}" \
-Wl,-rpath,"$RUNNER_TEMP/stringx-install/lib" -o build/linked-reg
./build/linked-reg
./make gen_deb
dpkg-deb --info build/deb/libstringx_*.deb
dpkg-deb --contents build/deb/libstringx_*.deb
- name: Keep diagnostics after a failure
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: diagnostics-${{ matrix.cc }}
path: |
build/ci-tests.log
build/**/*.d
build/**/*.meta
test/edge_*/build.conf
test/edge_*/build.c
if-no-files-found: ignore
retention-days: 7

static-analysis:
name: GCC static analysis
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Install analyzer dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y gcc-14 libpcre2-dev
- name: Check production code for unsafe memory and resource use
env:
CC: gcc-14
run: |
mkdir -p build
bash .github/scripts/check-c.sh analyze 2>&1 | tee build/ci-analyzer.log
- name: Keep analyzer diagnostics after a failure
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: analyzer-diagnostics
path: build/ci-analyzer.log
if-no-files-found: ignore
retention-days: 7

quality:
name: Workflow and secret checks
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
fetch-depth: 0
persist-credentials: false
- name: Install ShellCheck and pinned security tools
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y shellcheck
bash .github/scripts/install-tools.sh "$RUNNER_TEMP/ci-tools"
- name: Lint workflows and helper scripts
run: |
"$RUNNER_TEMP/ci-tools/actionlint" -color
shellcheck .github/scripts/*.sh
- name: Scan the complete Git history for credentials
run: '"$RUNNER_TEMP/ci-tools/gitleaks" git --redact --verbose'
- name: Reject whitespace errors in the proposed changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.sha }}
run: |
if test -n "$BASE_SHA" && git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
git diff --check "$BASE_SHA" "$HEAD_SHA"
else
git show --format= --check "$HEAD_SHA"
fi

ci:
name: CI
if: always()
needs: [build-test, static-analysis, quality]
runs-on: ubuntu-24.04
timeout-minutes: 2
steps:
- name: Require every check to pass
env:
BUILD_TEST: ${{ needs.build-test.result }}
STATIC_ANALYSIS: ${{ needs.static-analysis.result }}
QUALITY: ${{ needs.quality.result }}
run: |
test "$BUILD_TEST" = success
test "$STATIC_ANALYSIS" = success
test "$QUALITY" = success
67 changes: 67 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: CodeQL

on:
push:
branches: [main]
pull_request:
schedule:
- cron: '17 3 * * 1'
workflow_dispatch:

permissions:
contents: read

concurrency:
group: codeql-${{ github.ref }}
cancel-in-progress: true

jobs:
analyze:
name: CodeQL (C/C++)
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
security-events: write
actions: read
steps:
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y gcc-14 binutils pkg-config libpcre2-dev
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100
- name: Check out pinned Forge build dependency
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
repository: minelogy-dev/forge
ref: b6826ba762a1e9e337895e01ee604b943c682d23
path: .ci-deps/forge
persist-credentials: false
- name: Bootstrap Forge without installing system files
env:
CC: gcc
run: |
sh .ci-deps/forge/bootstrap.sh --no-test
echo "$GITHUB_WORKSPACE/.ci-deps/forge/build/output" >> "$GITHUB_PATH"
echo "CPATH=$GITHUB_WORKSPACE/.ci-deps/forge/build/output/include" >> "$GITHUB_ENV"
- name: Configure the build compiler
env:
CC: gcc
run: bash .github/scripts/configure.sh
- name: Initialize security and maintainability queries
uses: github/codeql-action/init@793b778da9c9275a0f276522b27cb5fc14bba279 # v4
with:
languages: c-cpp
build-mode: manual
queries: security-and-quality
- name: Build production code for analysis
run: |
forge .
./make
- name: Publish findings to code scanning and the pull request
uses: github/codeql-action/analyze@793b778da9c9275a0f276522b27cb5fc14bba279 # v4
with:
category: /language:c-cpp
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/make
/build/
/test/
/.ci-deps/
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ Contract highlights (full detail in `sx.h`):

## Tests

Pull requests run compiler, sanitizer, static-analysis and secret checks.
See [Continuous integration](docs/CI.md) for coverage and merge-protection setup.

```sh
./make test
```
Expand All @@ -131,4 +134,4 @@ the exact behavior boundaries it locks in.

MIT — see [LICENSE](LICENSE) and the SPDX headers in the source files. The
regex layer depends on **PCRE2** (BSD-3-Clause); its attribution and license
are in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
are in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
5 changes: 4 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ sx_reg_free(re);

## 测试

PR 会自动执行编译、内存检查、静态分析和密钥扫描。
检查范围与分支保护设置见[持续集成说明](docs/CI.md)。

```sh
./make test
```
Expand All @@ -124,4 +127,4 @@ sx_reg_free(re);

MIT —— 见 [LICENSE](LICENSE) 及源码文件中的 SPDX 标注。正则层依赖
**PCRE2**(BSD-3-Clause),其归属与许可全文见
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md)。
Loading
Loading