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
20 changes: 20 additions & 0 deletions .github/workflows/checker.yml
Original file line number Diff line number Diff line change
@@ -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@v7

- name: Build and test
run: ./test_runner.sh
68 changes: 68 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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@v7

- 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@v7
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
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions bitmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
{
Expand Down
14 changes: 14 additions & 0 deletions examples/bitmap_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include <iostream>

#include "../bitmap.h"

int main()
{
PixelMatrix pixels(1, std::vector<Pixel>(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;
}
51 changes: 51 additions & 0 deletions scripts/package-release.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash

set -euo pipefail

if [ "$#" -ne 2 ]; then
echo "Usage: $0 <tag> <output-directory>" >&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"
16 changes: 15 additions & 1 deletion test_runner.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
#!/bin/bash

g++ -c bitmap.cpp -o bitmap.o
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"
33 changes: 33 additions & 0 deletions tests/bitmap_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include <cassert>
#include <iostream>

#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<Pixel>(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;
}
Loading