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
106 changes: 106 additions & 0 deletions .claude/skills/add-organizer/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
name: add-organizer
description: Use when adding, removing, or updating a local organizer on a devopsdays event team - covers the team_members entry, the photo, and the mandatory email to info@devopsdays.org that gates the merge.
---

# Add or update a local organizer

Organizers live in the `team_members:` list in `data/events/<YYYY>/<city-slug>/main.yml`, with
photos in `assets/events/<YYYY>-<city-slug>/organizers/`.

## Read this before starting

**Adding or removing an organizer requires an email to `info@devopsdays.org`** containing the
full names, email addresses, and employers of the people added or removed. The same action adds
them to the mailing list and Slack, so **a maintainer will hold the merge until that email is
sent.**

This is not a formality and it is not something the PR can do on its own. Tell the person
explicitly, and put it in the PR body. Purely editing an existing organizer's bio, photo, or
links does not need it.

## The entry

Only `name` is required. Everything else is optional.

```yaml
team_members:
- name: "Jane Doe"
pronouns: "she/her"
role: "Sponsor Coordinator"
employer: "Acme Anvil Co."
bio: "Jane has been running operations teams since 2009. Markdown works here."
image: "jane-doe.jpg"
twitter: "janedoe"
github: "janedoe"
gitlab: "janedoe"
twitch: "janedoe"
linkedin: "https://www.linkedin.com/in/janedoe"
facebook: "https://www.facebook.com/jane.doe"
mastodon: "https://hachyderm.io/@janedoe"
bluesky: "https://bsky.app/profile/janedoe.bsky.social"
website: "https://janedoe.example.com"
```

- `role` defaults to "Organizer" when omitted.
- `twitter`, `github`, `gitlab` and `twitch` are bare usernames, no `@`.
- `linkedin`, `facebook`, `mastodon`, `bluesky` and `website` are full URLs.
- `image` is a bare filename resolved against `assets/events/<YYYY>-<city>/organizers/`.

`gitlab` and `twitch` do render for organizers even though `themes/devopsdays-theme/reference.md`
has historically omitted them from the team member table.

## YAML quoting, which is where this breaks

Bios are free text and routinely contain apostrophes and quotation marks. Use double quotes and
escape any internal double quote, or use a block scalar for anything long:

```yaml
bio: >-
Jane's been doing this a while. She says things like "it depends" a lot,
and she is usually right.
```

`utilities/add_organizers.sh` interpolates raw values into double-quoted YAML, so a bio with a
quote in it produces an invalid file. Writing the entry directly avoids that.

## The photo

```bash
mkdir -p assets/events/2026-boston/organizers
cp ~/Downloads/jane.jpg assets/events/2026-boston/organizers/jane-doe.jpg
```

- JPG, at least 300×300, ideally 600×600 square
- Lowercase filename and extension, or the `Lint Filenames` CI job fails the PR
- Then reference it: `image: "jane-doe.jpg"`

Note `utilities/add_organizers.sh` creates the directory under `static/` but copies into
`assets/` — under `set -e` it dies partway through. Create the `assets/` directory yourself.

## Removing an organizer

Delete their `team_members` entry. Leave the photo file — other years may reference it, and
removing it gains nothing. The `info@devopsdays.org` email is required for removals too, so the
global team can take them off the mailing list and Slack.

## Organizer email addresses

`organizer_email` in `main.yml` is the team's public contact and is normally
`<city-slug>@devopsdays.org`. `proposal_email` is the equivalent for CFP submissions. Neither is
a personal address.

## Verify

- The event still has `organizer_email` set
- Every `image:` names a file that exists in `organizers/`
- The YAML parses — a bad quote in a bio breaks the whole event
- Preview `/events/<YYYY>-<city>/contact/`, which is where the team is normally listed

## Submit

Follow `.claude/skills/submit-changes/SKILL.md`. The PR body **must** include an
`## Organizer email` section when people were added or removed, stating that the email to
`info@devopsdays.org` is required before merge.

PR title: `[BOS-2026] Add Jane Doe to the organizer team`.
146 changes: 146 additions & 0 deletions .claude/skills/add-speaker/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
name: add-speaker
description: Use when a devopsdays event has confirmed a speaker and their talk, workshop, ignite, or keynote needs to appear on the event site - covers the speaker bio page, the talk page, the headshot, and multi-speaker talks.
---

# Add a speaker and their talk

Every speaker needs **two** files that share one slug, plus usually a headshot:

| File | Purpose |
|---|---|
| `content/events/<YYYY>-<city>/speakers/<slug>.md` | The bio page |
| `content/events/<YYYY>-<city>/program/<slug>.md` | The talk page: title and abstract |
| `assets/events/<YYYY>-<city>/speakers/<slug>.<ext>` | The headshot |

Adding a speaker does **not** put them on the schedule. That is
`.claude/skills/build-program/SKILL.md`.

## The slug

Lowercase, ASCII, hyphen-separated: `jane-doe`, `maria-lopez`. Fold accents (`Ramírez` →
`ramirez`), drop apostrophes and periods (`O'Brien` → `obrien`, `J.R. Smith` → `jr-smith`).

**The `Lint Filenames` CI job hard-fails on any mixed-case filename**, so this is not cosmetic.

## 1. Speaker page

`content/events/<YYYY>-<city>/speakers/<slug>.md`. TOML frontmatter, `+++` delimiters:

```
+++
Title = "Jane Doe"
Twitter = ""
linkedin = ""
image = ""
type = "speaker"
linktitle = "jane-doe"

+++

Jane's bio goes here. Markdown works.
```

The mixed key casing is what the generator produces and what every existing file uses. Hugo is
case-insensitive about frontmatter keys, so match the template rather than tidying it.

Optional fields the theme renders: `Website`, `Facebook`, `Github`, `Pronouns`, `twitch`,
`mastodon`, `bluesky`. Give `mastodon` and `bluesky` full profile URLs; `Twitter` and `Github`
take bare usernames without the `@`.

`image` is a **bare filename**, not a path — it resolves against
`assets/events/<YYYY>-<city>/speakers/`. Leave it `""` if there is no photo; the theme falls back
to a default avatar.

**Watch the first line.** If the `+++` has a trailing space — which happens with CRLF line
endings on Windows — the site will not build. Use LF.

## 2. Talk page

`content/events/<YYYY>-<city>/program/<slug>.md`:

```
+++
Talk_date = ""
Talk_start_time = ""
Talk_end_time = ""
Title = "The Real Title Of The Talk"
Type = "talk"
Speakers = ["jane-doe"]
+++

The abstract goes here. Markdown works.
```

- `Title` here is the **display title of the talk**, unlike the program YAML where `title` is a
slug. Do not confuse the two.
- `Speakers` is always an array, even for one speaker, and holds **speaker slugs**, not names.
- The `Talk_*` fields are vestigial — the schedule comes from the program data. Leave them empty.
- Keep `Type = "talk"` even for a keynote, workshop, or ignite. Those values have no layout and
fall through to a blank template. The *program entry* is where you mark it as an ignite or
workshop.

## 3. Headshot

Copy into `assets/events/<YYYY>-<city>/speakers/<slug>.<ext>`, creating the directory if needed:

```bash
mkdir -p assets/events/2026-boston/speakers
cp ~/Downloads/jane.jpg assets/events/2026-boston/speakers/jane-doe.jpg
```

- `.jpg`, `.png` and `.webp` all work — the extension must match the actual format
- Square, ideally 600×600. Non-square images get cropped on the speakers index but not on the
individual pages
- Lowercase filename and lowercase extension
- Then set it in the speaker file: `image = "jane-doe.jpg"`

Note `utilities/add_speakers.sh` hardcodes `.png` for both the check and the destination, so it
will happily save a JPEG under a `.png` name. Writing the files directly avoids that.

## 4. Two or more speakers on one talk

Create a speaker page for each. Create **one** talk page listing all of them:

```
Speakers = ["jane-doe", "sam-patel"]
```

You can name that talk file after either speaker, or rename it to something combined
(`doe-patel.md`) — as long as the program entry's `title` matches the filename.

## 5. Show the speakers page in the nav

The first time an event adds speakers, uncomment the entry in
`data/events/<YYYY>/<city>/main.yml`:

```yaml
nav_elements:
- name: speakers
```

Do this by hand. The `sed` in `utilities/add_speakers.sh` that is supposed to do it no longer
matches the template and silently does nothing.

You also need `content/events/<YYYY>-<city>/speakers.md` to exist:

```
+++
Title = "Speakers"
Type = "speakers"
Description = "Speakers for devopsdays <City> <YYYY>"
+++
```

## 6. Verify

- Both files use the same slug
- `Speakers = [...]` holds slugs that match real files in `speakers/`
- `image` matches a file that is actually on disk, with the right extension
- Preview `/events/<YYYY>-<city>/speakers/` and the individual talk page

## 7. Submit

Follow `.claude/skills/submit-changes/SKILL.md`.

PR title: `[BOS-2026] Add Jane Doe and her talk`.
110 changes: 110 additions & 0 deletions .claude/skills/add-sponsor/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
name: add-sponsor
description: Use when a devopsdays event has sold or confirmed a sponsorship and the sponsor needs to appear on the event's site - covers both sponsors already in the shared directory and brand-new ones needing a logo and data file.
---

# Add a sponsor to an event

Sponsors are **global**, not per-event. `data/sponsors/<id>.yml` and
`assets/sponsors/<first-char>/<id>.png` are shared by every event that has ever listed that
sponsor. An event references a sponsor by id and level.

This matters: creating a duplicate sponsor entry when one already exists is the most common
mistake here, and it fragments the shared directory.

## Gather

- Event: year and city (→ `<YYYY>` and `<city-slug>`, e.g. `2026` / `boston`)
- Sponsor name as it should read
- Sponsorship level
- Sponsor URL, and whether this event needs its own tracking URL
- A logo, **only if** the sponsor is not already in the directory

## 1. Check whether the sponsor already exists — always do this first

```bash
ls data/sponsors/ | grep -i <name-fragment>
```

Search loosely. `thoughtworks-gocd`, `ibmcode`, and `redhat` are all real ids that a plain name
search would miss. Also look for date-suffixed variants like `pivotal-before-20190115.yml` —
those are *historical* entries kept so past events keep their old logo. **Never point a current
event at a `-before-` entry.**

If it exists with the right name and URL, go to step 3 — you do not need a logo, a data file, or
any change under `assets/`.

## 2. Only if it is genuinely new

Pick the id: lowercase, alphanumeric and hyphens, no spaces. It becomes the filename in both
places and must match exactly.

**Data file** — `data/sponsors/<id>.yml`:

```yaml
name: "Sponsor Display Name"
url: "https://sponsor.example.com"
```

`twitter:` is optional; add it without the `@` if you have it. That is the whole schema.

**Logo** — `assets/sponsors/<first-char>/<id>.png`, where `<first-char>` is the first character
of the id (`assets/sponsors/d/datadog.png`). Create the shard directory if it does not exist.

Requirements:

- At least 200px wide; 600px looks right on high-density displays
- White or transparent background
- Square is not required but usually looks better
- Lowercase filename, or the `Lint Filenames` CI job fails the PR

**Do not put anything in `static/img/sponsors/`.** A dedicated workflow
(`.github/workflows/no-sponsors-changes.yml`) hard-fails any PR that touches it.

## 3. Add the sponsor to the event

Edit `data/events/<YYYY>/<city-slug>/main.yml`.

Check `sponsor_levels:` first — the level you are adding must have a matching `id:` there, and if
that level has a `max:`, adding beyond it means the "Become a sponsor" link disappears for that
level. If the level does not exist yet, add it to `sponsor_levels:` too.

Then add to `sponsors:`:

```yaml
sponsors:
- id: cloudbees
level: bronze
```

If this event needs its own tracking URL, override it here rather than editing the shared sponsor
file — which would change the URL for every event that lists them:

```yaml
- id: netapp
level: gold
url: https://netapp.com/?campaign=devopsdays-boston
```

Set `sponsors_accepted: "yes"` if the event wants the "Become a sponsor" links shown at all.

## 4. Verify

- The `id:` in `main.yml` matches `data/sponsors/<id>.yml` exactly. A mismatch renders an empty
logo slot silently — nothing fails.
- The logo exists at `assets/sponsors/<first-char>/<id>.png`. A missing one produces a Hugo
warning and falls back to the legacy static path.
- The `level:` matches an `id:` under `sponsor_levels:`.
- Preview `/events/<YYYY>-<city-slug>/sponsor/` and confirm the logo renders at the right level.

## 5. Submit

Follow `.claude/skills/submit-changes/SKILL.md`.

PR title: `[BOS-2026] Add CloudBees as a Bronze sponsor`.

## If a sponsor wants their logo changed

That is a different job with history-preservation rules — use
`.claude/skills/change-sponsor-logo/SKILL.md`. Do not simply overwrite the PNG; it would
retroactively rewrite years of past events.
Loading
Loading