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
- Host: GitHub
- URL: https://github.com/bmmmm/bitcircus101
- Owner: bmmmm
- Created: 2025-03-21T18:06:13.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2026-03-29T12:54:29.000Z (3 months ago)
- Last Synced: 2026-03-29T13:47:02.273Z (3 months ago)
- Topics: bonn, commnuity, coworking-space, hackpace
- Language: JavaScript
- Homepage: https://bitcircus101.de
- Size: 1.59 MB
- Stars: 6
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: changelog.md
- Contributing: CONTRIBUTING.md
- Funding: funding.json
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.