Skip to content
Open
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
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
infra/** linguist-vendored
cookiecutter/** linguist-vendored
template/** linguist-vendored
copier/** linguist-vendored
*.bib -linguist-detectable
*.tex -linguist-detectable
papers/* linguist-documentation
12 changes: 6 additions & 6 deletions .github/workflows/catch2_exemplar_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ jobs:
uses: actions/checkout@v6
- name: Test catch2 exemplar
run: |
cd cookiecutter
source ./check_cookiecutter.sh
cookiecutter_venv_path=$(mktemp --directory --dry-run)
setup_venv "$cookiecutter_venv_path"
stamp "$PWD" "./catch2_exemplar" "catch2" "true"
cd catch2_exemplar/exemplar
cd copier
source ./check_copier.sh
copier_venv_path=$(mktemp --directory --dry-run)
setup_venv "$copier_venv_path"
stamp "$repo_root" "./catch2_exemplar" "catch2" "true"
cd catch2_exemplar
cmake -B build -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=$PWD/dist
cmake --build build
ctest --test-dir build
Expand Down
55 changes: 55 additions & 0 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,61 @@ jobs:
beman-submodule-check:
uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.7.3

copier-test:
runs-on: ubuntu-latest
container:
image: ghcr.io/bemanproject/infra-containers-gcc:latest
steps:
- uses: actions/checkout@v4
- name: Install uv
shell: bash
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Test Copier Template Variants
shell: bash
env:
GITHUB_ACTIONS: true
# gcc-release will use GCC 15 which supports C++23 modules appropriately in this container
run: ./copier/test_standard_project.sh gcc-release

copier-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
shell: bash
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Generated project self-lints cleanly
shell: bash
# Verify a freshly generated standard project passes its own
# pre-commit config (gersemi, clang-format, beman-tidy) on the first
# run, exercising copier/format_project.sh.
run: ./copier/lint_standard_project.sh my_project

copier-cmake-matrix:
runs-on: ubuntu-latest
container:
image: ghcr.io/bemanproject/infra-containers-gcc:latest
strategy:
fail-fast: false
matrix:
cmake_version: ["3.30.9", "3.31.10", "4.0.3", "4.1.3", "4.2.3", "4.3.2"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Why are v4.4, v4.3.3, v4.2.7, v4.1.6, v4.0.7, ... missing?
  • What is the intention of this test matrix?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

matches the selection of GUIDs in infra/cmake/enable-experimental-import-std.cmake

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not understand right this version set?

I would check the latest released versions: 3.30.x, 3.31.x, 4.0.x, 4.1.x, 4.2.x, 4.3.x, and 4.4.x

steps:
- uses: actions/checkout@v4
- name: Install uv
shell: bash
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Test Copier Template Variants Matrix
shell: bash
env:
GITHUB_ACTIONS: true
# Run across our experimental boundaries
run: ./copier/test_cmake_matrix.sh gcc-release ${{ matrix.cmake_version }}
preset-test:
uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.7.3
with:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

name: Cookiecutter Test
name: Copier Test
on:
push:
branches:
- main
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
cookiecutter-test:
copier-test:
runs-on: ubuntu-latest
name: "Check cookiecutter for consistency"
name: "Check copier for consistency"
steps:
- name: Checkout
uses: actions/checkout@v6
- name: beman cookiecutter consistency check
- name: beman copier consistency check
run: |
./cookiecutter/check_cookiecutter.sh
./copier/check_copier.sh
12 changes: 6 additions & 6 deletions .github/workflows/todo_exemplar_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ jobs:
uses: actions/checkout@v6
- name: Test static exemplar
run: |
cd cookiecutter
source ./check_cookiecutter.sh
cookiecutter_venv_path=$(mktemp --directory --dry-run)
setup_venv "$cookiecutter_venv_path"
stamp "$PWD" "./static_exemplar" "gtest" "false"
cd static_exemplar/exemplar
cd copier
source ./check_copier.sh
copier_venv_path=$(mktemp --directory --dry-run)
setup_venv "$copier_venv_path"
stamp "$repo_root" "./static_exemplar" "gtest" "false"
cd static_exemplar
grep -r 'identity' . && exit 1
find . -name '*identity*' | grep . && exit 1
cmake -B build -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake -DCMAKE_CXX_STANDARD=20 -DCMAKE_INSTALL_PREFIX=$PWD/dist
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ repos:
hooks:
- id: codespell

exclude: 'cookiecutter/|infra/|port/'
exclude: 'template/|copier/|infra/|port/'
41 changes: 41 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,47 @@ ctest --test-dir build
The file `./lockfile.json` configures the list of dependencies and versions that will be
acquired by FetchContent.

## Developing The Copier Template

This repository doubles as both the exemplar library and the Copier template used to stamp
new Beman libraries.

The template workflow is organized as follows:

* `copier.yml` defines the Copier questions, defaults, validation rules, and post-copy
tasks.
* `template/` contains the files rendered into generated projects.
* `stamp.sh` is the user-facing wrapper for stamping a forked exemplar repository into a
new project.
* `./copier/check_copier.sh` validates that exemplar regenerates cleanly and that a
non-exemplar project does not leak exemplar-specific names like `identity`.

When testing local template changes, do not render directly from the Git checkout. The
Copier helper scripts intentionally render from a temporary snapshot with `.git`, `build`,
and `.venv` excluded so that validation uses the current worktree contents.

Run the template checks with:

```shell
./copier/check_copier.sh
```

If you need to smoke-test generated projects manually, start from the same validated flow:

```shell
tmpdir=$(mktemp -d)
python3 -m venv "$tmpdir/venv"
"$tmpdir/venv/bin/python3" -m pip install copier
src=$(mktemp -d)
rsync -a --exclude .git --exclude build --exclude .venv ./ "$src/"
"$tmpdir/venv/bin/copier" copy --trust --defaults \
-d template_src_path=https://github.com/bemanproject/exemplar.git \
-d template_commit="$(git rev-parse HEAD)" \
"$src" "$tmpdir/project"
```

Then configure and build the generated project as usual.

## Project-specific configure arguments

Project-specific options are prefixed with `BEMAN_EXEMPLAR`.
Expand Down
172 changes: 143 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,155 @@
# How to Use This Template

To create a new Beman library, first click the "Use this template" dropdown in the
top-right and select "Create a new repository":
This repository uses [Copier](https://copier.readthedocs.io/) as its templating engine to generate, manage, and update Beman library boilerplate. The template inputs are defined in `copier.yml`, and the rendered project files live under `template/`.

<table><tr><td>
<img src="images/use-this-template.png" width="400px">
</td></tr></table>
> [!NOTE]
> ?? **`stamp.sh` is deprecated.** If you are used to the legacy workflow of forking this repository and running `./stamp.sh`, please transition to the natively supported `copier` workflow below. The script remains for legacy CI compatibility but will not receive future updates.

This will create a new repository that's an exact copy of exemplar. The next step is to
customize it for your use case.
## ? Quick Start: Create a New Library

To do so, execute the bash script `stamp.sh`. This script will prompt for parameters like
the new library's name, paper number, and description. Then it will replace your exemplar
copy with a stamped-out template containing these parameters and create a corresponding
git commit and branch:
If you are already familiar with the Beman project lifecycle, you can generate a new library immediately using `uvx` (via the [uv package manager](https://docs.astral.sh/uv/)):

```bash
uvx --from copier copier copy "git+[https://github.com/bemanproject/exemplar.git](https://github.com/bemanproject/exemplar.git)" my-new-library
cd my-new-library
git init
git add .
git commit -m "Initial commit from Beman Exemplar template"
uvx pre-commit install
```

---

## ?? The Beman Project Lifecycle

To create a new library and get it officially adopted into the Beman project, you will generate the project locally, push it to your personal GitHub for active development, and eventually transfer it to the `bemanproject` organization.

> **Why this specific workflow?**
> Developing on your personal account first gives you a sandbox to experiment and clean up your Git history. Transferring the repository (rather than creating it directly in the org) ensures the `bemanproject` organization ultimately holds the canonical, unbroken Git history, which prevents downstream forks from breaking later.

### Step 1: Push to Your Personal GitHub
After running the Quick Start commands above, push the fresh template to your personal account to begin development.

**Option A: Using the GitHub CLI (`gh`)**
```bash
gh repo create my-new-library --public --source=. --remote=origin --push
```

**Option B: Using the GitHub Browser Interface**
1. [Create a new repository](https://github.com/new). Name it exactly what you named your local folder. Leave it completely empty.
2. Link and push your local code:
```bash
git remote add origin [https://github.com/](https://github.com/)<your-username>/my-new-library.git
git branch -M main
git push -u origin main
```

### Step 2: Incubation & Implementation
Before transferring the project to the Beman organization, take the time to build out the actual proposal.
* Expand beyond the default placeholder headers (e.g., `my-new-library.hpp`).
* Stub out the necessary files for your library's domain.
* Write out the skeleton of the library's design and documentation.
* *Optional:* Once you have a working sandbox, feel free to squash or clean up your Git history using an interactive rebase (`git rebase -i`) before handing it over.

### Step 3: Transfer to the Beman Project
Once the repository has enough substance to be reviewed or collaborated on, hand it over to the organization to establish it as the canonical source.

1. On your GitHub repository page, go to **Settings** > **General**.
2. Scroll to the bottom to the **Danger Zone** and click **Transfer ownership**.
3. Type `bemanproject` as the new owner and confirm.

### Step 4: Fork It Back for Ongoing Development
Now that `bemanproject/my-new-library` is the official upstream repository, standard open-source contribution rules apply.

1. Navigate to the new URL: `https://github.com/bemanproject/my-new-library`.
2. Click **Fork** in the top right corner to create a fork in your personal namespace.
3. Update your local repository's remotes so you can pull from upstream and push to your new fork:
```bash
# Rename your current remote (the org repo) to 'upstream'
git remote rename origin upstream

# Add your new personal fork as 'origin'
git remote add origin [https://github.com/](https://github.com/)<your-username>/my-new-library.git
```

---

## ?? Reconfiguring or Renaming Your Project

One of the biggest advantages of using `copier` is that it actively maintains your project's boilerplate. If you start out with a placeholder name and later decide to rename your library (or change your minimum C++ version), you can instruct Copier to update the structural boilerplate using the `--data` flag.

For example, to rename your project to `range_discombobulator`:

```bash
uvx --from copier copier update --trust --skip-answered --data project_name=range_discombobulator
```

**What this does:**
* `--data project_name=...`: Overrides your previous answer for the project name.
* `--skip-answered`: Skips the interactive prompt for all the questions you aren't changing.

Copier will calculate the difference and automatically update the template-provided files—renaming CMake configurations, GitHub Actions matrices, standard folder paths, and core macros.

?? **Important Caveat:** Copier only manages the files it originally generated. If you have added custom files (like `detail/internal.hpp`), or manually typed the old project name into new C++ namespaces or include guards, you will need to update those manually. Always review the changes with `git diff`, and use your IDE or `grep` to hunt down any lingering references to the old name before committing!

## Rebasing An Older Exemplar Clone Onto Copier

If your repository predates the Copier migration and was created as a plain GitHub template copy, bootstrap a fresh stamped baseline at the original exemplar split point, then rebase your project commits onto that baseline.

Start from a clean worktree and make sure you can identify the branch you want to migrate; the examples below assume `main`.

```shell
git remote add exemplar [https://github.com/bemanproject/exemplar.git](https://github.com/bemanproject/exemplar.git)
git fetch exemplar
base=$(git merge-base main exemplar/main)
git branch pre-copier-backup main
git switch --detach "$base"

tmpdir=$(mktemp -d)
python3 -m venv "$tmpdir/venv"
"$tmpdir/venv/bin/python3" -m pip install copier pre-commit
"$tmpdir/venv/bin/copier" copy --trust --overwrite \
-d project_name=your_project_name \
-d maintainer=your_github_username \
-d minimum_cpp_build_version=20 \
-d paper=PnnnnRr \
-d description="Short project description." \
-d unit_test_library=gtest \
-d generating_exemplar=false \
-d owner=bemanproject \
[https://github.com/bemanproject/exemplar.git](https://github.com/bemanproject/exemplar.git) \
.
"$tmpdir/venv/bin/pre-commit" run --all-files || true
git add .
git switch -c stamp
git commit -m "Bootstrap Copier template"

git switch main
git rebase --rebase-merges --onto stamp "$base" main
```
$ ./stamp.sh
[1/6] project_name (my_project_name): example_library
[2/6] maintainer (your_github_username): your_username
[3/6] minimum_cpp_build_version (20):
[4/6] paper (PnnnnRr): P9999R9
[5/6] description (Short project description.):
[6/6] Select unit_test_library
1 - gtest
2 - catch2
Choose from [1/2] (1):
Switched to a new branch 'stamp'
Successfully stamped out exemplar template to the new branch 'stamp'.
Try 'git push origin stamp' to push the branch upstream,
then create a pull request.

After the rebase, commit the generated `.copier-answers.yml`. That file is what enables future template updates with:

```shell
uvx --from copier copier update --trust
```

From there, you can simply fill in all the remaining parts of the repository that are
labeled 'todo'.
If you want to track a fork of exemplar rather than `bemanproject/exemplar`, edit `_src_path` in `.copier-answers.yml` before your first `copier update`.

## Template Maintenance

If you are changing the template itself rather than developing a library, use the Copier workflow directly:

* Edit `copier.yml` for template questions, defaults, validators, and post-copy tasks.
* Edit `template/` for files that should be rendered into stamped projects.
* Run `./copier/check_copier.sh` to verify exemplar self-regeneration and non-exemplar templating.
* Create a new project locally using the Quick Start commands when you want to use the template for real work.

The consistency check renders from a `.git`-free temporary snapshot of the repository. That keeps local validation aligned with the current worktree contents, rather than only the last committed Git state.

---

What follow is an example of a Beman library README.
What follows is an example of a Beman library README.

# beman.exemplar: A Beman Library Exemplar

Expand All @@ -54,7 +168,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

`beman.exemplar` is a minimal C++ library conforming to [The Beman Standard](https://github.com/bemanproject/beman/blob/main/docs/beman_standard.md).
This can be used as a template for those intending to write Beman libraries.
It may also find use as a minimal and modern C++ project structure.
It may also find use as a minimal and modern C++ project structure.

**Implements**: `std::identity` proposed in [Standard Library Concepts (P0898R3)](https://wg21.link/P0898R3).

Expand Down
Loading
Loading