Skip to content

Commit 82582f5

Browse files
committed
build(ci): add containers, contract checks, and smoke automation
Intent: make the relaunch operable by default for local work, CI, and release verification. Goals: standardize the dev container workflow, move quality gates into automation, and make browser and container smoke checks part of delivery. Changes: add the devcontainer and compose setup, replace legacy workflows with the new CI pipeline, wire OpenAPI and smoke verification into tooling, and document the agent-facing workflow.
1 parent 13a5433 commit 82582f5

32 files changed

Lines changed: 1979 additions & 395 deletions

.devcontainer/Dockerfile

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
FROM ruby:4.0.1-alpine3.23
2+
3+
SHELL ["/bin/sh", "-o", "pipefail", "-c"]
4+
5+
RUN apk add --no-cache \
6+
bash \
7+
build-base \
8+
curl \
9+
git \
10+
libxml2-dev \
11+
libxslt-dev \
12+
nodejs \
13+
npm \
14+
openssl-dev \
15+
python3 \
16+
tzdata
17+
18+
ARG USER=vscode
19+
ARG UID=1000
20+
ARG GID=1000
21+
22+
RUN addgroup -g "$GID" "$USER" \
23+
&& adduser -D -G "$USER" -u "$UID" "$USER"
24+
25+
WORKDIR /workspace
26+
RUN chown -R "$USER":"$USER" /workspace
27+
28+
ENV BUNDLE_PATH=/usr/local/bundle
29+
30+
USER "$USER"
31+
32+
EXPOSE 4000
33+
34+
CMD ["sleep", "infinity"]

.devcontainer/README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Dev Container Workflow
2+
3+
This repository ships a single Dev Container for development. Open the project in VS Code (Dev
4+
Containers extension) or GitHub Codespaces and use that environment for all work.
5+
6+
## What gets created
7+
8+
The devcontainer starts one service named `app` and exposes:
9+
10+
- **Port 4000:** Ruby app
11+
- **Port 4001:** Astro dev server
12+
13+
The repo is mounted at `/workspace`. Bundler gems are cached in a Docker volume to speed up
14+
future launches.
15+
16+
## Bootstrap
17+
18+
On first open, the Dev Container runs:
19+
20+
```
21+
make setup
22+
```
23+
24+
This installs Ruby and frontend dependencies inside the container.
25+
26+
If setup fails due to missing network access (e.g., GitHub DNS), rerun `make setup` once
27+
network access is available.
28+
29+
## Common commands (run inside the container)
30+
31+
```
32+
make dev # Ruby + Astro
33+
make dev-ruby # Ruby only
34+
make dev-frontend # Astro only
35+
make test # Ruby + frontend tests
36+
make ready # RuboCop + RSpec (pre-commit gate)
37+
```
38+
39+
## Lint and tests
40+
41+
```
42+
bundle exec rubocop -F
43+
bundle exec rspec
44+
```
45+
46+
## Notes
47+
48+
- All commands are expected to run inside the Dev Container.
49+
- The default service command is `sleep infinity`; use the Make targets above to start servers.

.devcontainer/devcontainer.json

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,43 @@
11
{
22
"name": "html2rss-web",
3-
"image": "mcr.microsoft.com/devcontainers/ruby:3.4",
4-
"features": {
5-
"ghcr.io/devcontainers/features/git:1": {},
6-
"ghcr.io/devcontainers/features/github-cli:1": {}
7-
},
3+
"dockerComposeFile": "docker-compose.yml",
4+
"service": "app",
5+
"workspaceFolder": "/workspace",
6+
"shutdownAction": "stopCompose",
87
"customizations": {
98
"vscode": {
109
"extensions": [
11-
"redhat.vscode-yaml",
12-
"esbenp.prettier-vscode",
13-
"github.copilot",
14-
"github.copilot-chat",
15-
"shopify.ruby-lsp"
10+
"rebornix.ruby",
11+
"astro-build.astro-vscode",
12+
"esbenp.prettier-vscode"
1613
],
1714
"settings": {
18-
"ruby.rubocop.executePath": "bundle exec",
19-
"ruby.format": "rubocop",
20-
"ruby.lint": {
21-
"rubocop": true
22-
},
15+
"editor.formatOnSave": true,
16+
"editor.defaultFormatter": "esbenp.prettier-vscode",
2317
"files.associations": {
24-
"*.erb": "erb"
25-
}
18+
"*.astro": "astro"
19+
},
20+
"prettier.configPath": "./frontend/prettier.config.js",
21+
"ruby.format": "rubocop",
22+
"ruby.lint": { "rubocop": true },
23+
"editor.tabSize": 2,
24+
"editor.insertSpaces": true,
25+
"files.trimTrailingWhitespace": true,
26+
"files.insertFinalNewline": true
2627
}
2728
}
2829
},
29-
"postCreateCommand": "make setup",
30-
"postStartCommand": "echo '🚀 html2rss-web Development Environment Ready!' && echo '' && echo '📋 Quick Start Commands:' && echo ' make dev # Start development server' && echo ' make test # Run tests' && echo ' make lint # Run linter' && echo ' make fix # Auto-fix linting issues' && echo ' make help # Show all commands' && echo '' && echo '🌐 Server will be available at: http://localhost:3000' && echo '📁 Project files are in: /workspaces/html2rss-web' && echo '' && echo '💡 Tip: Use Ctrl+C to stop the development server' && echo ''",
31-
"forwardPorts": [
32-
3000
33-
],
30+
"forwardPorts": [4000, 4001],
3431
"portsAttributes": {
35-
"3000": {
36-
"label": "html2rss-web",
32+
"4000": {
33+
"label": "Ruby App",
3734
"onAutoForward": "notify"
35+
},
36+
"4001": {
37+
"label": "Astro Dev Server",
38+
"onAutoForward": "silent"
3839
}
3940
},
41+
"postCreateCommand": "bash -lc \"make setup || (echo 'make setup failed; rerun once network access is available.' && exit 0)\"",
4042
"remoteUser": "vscode"
4143
}

.devcontainer/docker-compose.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
services:
2+
app:
3+
build:
4+
context: ..
5+
dockerfile: .devcontainer/Dockerfile
6+
volumes:
7+
- ../:/workspace:cached
8+
- bundle-cache:/usr/local/bundle
9+
ports:
10+
- "4000:4000"
11+
- "4001:4001"
12+
environment:
13+
- RACK_ENV=development
14+
- BUNDLE_PATH=/usr/local/bundle
15+
- PORT=4000
16+
command: sleep infinity
17+
user: vscode
18+
working_dir: /workspace
19+
20+
volumes:
21+
bundle-cache:

.github/copilot-instructions.md

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,57 @@
33
## Overview
44

55
- Ruby web app that converts websites into RSS 2.0 feeds.
6-
- Built with **Roda**, using the **html2rss** gem (+ `html2rss-configs`).
7-
- **Principle:** _All features must work without JavaScript._ JS is only progressive enhancement.
6+
- Built with **Roda** backend + **Preact** frontend, using the **html2rss** gem (+ `html2rss-configs`).
7+
- **Frontend:** Vite-built Preact UI, served alongside Ruby backend.
8+
9+
## Documentation website of core dependencies
10+
11+
Search these pages before using them. Find examples, plugins, UI components, and configuration options.
12+
13+
### Roda
14+
15+
1. https://roda.jeremyevans.net/documentation.html
16+
17+
### Preact & Vite
18+
19+
1. https://preactjs.com/guide/v10/getting-started/
20+
2. https://vite.dev/guide/
21+
22+
### html2rss
23+
24+
1. If available, find source locally in: `../html2rss`.
25+
2. source code on github: https://github.com/html2rss/html2rss
26+
27+
### Test and Linters
28+
29+
1. https://docs.rubocop.org/rubocop/cops.html
30+
2. https://docs.rubocop.org/rubocop-rspec/cops_rspec.html
31+
3. https://rspec.info/features/3-13/rspec-expectations/built-in-matchers/
32+
4. https://www.betterspecs.org/
33+
34+
Fix rubocop `RSpec/MultipleExpectations` adding rspec tag `:aggregate_failures`.
835

936
## Core Rules
1037

11-
-Use **Roda routing with `hash_branch`**. Keep routes small.
12-
-Put logic into `helpers/` or `app/`, not inline in routes.
38+
-Organise Roda routes via dedicated modules (e.g. `Html2rss::Web::Routes::*`), keeping the main app class thin.
39+
-Keep helper modules minimal: define entrypoints with `class << self` and push implementation helpers under `private`; avoid `module_function` unless mirroring existing conventions.
1340
- ✅ Validate all inputs. Pass outbound requests through **SSRF filter**.
1441
- ✅ Add caching headers where appropriate (`Rack::Cache`).
1542
- ✅ Errors: friendly messages for users, detailed logging internally.
16-
-CSS: Water.css + small overrides in `public/styles.css`.
17-
-Specs: RSpec, unit + integration, use VCR for external requests.
18-
19-
## Don’t
43+
-**Frontend**: Use Preact components in `frontend/src/`. Keep it simple.
44+
-**CSS**: Use the app-owned frontend styles in `frontend/src/styles/`.
45+
-**Specs**: RSpec for Ruby, build tests for frontend.
46+
- ✅ When a spec needs to tweak environment variables, wrap the example in `ClimateControl.modify` so state is restored automatically.
2047

21-
- ❌ Don’t depend on JS for core flows.
22-
- ❌ Don’t bypass SSRF filter or weaken CSP.
23-
- ❌ Don’t add databases, ORMs, or background jobs.
24-
- ❌ Don’t leak stack traces or secrets in responses.
48+
## Don't
2549

26-
## Project Structure
27-
28-
- `app.rb` – main Roda app
29-
- `app/` – core modules (config, cache, ssrf, health)
30-
- `routes/` – route handlers (`hash_branch`)
31-
- `helpers/` – pure helper modules (`module_function`)
32-
- `views/` – ERB templates
33-
- `public/` – static assets (CSS/JS, minimal)
34-
- `config/feeds.yml` – feed definitions
35-
- `spec/` – RSpec tests + VCR cassettes
50+
- ❌ Don't use Ruby's URI class or addressable gem directly. Strictly use `Html2rss::Url` only.
51+
- ❌ Don't bypass SSRF filter or weaken CSP.
52+
- ❌ Don't add databases, ORMs, or background jobs.
53+
- ❌ Don't leak stack traces or secrets in responses.
54+
- ❌ Don't reach into private API with `send(...)`; expose what you need at the module level instead.
55+
- ❌ Don't modify `frontend/dist/` - it's generated by build process.
56+
- ❌ NEVER expose the auth token a user provides.
3657

3758
## Environment
3859

@@ -41,6 +62,12 @@
4162
- `HEALTH_CHECK_USERNAME`, `HEALTH_CHECK_PASSWORD`
4263
- `SENTRY_DSN` (optional)
4364

65+
### Verification Steps
66+
67+
- Run `ruby -c app.rb` to check syntax
68+
- Run `bundle exec rspec` to verify tests
69+
- Check `bundle install` removes unused dependencies
70+
4471
## Style
4572

4673
- Add `# frozen_string_literal: true`

.github/workflows/bundle-update.yml

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)