Skip to content

Contributing

To get started, clone this repository and allow devenv to set up the environment with the configuration from the ./dev directory:

Terminal window
git clone ssh://git@codeberg.org/tarcisio/conan-flake.git
cd conan-flake
devenv --from path:dev allow

It will complain that conan-flake is not available:

error: To use 'conan', run the following command:
$ devenv inputs add conan-flake git+https://codeberg.org/tarcisio/conan-flake

Add the conan-flake input pointing to the local checkout (the root of this repository) and activate devenv shell:

Terminal window
devenv inputs add conan-flake "git+file://$PWD"
devenv shell

Point that input at the checkout as a git reference rather than as path:"$PWD". A path: input is copied off disk verbatim, .gitignore and all, and Nix can only archive regular files, directories and symbolic links — so a socket or a device node left in the working tree by some local tool aborts the fetch outright with has an unsupported type, whether or not the process that made it is still alive. git+file:// copies the tree through git, which leaves ignored files behind, and it resolves to the same store path as the . reference the justfile recipes override this input with. It also inherits git’s view of the tree: a file that has never been git added is invisible to the fetch.

Check that a default Conan profile was configured successfully:

Terminal window
conan profile show
Host profile:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=15.3.0
os=Linux
[platform_tool_requires]
cmake/4.3.4
[conf]
Build profile:
[settings]
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=20
compiler.libcxx=libstdc++11
compiler.version=15.3.0
os=Linux
[platform_tool_requires]
cmake/4.3.4
[conf]

Commands can also be run without entering the shell interactively, by prefixing them with devenv shell --. To pick a different secretspec provider when activating it:

Terminal window
devenv inputs add conan-flake "git+file://$PWD"
devenv --secretspec-provider dotenv shell

The justfile recipes all point nix at ./dev and override the conan-flake input with the local checkout:

Terminal window
just show # nix flake show ./dev
just check # nix flake check ./dev
just repl # nix repl ./dev
just ci # the full local CI, via `vira`
just vira <args> # `vira` with arbitrary arguments
just search <query> # conan search "<query>" (defaults to "*")

just show and just check also accept a path or flake reference, so a single scenario can be targeted:

Terminal window
just check ./examples/flake-parts

Each directory under examples and test is an independent flake, and can be validated in isolation against the local checkout:

Terminal window
nix flake check ./examples/flake-parts --override-input conan-flake . --show-trace --no-pure-eval

Most examples define a checks.test derivation that runs conan install and conan build inside a simulated shell, so nix flake check is the whole test runner — there is no separate one.

vira.hs is the source of truth for which example and test flakes CI exercises: a new scenario under examples/ or test/ has to be added to its build.flakes list to be checked. The pipelines themselves are in .woodpecker: checks.yml runs nix flake check ./dev, then vira ci -b, then a build of flake.parts-website against this repository, which is what publishes the option reference; release.yml runs vira ci -b again on release events.

This site is an Astro project living under docs, themed with Starlight. Its sources are the Markdown files under docs/src/content/docs, one per chapter. From the development shell:

Terminal window
just docs # build the site, exactly as CI builds it
just docs-serve # serve it locally, reloading on every source change

Neither command installs anything: the development shell puts the site’s npm dependencies at docs/node_modules, installed by Nix from the committed docs/pnpm-lock.yaml.

A new page is a new Markdown file under docs/src/content/docs, with a title in its frontmatter, listed in the sidebar of docs/astro.config.mjs. That list is what Starlight renders into the navigation of every page, and a chapter missing from it is a chapter nothing links to. Chapters link to each other by their source path ([Toolchains](./toolchains.md)), which keeps the link working while reading the sources on Codeberg; a rehype plugin (docs/plugins/rehype-rewrite-links.mjs) rewrites those targets to the page URLs the built site serves.

just docs builds through Nix, from a filtered copy of the sources, and its output carries the site alone. just docs-serve runs the Astro development server over the checkout instead, which is also what watches the sources and reloads the page being read.

Code samples are never written into the Markdown sources by hand. Each one is declared by an embedmd marker naming the example file and the region to take, and the fenced block below the marker is rewritten from that file:

[embedmd]:# (./.examples/flake-parts/flake.nix nix !/.*{ inputs/ !/.*inputs }/ s/# // dedent)

embedmd resolves the path relative to the Markdown file and refuses to leave that directory, which is why the site’s markers go through docs/src/content/docs/.examples, a symbolic link to the examples directory at the root of the repository.

Running embedmd over the sources rewrites every such block in place:

Terminal window
embedmd README.md docs/src/content/docs/*.md

That same command runs as a pre-commit hook and as a treefmt formatter, so in practice the blocks are refreshed on commit, and a sample that no longer matches its example project fails the authoring.EMBEDDING.2 check in CI — the checks over the site are named after the requirement each one proves.

Command-output blocks are generated the same way, by mdsh: the command that produces the output is recorded next to the block, either visibly —

```sh > text $
conan profile show
```
<!-- BEGIN mdsh -->
```text
…output…
```
<!-- END mdsh -->

— or hidden, when the command is scaffolding rather than something the reader would type:

<!-- > $
echo '```text'
cd "$(git rev-parse --show-toplevel)/examples/flake-parts"
nix develop --command bash -c "profile-show-wrapper 2>/dev/null"
echo '```'
-->

mdsh runs each block from the directory of the Markdown file that carries it, which is why the site’s blocks cd to the repository root first. Running it replaces the block that follows with the command’s current output:

Terminal window
mdsh --inputs docs/src/content/docs/*.md

The sources of the site are the whole of that list: README.md is a pointer at this site and carries no command-output block, so it is off programs.mdsh.includes in dev/treefmt.nix. It does keep one embedded sample, which is why it is still named in the embedmd command above.

This site is a Codeberg Page of this repository: the content of its pages branch is served at https://tarcisio.codeberg.page/conan-flake/. One command builds the site and updates that branch from the build:

Terminal window
just docs-publish

It runs scripts/publish-pages.sh, which is also what CI runs, so what a contributor publishes and what a pipeline publishes are produced by the same code. It needs no credential beyond the one that already pushes to this repository: the push goes through the checkout’s own origin.

What it does, and why it does it that way:

  • the site is built first, and nothing else happens if that build fails, so a broken build cannot leave a half-published branch behind;
  • the branch is updated through a temporary git worktree, never by switching this checkout, so the branch that was checked out stays checked out and the working tree is not touched — including when the run fails;
  • pages is created as an orphan branch: it carries no source of the repository and its history starts at a root commit of its own;
  • everything the branch carried is dropped before the new build is copied in, so a page removed from the site stops being served;
  • the build output is copied out of the Nix store dereferencing symbolic links and without the store’s permissions, since a store path is read-only and its links point back into a store no visitor has. What lands on the branch is the output of the site’s derivation, whole: the server builds nothing.

The pipeline that runs the same script from main is .woodpecker/pages.yml. A push to main that touches the sources the site is built from publishes it, and the pipeline can still be run on demand, which is how the site is republished without a commit. The checklist below is how that was set up.

When two publications race, the second push loses: its run fails on a rejected push, and the site goes on carrying the other run’s build until the next change or a manual run.

These steps need administration rights on the Codeberg repository and on its Woodpecker project, so they cannot be done from a checkout. All of them have been taken here: the webhook and the codeberg_token secret are registered, https://tarcisio.codeberg.page/conan-flake/ serves this site from the pages branch, and CI publishes it on every change to its sources on main. They are kept because they are what a fork, or a move to another forge, has to repeat — and a fork has to repeat them before its pages workflow runs at all: the publishing step names codeberg_token, and a workflow whose secret Woodpecker cannot resolve fails to compile rather than reporting that nothing was published.

  • Register the webhook. Repository settings → Webhooks → Add webhook → type Forgejo, with Target URL https://tarcisio.codeberg.page/conan-flake/ — which doubles as the address the site is served from — and Branch filter pages. This is what tells git-pages to pull the branch when it is pushed.
  • Do not read a failed test delivery as a broken setup. The “Test delivery” button fails by design; Codeberg’s own documentation says so. Verify by pushing the branch and loading the site instead.
  • Create the push credential. A Codeberg access token with write access to this repository, belonging to the account the pipeline pushes as (tarcisio, or whatever PAGES_USER in scripts/publish-pages-ci.sh names), stored as a secret named exactly codeberg_token on this repository’s Woodpecker project. .woodpecker/pages.yml reads it, and publishes nothing while it is empty.
  • Allow that secret at both events the pipeline runs on. Open the secret and tick push and manual under “Available at the following events”. Woodpecker offers a secret only to a run whose event the secret lists, and a new secret lists push, tag and deployment — so manual has to be added, and push has to be left ticked. It resolves secrets while compiling the workflow, before anything runs, so a missing event does not fail a step: it fails the whole run, with secret "codeberg_token" is not allowed to be used with pipeline event "push" (or "manual"). This is the one step of this checklist that cannot be taken from a checkout, and the one to take before the commit that turns publishing on, or that very push is the run that fails.
  • Publish once, with just docs-publish from a checkout. That first run is what creates the pages branch, and it uses your own push credentials rather than the secret.
  • Publish from CI on demand. Once codeberg_token exists and allows manual, run the pages pipeline of the main branch from Woodpecker’s interface: that publishes the site, with no commit to make. This is still how the site is republished when the sources did not change.
  • Let CI publish on every change. .woodpecker/pages.yml has a single step, pages, which runs on - event: [push, manual]: a push to main touching the paths the workflow filters on publishes the site, and so does a run started by hand. There is no second, tokenless step beside it — on a push that one would run the publishing wrapper again without a credential and announce that nothing was published next to a successful publication. What keeps unrelated pushes from publishing is the workflow’s own when, which was already filtering on main and on the site’s sources. The ordering matters: the codeberg_token secret has to allow the push event before the commit that makes this change lands, or that push is compiled with a secret it may not read and fails before it starts.
  • Verify. Load https://tarcisio.codeberg.page/conan-flake/; content can take a few minutes to refresh. If it does not appear, ask git-pages what it deployed, with curl https://tarcisio.codeberg.page/conan-flake/.git-pages/manifest.json — the manifest names the repository, the branch and the commit it served the site from.

A path the site does not carry is answered with the site’s own 404.html, which Astro generates at the root of the output and links back into the sub-path the site is built for (base in docs/astro.config.mjs).