From 9cdbf9a7e3f6cfe20eed19dd0769f8d61c19fe75 Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Tue, 29 Sep 2026 16:27:29 -0700 Subject: [PATCH 1/4] test: add reproducible Bitmap checker Compile the library and a packaged example, and exercise core public behavior. Also guard empty images before inspecting their first row. --- bitmap.cpp | 12 ++++++------ examples/bitmap_example.cpp | 14 ++++++++++++++ test_runner.sh | 16 +++++++++++++++- tests/bitmap_test.cpp | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 examples/bitmap_example.cpp create mode 100644 tests/bitmap_test.cpp diff --git a/bitmap.cpp b/bitmap.cpp index 3aa4030..0d24a43 100644 --- a/bitmap.cpp +++ b/bitmap.cpp @@ -233,16 +233,16 @@ void Bitmap::save(std::string filename) bool Bitmap::isImage() { const int height = pixels.size(); - bool valid = true; + bool valid = height > 0; + int width = 0; - if( height == 0 || pixels[0].size() == 0) + if( valid ) { - valid = false; + width = pixels[0].size(); + valid = width > 0; } - const int width = pixels[0].size(); - - for(int row=0; row < height; row++) + for(int row=0; valid && row < height; row++) { if( pixels[row].size() != width ) { diff --git a/examples/bitmap_example.cpp b/examples/bitmap_example.cpp new file mode 100644 index 0000000..d8ed074 --- /dev/null +++ b/examples/bitmap_example.cpp @@ -0,0 +1,14 @@ +#include + +#include "../bitmap.h" + +int main() +{ + PixelMatrix pixels(1, std::vector(1, Pixel(0, 128, 255))); + Bitmap image; + image.fromPixelMatrix(pixels); + + std::cout << "Created a " << image.toPixelMatrix().size() + << "x" << image.toPixelMatrix()[0].size() << " bitmap.\n"; + return image.isImage() ? 0 : 1; +} diff --git a/test_runner.sh b/test_runner.sh index 973a36f..166a218 100755 --- a/test_runner.sh +++ b/test_runner.sh @@ -1,3 +1,17 @@ #!/bin/bash -g++ -c bitmap.cpp -o bitmap.o \ No newline at end of file +set -euo pipefail + +project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +build_dir="$(mktemp -d "${TMPDIR:-/tmp}/bitmap-tests.XXXXXX")" +trap 'rm -rf "$build_dir"' EXIT + +cxx="${CXX:-g++}" +compile_flags=(-std=c++11 -Wall -Wextra -Wpedantic) + +"$cxx" "${compile_flags[@]}" -c "$project_root/bitmap.cpp" -o "$build_dir/bitmap.o" +"$cxx" "${compile_flags[@]}" "$project_root/tests/bitmap_test.cpp" \ + "$project_root/bitmap.cpp" -o "$build_dir/bitmap_test" +"$build_dir/bitmap_test" +"$cxx" "${compile_flags[@]}" "$project_root/examples/bitmap_example.cpp" \ + "$project_root/bitmap.cpp" -o "$build_dir/bitmap_example" diff --git a/tests/bitmap_test.cpp b/tests/bitmap_test.cpp new file mode 100644 index 0000000..7f2a711 --- /dev/null +++ b/tests/bitmap_test.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include "../bitmap.h" + +int main() +{ + Pixel purple(128, 0, 255); + assert(purple.red == 128); + assert(purple.green == 0); + assert(purple.blue == 255); + + Bitmap image; + assert(!image.isImage()); + + PixelMatrix pixels(2, std::vector(3, Pixel(1, 2, 3))); + image.fromPixelMatrix(pixels); + assert(image.isImage()); + assert(image.toPixelMatrix().size() == 2); + assert(image.toPixelMatrix()[0].size() == 3); + + pixels[1].push_back(Pixel()); + image.fromPixelMatrix(pixels); + assert(!image.isImage()); + + pixels.resize(1); + pixels[0][0] = Pixel(256, 0, 0); + image.fromPixelMatrix(pixels); + assert(!image.isImage()); + + std::cout << "Bitmap tests passed\n"; + return 0; +} From fc2868aa8a5284998b06b413d53c5bf826afdbd2 Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Tue, 29 Sep 2026 16:28:00 -0700 Subject: [PATCH 2/4] build: package Linux release artifacts Create a verified four-file source archive alongside Linux static-library and example artifacts for a release tag. --- scripts/package-release.sh | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100755 scripts/package-release.sh diff --git a/scripts/package-release.sh b/scripts/package-release.sh new file mode 100755 index 0000000..673e309 --- /dev/null +++ b/scripts/package-release.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -euo pipefail + +if [ "$#" -ne 2 ]; then + echo "Usage: $0 " >&2 + exit 64 +fi + +tag="$1" +if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then + echo "Release tag must be vMAJOR.MINOR or vMAJOR.MINOR.PATCH." >&2 + exit 64 +fi + +project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +output_dir="$2" +mkdir -p "$output_dir" +output_dir="$(cd "$output_dir" && pwd)" +build_dir="$(mktemp -d "${TMPDIR:-/tmp}/bitmap-release.XXXXXX")" +trap 'rm -rf "$build_dir"' EXIT + +cxx="${CXX:-g++}" +compile_flags=(-std=c++11 -O2) +source_archive="$output_dir/bitmap-${tag}-source.zip" +static_library="$output_dir/libbitmap-${tag}-linux-x86_64.a" +example_binary="$output_dir/bitmap-example-${tag}-linux-x86_64" + +rm -f "$source_archive" "$static_library" "$example_binary" + +( + cd "$project_root" + zip -q -j "$source_archive" bitmap.h bitmap.cpp LICENSE README.md +) + +expected_archive_contents=$'LICENSE\nREADME.md\nbitmap.cpp\nbitmap.h' +actual_archive_contents="$(zipinfo -1 "$source_archive" | LC_ALL=C sort)" +if [ "$actual_archive_contents" != "$expected_archive_contents" ]; then + echo "Source archive contains unexpected files." >&2 + exit 1 +fi + +"$cxx" "${compile_flags[@]}" -c "$project_root/bitmap.cpp" -o "$build_dir/bitmap.o" +ar rcs "$static_library" "$build_dir/bitmap.o" +"$cxx" "${compile_flags[@]}" "$project_root/examples/bitmap_example.cpp" \ + "$static_library" -o "$example_binary" +"$example_binary" + +test -s "$source_archive" +test -s "$static_library" +test -x "$example_binary" From 97f772651519f9ac8eb26e076cb7407a8763936a Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Tue, 29 Sep 2026 16:28:45 -0700 Subject: [PATCH 3/4] ci: publish tagged Linux releases Run the checker on pull requests and manual dispatches, then gate tagged release publication on a passing check and master ancestry. --- .github/workflows/checker.yml | 20 +++++++++++ .github/workflows/release.yml | 68 +++++++++++++++++++++++++++++++++++ README.md | 25 +++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 .github/workflows/checker.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/checker.yml b/.github/workflows/checker.yml new file mode 100644 index 0000000..c266085 --- /dev/null +++ b/.github/workflows/checker.yml @@ -0,0 +1,20 @@ +name: Checker + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + check: + name: Build and test + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Build and test + run: ./test_runner.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bb9eeea --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,68 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: read + +jobs: + check: + name: Build and test + runs-on: ubuntu-latest + + steps: + - name: Check out tagged source + uses: actions/checkout@v4 + + - name: Build and test + run: ./test_runner.sh + + release: + name: Package and publish + needs: check + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Check out tagged source and history + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Validate release tag + env: + TAG: ${{ github.ref_name }} + run: | + if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then + echo "Release tags must be vMAJOR.MINOR or vMAJOR.MINOR.PATCH." >&2 + exit 1 + fi + + - name: Verify tag is on master + env: + TAG: ${{ github.ref_name }} + run: | + git fetch origin master:refs/remotes/origin/master + tag_commit="$(git rev-list -n 1 "$TAG")" + git merge-base --is-ancestor "$tag_commit" origin/master + + - name: Package release assets + env: + TAG: ${{ github.ref_name }} + run: ./scripts/package-release.sh "$TAG" release-artifacts + + - name: Publish GitHub release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + run: | + assets=(release-artifacts/*) + if gh release view "$TAG" >/dev/null 2>&1; then + gh release upload "$TAG" "${assets[@]}" --clobber + else + gh release create "$TAG" "${assets[@]}" --generate-notes --verify-tag + fi diff --git a/README.md b/README.md index 5c8e533..921eeec 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,31 @@ This repo is compatible with the [cpp-container](https://github.com/ChicoState/cpp-container) Docker container. +## Development and releases + +Run the local build and test checker with: + +``` +./test_runner.sh +``` + +The Checker GitHub Actions workflow runs this command for pull requests and +manual dispatches. Release tags run the same checker before publication. + +To publish a Linux release, first merge the intended commit into `master`, then +push a version tag. The initial release tag is `v0.1`; later releases may use +`vMAJOR.MINOR` or `vMAJOR.MINOR.PATCH` tags. + +``` +git tag -a v0.1 -m "Release v0.1" +git push origin v0.1 +``` + +The release workflow rejects tags whose commits are not reachable from +`master`. It publishes a source ZIP containing only `bitmap.h`, `bitmap.cpp`, +`LICENSE`, and `README.md`, plus Linux x86_64 static-library and example-binary +assets. + ## Getting Started 1. Clone this repository onto your development environment From 9a4d94670fd7b9da3e31392d622c79c2ea8f4a71 Mon Sep 17 00:00:00 2001 From: Kevin Buffardi Date: Tue, 29 Sep 2026 16:37:59 -0700 Subject: [PATCH 4/4] ci: upgrade checkout action to v7 Use the current checkout major release across PR and release workflows. --- .github/workflows/checker.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/checker.yml b/.github/workflows/checker.yml index c266085..25a7d88 100644 --- a/.github/workflows/checker.yml +++ b/.github/workflows/checker.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Check out repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Build and test run: ./test_runner.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb9eeea..80e63cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Check out tagged source - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Build and test run: ./test_runner.sh @@ -29,7 +29,7 @@ jobs: steps: - name: Check out tagged source and history - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0