Skip to content

Commit 27ae2bd

Browse files
authored
feat: add 37signals Once platform compatibility (#37)
1 parent f6cb581 commit 27ae2bd

8 files changed

Lines changed: 104 additions & 2 deletions

File tree

.github/workflows/release.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ env:
1010

1111
permissions:
1212
contents: write
13+
packages: write
1314

1415
jobs:
1516
release:
@@ -85,3 +86,40 @@ jobs:
8586
files: slack-github-threads-v${{ steps.version.outputs.VERSION }}.tar.gz
8687
draft: false
8788
prerelease: false
89+
90+
docker:
91+
runs-on: ubuntu-latest
92+
needs: release
93+
94+
steps:
95+
- name: Checkout code
96+
uses: actions/checkout@v4
97+
98+
- name: Extract version from tag
99+
id: version
100+
run: |
101+
VERSION=${GITHUB_REF#refs/tags/v}
102+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
103+
104+
- name: Log in to GitHub Container Registry
105+
uses: docker/login-action@v3
106+
with:
107+
registry: ghcr.io
108+
username: ${{ github.actor }}
109+
password: ${{ secrets.GITHUB_TOKEN }}
110+
111+
- name: Set up Docker Buildx
112+
uses: docker/setup-buildx-action@v3
113+
114+
- name: Build and push Docker image
115+
uses: docker/build-push-action@v6
116+
with:
117+
context: .
118+
push: true
119+
tags: |
120+
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}
121+
ghcr.io/${{ github.repository }}:latest
122+
labels: |
123+
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
124+
org.opencontainers.image.version=${{ steps.version.outputs.VERSION }}
125+
org.opencontainers.image.revision=${{ github.sha }}

Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ ENV RACK_ENV=production
1010
ENV PORT=80
1111
EXPOSE 80
1212

13+
# Once platform: persistent storage and backup/restore hooks
14+
RUN mkdir -p /storage/log
15+
COPY once/hooks/ /hooks/
16+
RUN chmod +x /hooks/*
17+
1318
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ This project is configured for deployment using [Kamal](https://kamal-deploy.org
220220
kamal deploy
221221
```
222222

223+
## Once Platform
224+
225+
This app is compatible with [37signals' Once](https://once.com/) for self-hosted, single-tenant deployment. Docker images are published to GHCR on each release and can be installed directly from the Once installer. See [docs/ONCE.md](docs/ONCE.md) for setup details.
226+
223227
## Docker
224228

225229
You can also run the application using Docker:

app.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
set :debug_mode, ENV.fetch('DEBUG', nil) == 'true' || development?
2121

2222
# Setup logging
23-
log_dir = File.join(settings.root, 'log')
23+
log_dir = if Dir.exist?('/storage')
24+
'/storage/log'
25+
else
26+
File.join(settings.root, 'log')
27+
end
2428
FileUtils.mkdir_p(log_dir)
2529

2630
log_file = File.join(log_dir, "#{settings.environment}.log")

config/puma.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
environment ENV.fetch('RACK_ENV', 'production')
66

77
# Number of worker processes
8-
workers ENV.fetch('WEB_CONCURRENCY', 2)
8+
workers ENV.fetch('WEB_CONCURRENCY') { ENV.fetch('NUM_CPUS', 2) }
99

1010
# Number of threads per worker
1111
threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)

docs/ONCE.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Once Platform Deployment
2+
3+
This application is compatible with [37signals' Once](https://once.com/) platform.
4+
5+
## Installation
6+
7+
When the Once installer prompts for a Docker image path, enter:
8+
9+
```
10+
ghcr.io/markhallen/slack-github-threads:latest
11+
```
12+
13+
Or pin to a specific version (e.g., `ghcr.io/markhallen/slack-github-threads:1.0.0`).
14+
15+
## Compatibility
16+
17+
The app meets all Once requirements:
18+
19+
| Requirement | Status |
20+
|-------------|--------|
21+
| Docker container | Yes |
22+
| HTTP on port 80 | Yes |
23+
| Health check at `/up` | Yes (returns 200 OK) |
24+
| Persistent data in `/storage` | Yes (logs only) |
25+
26+
## Environment Variables
27+
28+
**Required** (set in Once admin UI):
29+
30+
- `SLACK_BOT_TOKEN` — Slack bot token (starts with `xoxb-`)
31+
- `GITHUB_TOKEN` — GitHub personal access token (starts with `ghp_`)
32+
33+
**Automatically provided by Once:**
34+
35+
- `NUM_CPUS` — Used to set Puma worker count (falls back to `WEB_CONCURRENCY`, then default of 2)
36+
- `SECRET_KEY_BASE` — Injected but not used by this app
37+
- `DISABLE_SSL` — Injected but not used (SSL is handled by the reverse proxy)
38+
39+
## Storage
40+
41+
The app is stateless — no database or file uploads. The `/storage` volume is used only for log files at `/storage/log/`. When running outside Once, logs fall back to `log/` in the app directory.
42+
43+
## Backup and Restore
44+
45+
The Once hook scripts at `/hooks/pre-backup` and `/hooks/post-restore` are no-ops because the app has no stateful data to back up. Source files are in `once/hooks/` in the repository.

once/hooks/post-restore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
# No-op: app has no database or stateful data to restore.
3+
exit 0

once/hooks/pre-backup

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
# No-op: app has no database or stateful data to flush before backup.
3+
exit 0

0 commit comments

Comments
 (0)