Reference

Content Authoring

How to write, structure, and publish cocktail and whiskey pages to BookStack — including templates, image conventions, and the automated CI quality checks.

Repository Structure

Content lives in two top-level directories, each mapped to a dedicated BookStack book:

repo/
├── cocktails/
│   ├── negroni.md
│   ├── old-fashioned.md
│   └── images/
│       ├── negroni.jpg
│       └── old-fashioned.jpg
├── whiskeys/
│   ├── ardbeg-10.md
│   ├── glenfiddich-12.md
│   └── images/
│       ├── ardbeg-10.jpg
│       └── glenfiddich-12.jpg
└── bookstack-config.json

File Naming Convention

File names are used to derive the BookStack page title. The sync script converts the filename stem to Title Case, treating hyphens and underscores as word separators:

FilenameBookStack page title
old-fashioned.mdOld Fashioned
gin_and_tonic.mdGin And Tonic
mojito.mdMojito
glenfiddich-12.mdGlenfiddich 12

Use lowercase, hyphen-separated words. Numbers are preserved.

Cocktail Page Template

Every file in cocktails/ must include the following sections (H2 headings). The CI test suite verifies their presence.

Required sections

SectionPurpose
## DetailsMetadata table — author, date, glassware, season, allergens
## RecipeIngredients and method / instructions
## HistoryOrigin and background of the cocktail
## NotesServing suggestions, variations, pairing notes

Minimal example

cocktails/negroni.md
![Negroni](images/negroni.jpg)

---

## Details

| Field         | Value          |
|---------------|----------------|
| Author        | Your Name      |
| Date Created  | 2026-04-10     |
| Updated       | 2026-04-10     |

---

## Recipe

**Ingredients**

- 30 ml gin
- 30 ml sweet vermouth
- 30 ml Campari

**Method**

Stir all ingredients over ice. Strain into a rocks glass. Garnish with orange peel.

---

## History

The Negroni is said to have been invented in Florence in 1919 …

---

## Notes

Works well as a pre-dinner aperitivo. Try substituting mezcal for gin for a smoky twist.

Whiskey Page Template

Every file in whiskeys/ must include these sections. Whiskey pages have stricter structural requirements than cocktail pages.

Required sections

SectionRequired sub-sectionsPurpose
## DetailsMetadata table with specific required fields (see below)
## Tasting Notes### Nose, ### Palate, ### FinishStructured sensory evaluation
## HistoryDistillery background and product history
## NotesFood pairing, serving recommendations, personal notes

Required Details table fields

The ## Details table must contain at least these four rows (the CI test checks for them):

FieldExample
TypeSingle Malt Scotch
OriginIslay, Scotland
DistillerArdbeg Distillery
ABV46%

Minimal example

whiskeys/ardbeg-10.md
![Ardbeg 10](images/ardbeg-10.jpg)

---

## Details

| Field          | Value                  |
|----------------|------------------------|
| Author         | Your Name              |
| Date Created   | 2026-04-10             |
| Updated        | 2026-04-10             |
| Type           | Single Malt Scotch     |
| Origin         | Islay, Scotland        |
| Distiller      | Ardbeg Distillery      |
| Age Statement  | 10 Years               |
| ABV            | 46%                    |

---

## Tasting Notes

### Nose

Peat smoke, lemon, vanilla, and a hint of toffee.

### Palate

Rich and peaty with dark chocolate, espresso, and dried fruit.

### Finish

Long, smoky finish with a warming sweetness.

---

## History

Ardbeg Distillery was founded in 1815 on the southern shore of Islay …

---

## Notes

Best enjoyed neat or with a few drops of still water to open it up.

Images

Every page must have a leading image as its very first line:

![Page Title](images/slug.jpg)

Conventions

Auto-generated placeholder images

When a new Markdown file is pushed to develop without a corresponding image, the image generation workflow automatically creates a placeholder image using Pillow and commits it back to the branch. You can replace it with a real image at any time.

Authoring Rules

⚠️ No H1 headings Do not use # Page Title H1 headings in your Markdown. BookStack renders the page title from its own UI. The CI test will fail if an H1 is detected.

Markdown style

Markdown lint

The markdown linter uses the rules defined in .markdownlint.json. Common rules to be aware of:

Spell check

Spell checking uses cspell with the config in .cspell.json. Spell-check failures appear as inline annotations on the PR diff. They are warnings only — the pipeline continues even if words are flagged. Add custom words to .cspell.json to suppress false positives.

What CI Checks on Your Content

The test suite in tests/test_content.py runs parametrised checks against every page:

Cocktail pages

CheckWhat it verifies
Required sections## Details, ## Recipe, ## History, ## Notes all present
No H1 headingFile does not contain a line starting with #
Image reference presentFile starts with ![…](images/….jpg)
Image file existsThe referenced image file is on disk
Not emptyFile size is greater than 0

Whiskey pages

CheckWhat it verifies
Required sections## Details, ## Tasting Notes, ## History, ## Notes all present
No H1 headingFile does not contain a line starting with #
Image reference presentFile starts with ![…](images/….jpg)
Image file existsThe referenced image file is on disk
Details table fieldsType, Origin, Distiller, ABV rows present in Details table
Tasting Notes subsections### Nose, ### Palate, ### Finish all present
Not emptyFile size is greater than 0

Coverage checks

CheckWhat it verifies
Cocktail image coverageEvery .jpg in cocktails/images/ has a matching .md file
Whiskey image coverageEvery .jpg in whiskeys/images/ has a matching .md file

Publishing Workflow

  1. 1

    Create or edit a Markdown file

    Write your content in cocktails/ or whiskeys/. Add an image to images/ with the matching name.

  2. 2

    Commit and push to develop

    The CI pipeline starts automatically. If an image is missing, the image-generation workflow creates a placeholder and commits it.

  3. 3

    CI runs quality gates

    Secret scan → lint → tests → spell check → sync to local BookStack. If any gate fails, Claude Autofix attempts a fix automatically.

  4. 4

    PR created automatically

    After a successful CI run, a pull request from develop → main is created (or updated if one already exists).

  5. 5

    Merge the PR

    Merging to main triggers the CD pipeline, which syncs the pages to the remote production BookStack instance.

Content Generators

The scripts in .github/scripts/ can generate entire batches of correctly structured pages from data definitions:

ScriptOutputUsage
generate-cocktail-pages.py One .md per cocktail in cocktails/ python3 .github/scripts/generate-cocktail-pages.py
generate-whiskey-pages.py One .md per whiskey in whiskeys/ python3 .github/scripts/generate-whiskey-pages.py
generate-cocktail-images.py Placeholder .jpg images in cocktails/images/ Run automatically by CI when images are missing
generate-whiskey-images.py Placeholder .jpg images in whiskeys/images/ Run automatically by CI when images are missing
delete-whiskey-pages.py Removes whiskey pages from BookStack Run manually when retiring pages

The generator scripts embed cocktail and whiskey data definitions directly in the source. Generated pages conform to the template format including inline HTML pill-group checkboxes for glassware, seasons, and allergens (cocktails) and structured tasting note subsections (whiskeys).

💡 Adding new content The fastest way to add a batch of new pages is to add entries to the data definitions inside the relevant generator script and re-run it. All generated pages will pass CI checks automatically.