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
35 changes: 35 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# http://editorconfig.org

# A special property that should be specified at the top of the file outside of
# any sections. Set to true to stop .editor config file search on current file
root = true

[*]
# Indentation style
# Possible values - tab, space
indent_style = space

# Indentation size in single-spaced characters
# Possible values - an integer, tab
indent_size = 2

# Line ending file format
# Possible values - lf, crlf, cr
end_of_line = lf

# File character encoding
# Possible values - latin1, utf-8, utf-16be, utf-16le
charset = utf-8

# Denotes whether to trim whitespace at the end of lines
# Possible values - true, false
trim_trailing_whitespace = true

# Denotes whether file should end with a newline
# Possible values - true, false
insert_final_newline = true

# Verbatim legal text, reproduced exactly as published. Its roman-numeral
# hanging indents are not ours to normalise.
[LICENSE]
indent_size = unset
8 changes: 8 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# shellcheck disable=all # https://devenv.sh/integrations/direnv/#configure-shell-activation

eval "$(devenv direnvrc)"

# You can pass flags to the devenv command
# For example: use devenv --impure --option services.postgres.enable:bool true
use devenv
58 changes: 58 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Lint

on:
push:
# Kept in step with render-docs.yml's trigger list: a branch worth rendering
# is a branch worth linting. Nothing here depends on which branch publishes.
branches: [main, develop]
pull_request:

permissions:
contents: read

# Deliberately not the `pages` group that render-docs.yml uses: linting does not
# touch the deployment, and sharing that group would queue every lint run behind
# a publish.
concurrency:
group: lint-${{ github.ref }}
cancel-in-progress: true

jobs:
git-hooks:
runs-on: ubuntu-latest
steps:
# The hooks read the index, not just the work tree -- check-asciidoc and
# site-index.sh both call `git ls-files` -- so this needs a real checkout.
# Full history is not needed: every hook looks at the tree as it stands.
- uses: actions/checkout@v7

- name: Install Nix
uses: cachix/install-nix-action@v31

# Pulls the prebuilt devenv closure instead of compiling it on the runner.
- name: Use the devenv binary cache
uses: cachix/cachix-action@v17
with:
name: devenv

- name: Install devenv
run: nix profile install nixpkgs#devenv

# `prek run --all-files` rather than `devenv test`: devenv reports only
# that the git-hooks task failed, without saying which hook or why, even
# with tasks."devenv:git-hooks:run".showOutput set. Running prek directly
# prints the per-hook result and the offending lines, which is the whole
# point of having this job.
#
# --show-diff-on-failure matters because several of these hooks fix the
# file rather than just complaining (end-of-file-fixer, dos2unix,
# trim-trailing-whitespace, nixfmt). Without the diff, CI would report
# "files were modified by this hook" and show nothing else.
#
# `--option env.cicd:bool true` drops the interactive-only packages, most
# of which is VS Code. Without it this job would download the whole
# editor closure on every push just to run the hooks.
- name: Run every git hook over every file
run: |
devenv --option env.cicd:bool true \
shell -- prek run --all-files --show-diff-on-failure
232 changes: 232 additions & 0 deletions .github/workflows/render-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
name: Render docs

on:
push:
# `on:` is the one block where expressions are not allowed, so this list
# cannot itself be driven by PAGES_PUBLISH_BRANCH (see `env:` below). Read
# it as the set of branches CI may *run* on; the variable picks which one of
# them *publishes*. A branch missing from here triggers nothing at all, so
# pointing the variable at a new branch means listing it here as well.
branches: [main, develop]
# Docs releases only. `open-govpress-v*` tags the renderer binary released
# from this repo (see GOVPRESS_URL below), and must not publish a docs
# archive of its own -- `v*` excludes it by construction.
tags: ['v*']
pull_request:

permissions:
contents: read

concurrency:
group: pages
cancel-in-progress: false

env:
# Keep in sync with devenv.nix's govpressVersion/govpressUrl/govpressSha256.
# Fetched from a GitHub Release rather than committed to the repo: at
# ~112MiB it clears GitHub's 100MB hard limit on regular git objects, and
# this repo is a fork, which GitHub's LFS policy blocks from uploading
# *new* LFS objects at all -- release assets hit neither limit.
GOVPRESS_VERSION: "0.0.19"
GOVPRESS_URL: https://github.com/736-c41-2c1-e464fc974/opensource-guidelines/releases/download/open-govpress-v0.0.19/open-govpress-0.0.19-linux-x64.tar.gz
GOVPRESS_SHA256: 946c7f187ec1eb109681e89cbd9aebcba869e560df8f7493ddbecd3540a9b679

# Which branch owns the published site. Set the repository variable
# PAGES_PUBLISH_BRANCH (Settings -> Secrets and variables -> Actions ->
# Variables) to move publishing to another branch without editing this file;
# unset means `main`, so the default is the behaviour this workflow has always
# had. Tags publish regardless -- a release archives itself whatever branch is
# currently the published one.
#
# Carried into tools/site-publish.sh, which re-checks it rather than trusting
# the caller, and on into tools/site-archive.sh, which names the branch on the
# archive page.
PUBLISH_BRANCH: ${{ vars.PAGES_PUBLISH_BRANCH || 'main' }}

jobs:
# One job per language. Every document carries all five languages in one
# file, so the same sources are rendered five times, each pinned with
# `--lang`, into a directory of its own -- `-o <dir>` names outputs
# `<basename>.pdf` with no language in the filename, so the directory is
# what keeps them apart.
render:
runs-on: ubuntu-latest
strategy:
# One broken language should report itself rather than cancel the other
# four and hide whether they were fine.
fail-fast: false
matrix:
lang: [en, de, fr, it, rm]
steps:
- uses: actions/checkout@v7

- name: Cache open-govpress download
uses: actions/cache@v6
with:
path: /tmp/open-govpress.tar.gz
key: open-govpress-${{ env.GOVPRESS_SHA256 }}

- name: Install Xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb

- name: Download and extract open-govpress
run: |
if [ ! -f /tmp/open-govpress.tar.gz ]; then
curl -fL --retry 3 -o /tmp/open-govpress.tar.gz "$GOVPRESS_URL"
fi
echo "$GOVPRESS_SHA256 /tmp/open-govpress.tar.gz" | sha256sum -c -
mkdir -p /tmp/open-govpress
tar xzf /tmp/open-govpress.tar.gz -C /tmp/open-govpress
bin=$(find /tmp/open-govpress -maxdepth 2 -name open-govpress -type f)
echo "OGP_BIN=$bin" >> "$GITHUB_ENV"

- name: Render all documents in ${{ matrix.lang }}
env:
LANG_ID: ${{ matrix.lang }}
run: |
mkdir -p "out/$LANG_ID"

# A document may opt out of a language with `:l10n-languages:` in its
# header. Absent means all five. Skipping it here is what makes an
# untranslated document *absent* from that language rather than
# rendered as a blank PDF -- no ifeval:: branch matches, so the body
# would be empty.
#
# `docs/partials/` is excluded: it holds fragments that are
# `include::`d into the documents, and a fragment is not a document.
docs=()
while IFS= read -r doc; do
langs=$(sed -n 's/^:l10n-languages:[[:space:]]*//p' "$doc" | head -1)
if [ -z "$langs" ] || grep -qw "$LANG_ID" <<<"$langs"; then
docs+=("$doc")
else
echo "skipping $doc (no $LANG_ID translation)"
fi
done < <(git ls-files '*.adoc' ':!docs/partials/*')

xvfb-run -a "$OGP_BIN" render --no-sandbox \
--lang "$LANG_ID" -o "out/$LANG_ID" "${docs[@]}"

- name: Generate language index page
run: tools/site-index.sh "${{ matrix.lang }}" "out/${{ matrix.lang }}"

- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
# `path: out/` so the archive's root is `<lang>/`, which is what lets
# the collect job merge all five into one tree.
name: pdfs-${{ matrix.lang }}
path: out/

collect:
needs: render
runs-on: ubuntu-latest
outputs:
# The one publish decision for the whole workflow -- the steps below read
# it back, and so does the deploy job.
publish: ${{ steps.gate.outputs.publish }}
permissions:
# `contents: write` is for pushing the accumulated site to the
# `site-history` branch. Job-level permissions replace the top-level block
# outright rather than adding to it, so the read scopes have to be
# restated here.
contents: write
actions: read
steps:
- uses: actions/checkout@v7

# Decided once, here, instead of as the same three-clause condition
# repeated on every publishing step and on the deploy job. Those copies
# had to agree with each other and with site-publish.sh's own guard to
# mean anything; a single output cannot drift out of step with itself.
#
# The ref reaches the shell through the environment rather than through
# `${{ }}`: branch names may contain characters that would otherwise be
# substituted into the script before bash ever sees it.
- name: Decide whether this run publishes
id: gate
env:
REF_NAME: ${{ github.ref_name }}
REF_TYPE: ${{ github.ref_type }}
EVENT_NAME: ${{ github.event_name }}
run: |
publish=false
if [ "$EVENT_NAME" = push ]; then
# The trigger admits only `v*` tags, so any tag push reaching here
# is a docs release and archives itself under tags/ either way.
if [ "$REF_TYPE" = tag ] || [ "$REF_NAME" = "$PUBLISH_BRANCH" ]; then
publish=true
fi
fi
echo "publish=$publish" >> "$GITHUB_OUTPUT"
echo "publish=$publish (event=$EVENT_NAME $REF_TYPE=$REF_NAME, published branch=$PUBLISH_BRANCH)"

- name: Download every language
uses: actions/download-artifact@v8
with:
pattern: pdfs-*
# Without this each archive lands under its own `pdfs-<lang>/`
# directory instead of merging into `site/<lang>/`.
merge-multiple: true
path: site

- name: Generate language chooser
run: tools/site-root.sh site

- name: Upload site artifact
uses: actions/upload-artifact@v7
with:
name: site
path: site/

# A Pages deployment always replaces the whole site, so publishing an
# older build alongside the current one means uploading both -- which
# means remembering the older one somewhere between runs. That somewhere
# is the `site-history` branch.
- name: Overlay this build onto the published site
if: steps.gate.outputs.publish == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
KEEP_COMMITS: "10"
run: tools/site-publish.sh site .site-history

# upload-pages-artifact excludes `.git` from its tar since v4, but the
# site is re-published from a fresh clone each run and the object store
# has no business being in the artifact -- dropping it here keeps that
# guarantee ours rather than the action's.
- name: Strip the git metadata
if: steps.gate.outputs.publish == 'true'
run: rm -rf .site-history/.git

- name: Upload Pages artifact
if: steps.gate.outputs.publish == 'true'
uses: actions/upload-pages-artifact@v5
with:
path: .site-history/

deploy:
# Job-level `if:` cannot read the `env` context, which is why the decision
# travels as a job output rather than as PUBLISH_BRANCH compared in place.
if: needs.collect.outputs.publish == 'true'
needs: collect
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
env:
# actions/deploy-pages keys the deployment on GITHUB_SHA alone, and
# Pages treats a repeat of an already-published version as a no-op --
# reporting success while continuing to serve the earlier build. A
# release tag normally points at the commit main has just published,
# so the tag's archive would never reach the site. Qualifying the
# version with the ref name makes each ref its own deployment.
# Undocumented but stable: the runner layers step `env` over the
# default variables, and pages_build_version is a free-form string.
GITHUB_SHA: ${{ github.sha }}-${{ github.ref_name }}
uses: actions/deploy-pages@v5
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# devenv / direnv
.devenv*
.direnv/

# Generated by devenv from the git-hooks.hooks block in devenv.nix, as a
# symlink into the nix store. Committing it would pin hook versions to one
# machine's store paths.
/.pre-commit-config.yaml

# open-govpress: downloaded from a GitHub Release and extracted on demand,
# see devenv.nix
/open-govpress/.cache/
/open-govpress/.extracted/

# rendered output (build artifacts, not source)
*.pdf

# the per-language site built by `render-docs` locally and by CI
/build/
/site/
Loading
Loading