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
91 changes: 91 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: CD

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# A tag that fails its own tests should not become a release.
- name: Build strum
run: |
git clone --depth 1 https://github.com/Strongleong/Strum strum
cd strum
cc build.c -o build
./build -c gcc
echo "$PWD/out" >> "$GITHUB_PATH"

- name: Build and test
run: |
cc build.c -o build
./build
./build tests

# The tag and the version macros have to agree, or people who check
# OPTLY_VERSION_NUMBER at compile time get a different answer from people
# who read the release page.
- name: Check the version matches the tag
run: |
major=$(sed -n 's/^#define OPTLY_VERSION_MAJOR *//p' optly.h | tr -d ' ')
minor=$(sed -n 's/^#define OPTLY_VERSION_MINOR *//p' optly.h | tr -d ' ')
patch=$(sed -n 's/^#define OPTLY_VERSION_RELEASE *//p' optly.h | tr -d ' ')
header="v${major}.${minor}.${patch}"
tag="${GITHUB_REF_NAME}"

echo "header says $header, tag says $tag"

if [ "$header" != "$tag" ]; then
echo "::error::optly.h is $header but the tag is $tag"
exit 1
fi

# The version also appears in the banner comment at the top of the file,
# which is what people actually read when they open a vendored copy.
- name: Check the banner matches the tag
run: |
banner=$(sed -n 's/^ optly\.h — v//p' optly.h | head -1 | tr -d ' ')
tag="${GITHUB_REF_NAME#v}"

echo "banner says $banner, tag says $tag"

if [ "$banner" != "$tag" ]; then
echo "::error::the banner in optly.h says v$banner but the tag is v$tag"
exit 1
fi

# The changelog should describe the tag being cut, not still say
# Upcoming.
- name: Check the changelog has an entry for this tag
run: |
if ! grep -q "^## ${GITHUB_REF_NAME}$" CHANGELOG.md; then
echo "::error::CHANGELOG.md has no '## ${GITHUB_REF_NAME}' heading"
exit 1
fi

# optly.h is the artifact. Shipping it as a release asset means a user
# can pin a version without cloning or trusting a moving branch.
- name: Package
run: |
mkdir -p dist
cp optly.h LICENSE README.md CHANGELOG.md dist/
tar -czf "optly-${GITHUB_REF_NAME}.tar.gz" -C dist .
sha256sum optly.h "optly-${GITHUB_REF_NAME}.tar.gz" > checksums.txt

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
optly.h
optly-${{ github.ref_name }}.tar.gz
checksums.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
157 changes: 157 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: CI

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

jobs:
# The .tspec suite is the real check. strum is POSIX-only, so this is
# everywhere it can run.
tests:
name: tests (${{ matrix.os }}, ${{ matrix.cc }}, ${{ matrix.std }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
cc: [gcc, clang]
std: [c99, c11, c17]
steps:
- uses: actions/checkout@v4

# Built from source rather than pulled from a release, so this does not
# depend on an asset existing for the runner's platform.
- name: Build strum
run: |
git clone --depth 1 https://github.com/Strongleong/Strum strum
cd strum
cc build.c -o build
./build -c ${{ matrix.cc }}
echo "$PWD/out" >> "$GITHUB_PATH"

- name: Build examples
run: |
cc build.c -o build
./build -c ${{ matrix.cc }} --std ${{ matrix.std }}

- name: Run tests
run: ./build tests

# Windows has no strum, so these jobs answer a narrower question: does the
# header compile, and does a program built with it behave. Each step asks one
# thing, so a red run names its own cause instead of leaving three
# candidates. Commands are echoed and output is compared against exact bytes;
# a green tick here means something specific happened.
mingw:
name: windows (mingw, ${{ matrix.std }})
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
std: [c99, c11, c17]
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4

- name: Compile the header on its own
run: |
set -x
gcc -std=${{ matrix.std }} -Wall -Wextra -Werror -pedantic \
-DOPTLY_IMPLEMENTATION -I. -c -x c optly.h -o header.o

- name: Compile the build tool
run: |
set -x
gcc -std=${{ matrix.std }} -Wall -Wextra -Werror -pedantic build.c -o build.exe

- name: Build the examples with it
run: |
set -x
./build.exe -c gcc --std ${{ matrix.std }}
ls -l out

- name: A program built with optly parses its command line
run: |
set -x
gcc -std=${{ matrix.std }} -Wall -Wextra -Werror -pedantic \
-I. tests/flags/flags.c -o flags.exe
./flags.exe -vqf --threads=16 --out=bin/app > got.txt
printf 'verbose=1\nquiet=1\nforce=1\nthreads=16\nout=bin/app\n' > want.txt
diff -u want.txt got.txt

# MSVC is the toolchain optly has never been built with. /std:c11 is not
# optional: the whole DSL is designated initializers and compound literals,
# and MSVC rejects both in its default C mode.
msvc:
name: windows (msvc)
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: ilammy/msvc-dev-cmd@v1

- name: Compile the header on its own
shell: cmd
run: |
echo on
cl /W4 /WX /std:c11 /TC /DOPTLY_IMPLEMENTATION /I. /c optly.h /Fo:header.obj

- name: Compile the build tool
shell: cmd
run: |
echo on
cl /W4 /WX /std:c11 /TC build.c /Fe:build.exe

- name: Build the examples with it
shell: cmd
run: |
echo on
build.exe -c cl --std c11
dir out

- name: A program built with optly parses its command line
shell: cmd
run: |
echo on
cl /W4 /WX /std:c11 /TC /I. tests\flags\flags.c /Fe:flags.exe
flags.exe -vqf --threads=16 --out=bin/app > got.txt
type got.txt

- name: Compare that output against the expected bytes
shell: bash
run: |
printf 'verbose=1\nquiet=1\nforce=1\nthreads=16\nout=bin/app\n' > want.txt
diff -u --strip-trailing-cr want.txt got.txt

sanitizers:
name: sanitizers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build strum
run: |
git clone --depth 1 https://github.com/Strongleong/Strum strum
cd strum
cc build.c -o build
./build -c gcc
echo "$PWD/out" >> "$GITHUB_PATH"

# The over-long --flag=value crash fixed in v2.4.0 was a NULL write that
# a normal build can miss. ASan is how that class gets caught next time.
- name: Test fixtures under AddressSanitizer
run: |
for fixture in tests/*/*.c; do
gcc -std=c99 -Wall -Wextra -pedantic -fsanitize=address,undefined \
-I. "$fixture" -o /tmp/fixture
/tmp/fixture >/dev/null 2>&1 || true
done

- name: Tests
run: |
cc build.c -o build
./build tests
36 changes: 0 additions & 36 deletions .github/workflows/main.yml

This file was deleted.

7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## Upcoming

### Added

- The last flag in a batch of short flags can now take a value, the way tar
spells `-xzvf archive.tar`. So `-vqo out.txt` works. Everything before the
last flag still has to be boolean, since a flag in the middle has no way to
say where its value ends.

## v2.4.0

### Added
Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,12 @@ Equivalent to:

-a -b -c

*(Batched flags must be boolean.)*
The last flag in a batch may take a value, the way tar does it:

tar -xzvf archive.tar

Everything before the last one must be boolean -- a flag in the middle has no
way to say where its value ends.

## Commands

Expand Down
Loading
Loading