An open API service indexing awesome lists of open source software.

https://github.com/bmmmm/bitcircus101

website repo of bitcircus101
https://github.com/bmmmm/bitcircus101

bonn commnuity coworking-space hackpace

Last synced: 2 months ago
JSON representation

website repo of bitcircus101

Awesome Lists containing this project

README

          

# bitcircus101

[bitcircus101](https://bitcircus101.de) is a tech and creative space located in Bonn, Germany.

This repository contains the website for bitcircus101.

## Branches

| Branch | Purpose |
|--------|---------|
| `feat/…` | Feature branches — all changes land here first, then merge to `main` via PR; delete after merge |
| `main` | Integration branch (no direct pushes for normal work; see [CONTRIBUTING.md](CONTRIBUTING.md)) |
| `live` | Production branch — served by GitHub Pages. CI actions commit generated files here |

## Project structure

```
index.html Main landing page
events.html Events page (loads events-data.json)
donations.html Donation / funding page (loads funding.json)
raum-nutzen.html Room usage info
impressum-datenschutz.html Legal / privacy
dankedankedanke.html Thank you page

ascii/index.html Internal ASCII playground (/ascii/) — noindex, not in nav; strict system monospace

includes/
site-header.html Shared (nav); inlined into pages by build:layout
site-footer.html Shared ; inlined into pages by build:layout
scripts/inject-layout.mjs Writes header/footer into the six layout HTML files

style.css Global styles (terminal theme, dark bg, green accent)
main.js Frontend: nav, carousel, map, events preview, footer

events.js Frontend: fetches events-data.json, renders event cards, tag filtering
calendars.json Calendar source definitions (ICS URLs, names, flags)
funding.json Current funding percentage (generated by CI)

scripts/
sync-events.mjs Fetches ICS from Nextcloud, generates events-data.json + feed.xml

tests/
site.spec.js Playwright end-to-end tests (~20 tests × 2 browsers)
sync-events.spec.mjs Unit tests for ICS parser and RRULE expansion (22 tests)
playwright.config.js Playwright config (Chromium, Mobile Chrome)

.github/workflows/
ci.yml PR checks: layout sync + unit tests (no Playwright)
deploy.yml Test on main → deploy to live
sync-events.yml Calendar sync (every 30 min)
release.yml Create release (manual, calver tags)
update-funding.yml Funding level update (manual)
sitemap.yml Sitemap generation (on push to live)

llms.txt LLM-friendly site summary (llms.txt standard)
changelog.md Release history
robots.txt Crawler rules (allows AI bots, blocks /ascii/)
```

## Generated files

These files are created/updated by CI actions on the `live` branch. Seed values are tracked on `main` so local dev and E2E tests work without running CI first:

| File | Generated by | Schedule |
|------|-------------|----------|
| `events-data.json` | `sync-events.yml` | Every 30 minutes |
| `feed.xml` | `sync-events.yml` | Every 30 minutes |
| `sitemap.xml` | `sitemap.yml` | On push to `live` |
| `funding.json` | `update-funding.yml` | Manual trigger |

## CI / GitHub Actions

### Deploy pipeline (`deploy.yml`)

```
main (push) → Unit tests → Sync script → Playwright E2E → Deploy to live
```

Every push to `main` triggers:
1. **Layout sync check:** `scripts/inject-layout.mjs` must not change any committed `*.html`
2. **Unit tests:** ICS parser, RRULE expansion, date parsing (node:test)
3. **Sync script:** Generates `events-data.json` for E2E tests to use
4. **E2E tests:** Playwright across 2 browsers (~20 tests × Chromium + Mobile Chrome)
5. **Deploy** (only if all tests pass): Syncs site files from `main` to `live`

PRs get lightweight CI via [`ci.yml`](.github/workflows/ci.yml): same layout check + unit tests (no Playwright). See [CONTRIBUTING.md](CONTRIBUTING.md).

Nothing reaches production without passing all tests first.

### Sync calendar events (`sync-events.yml`)

- **Trigger:** Cron every 30 minutes + manual
- **Branch:** Checks out `live`, pulls scripts/config from `main`
- **What it does:**
1. Runs `scripts/sync-events.mjs` (Node 22, zero dependencies)
2. Fetches ICS feeds from all calendars defined in `calendars.json`
3. Parses VEVENT entries, expands RRULE recurrences (weekly, monthly with BYDAY/BYSETPOS)
4. Filters out internal/blocker events and past events
5. Generates `events-data.json` with `lastSync` timestamp (max 40 cards, 120-day horizon)
6. Generates `feed.xml` (RSS 2.0, primary calendar only, max 15 items)
7. Commits and pushes to `live` with retry logic

### Create release (`release.yml`)

- **Trigger:** Manual (`workflow_dispatch`)
- **Branch:** `main`
- **Versioning:** [CalVer](https://calver.org/) — `v2026.03.28` (`.N` suffix for same-day releases)
- **What it does:**
1. Determines next calver tag (checks for existing same-day tags)
2. Collects commits since last tag, drops CI bot / merge commits
3. Groups by [conventional commit](https://www.conventionalcommits.org/) prefix (Features, Fixes, Styling, Docs, Maintenance, Other)
4. Creates git tag and GitHub Release with auto-generated notes
5. Prepends release to `changelog.md` and commits to `main`

Releases are **decoupled from deploys** — every merge to `main` deploys automatically, but releases are created manually when enough changes have accumulated.

### Update funding level (`update-funding.yml`)

- **Trigger:** Manual (`workflow_dispatch`) with `percent` input (0-100)
- **Branch:** `live`
- **What it does:** Writes `funding.json` with the given percentage, commits to `live`

### Generate sitemap (`sitemap.yml`)

- **Trigger:** On push to `live` + manual
- **Branch:** `live`
- **What it does:**
1. Uses `cicirello/generate-sitemap` to scan all HTML files
2. Strips Google verification files from the sitemap
3. Commits `sitemap.xml` to `live`

## Calendar system

### Data sources (`calendars.json`)

| ID | Name | Nextcloud instance | Primary | In RSS |
|----|------|--------------------|---------|--------|
| `bitcircus` | bitcircus101 | nc.6bm.de | yes | yes |
| `datenburg` | Datenburg e.V. | cloud.datenb.org | no | no |

To add a new calendar, add an entry to `calendars.json` — no code changes needed.

### How events get tagged

Tags are resolved in priority order:

1. **Explicit `#hashtags`** in the Nextcloud event description (e.g. `#workshop #hardware`)
2. **ICS CATEGORIES** field (set in Nextcloud calendar UI)
3. **Keyword auto-detection** from title + description (fallback)

If no tags match, the event gets `#community` as default.

### Filtering

- Events with "blocker" or "interne veranstaltung" in the title are excluded
- Only future events are shown (up to 120 days ahead)
- Max 30 events per calendar, 40 total

### Frontend (`events.js`)

- Loads `events-data.json`, falls back to live ICS fetch if JSON unavailable
- Renders event cards grouped by month
- Dynamic tag filter bar (OR logic: any matching tag shows event)
- Source badge for non-primary calendar events
- Permalink anchors with smooth scroll

## Local development

**Site chrome (nav + footer):** Edit [`includes/site-header.html`](includes/site-header.html) and [`includes/site-footer.html`](includes/site-footer.html), then run `npm run build:layout` and commit the partials plus updated `*.html`. Documented in [CONTRIBUTING.md](CONTRIBUTING.md) and [CLAUDE.md](CLAUDE.md).

Static site — open HTML files directly or use any local server:

```sh
python3 -m http.server 8080
```

### Running tests

Contributors only need the quick check — see [CONTRIBUTING.md](CONTRIBUTING.md).

```sh
npm install
npm run test:quick # unit tests only (~100ms, no browser needed)
npm test # full suite (unit + E2E, used by CI)
npm run test:e2e # only Playwright E2E
npm run test:headed # E2E with browser window
npm run test:ui # Playwright UI mode
```

### Test coverage

**Unit tests** (`tests/sync-events.spec.mjs` — 22 tests, ~100ms):

| Area | What is tested |
|------|---------------|
| `parseDate` | All-day dates, datetime with timezone, UTC, null inputs |
| `nthWeekday` | Nth weekday calculation, month overflow |
| `expandRRule` | BYSETPOS + classic BYDAY, weekly, COUNT, EXDATE exclusions |
| `parseICS` | Single events, all-day, TZID params, line folding, recurring BYSETPOS |
| `clean` | ICS character unescaping (`\n`, `\,`, `\;`) |

**E2E tests** (`tests/site.spec.js` — ~20 tests × 2 browsers):

| Area | Tests | What is tested |
|------|-------|---------------|
| Home page | 3 | Title + heading, CTAs, carousel + map interaction |
| SEO / Meta | 2 | All meta tags on home, subpage descriptions + keywords |
| Privacy | 1 | No Google Fonts (HTML + network) |
| Navigation | 2 | Desktop nav + routing, mobile hamburger toggle |
| Events page | 1 | Title, list, subscribe, RSS, back link |
| Events content | 2 | Tags + filtering + month groups, sync status |
| Donations | 1 | Title, consent banner dismiss |
| Raum nutzen | 1 | Title, CTA, structured data |
| Impressum | 1 | Title + back link |
| Danke page | 1 | Title, noindex, content, back link |
| Terminal theme | 1 | Dark bg, monospace, no inline styles |
| JS errors | 6 | All 6 pages free of console errors |
| Internal links | 1 | Crawls all pages, verifies every internal link resolves |
| Accessibility | 1 | Aria-labels, alt texts, landmark roles |

Browsers: Chromium, Mobile Chrome (Pixel 5).

### Running calendar sync locally

```sh
node scripts/sync-events.mjs
```

This fetches live ICS data and writes `events-data.json` + `feed.xml` to the project root.