Skip to content
Merged
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
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ env:

permissions:
contents: write
packages: write

jobs:
release:
Expand Down Expand Up @@ -85,3 +86,40 @@ jobs:
files: slack-github-threads-v${{ steps.version.outputs.VERSION }}.tar.gz
draft: false
prerelease: false

docker:
runs-on: ubuntu-latest
needs: release

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}
ghcr.io/${{ github.repository }}:latest
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.version=${{ steps.version.outputs.VERSION }}
org.opencontainers.image.revision=${{ github.sha }}
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ ENV RACK_ENV=production
ENV PORT=80
EXPOSE 80

# Once platform: persistent storage and backup/restore hooks
RUN mkdir -p /storage/log
COPY once/hooks/ /hooks/
RUN chmod +x /hooks/*

CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ This project is configured for deployment using [Kamal](https://kamal-deploy.org
kamal deploy
```

## Once Platform

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.

## Docker

You can also run the application using Docker:
Expand Down
6 changes: 5 additions & 1 deletion app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
set :debug_mode, ENV.fetch('DEBUG', nil) == 'true' || development?

# Setup logging
log_dir = File.join(settings.root, 'log')
log_dir = if Dir.exist?('/storage')
'/storage/log'
else
File.join(settings.root, 'log')
end
FileUtils.mkdir_p(log_dir)

log_file = File.join(log_dir, "#{settings.environment}.log")
Expand Down
2 changes: 1 addition & 1 deletion config/puma.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
environment ENV.fetch('RACK_ENV', 'production')

# Number of worker processes
workers ENV.fetch('WEB_CONCURRENCY', 2)
workers ENV.fetch('WEB_CONCURRENCY') { ENV.fetch('NUM_CPUS', 2) }

# Number of threads per worker
threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
Expand Down
45 changes: 45 additions & 0 deletions docs/ONCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Once Platform Deployment

This application is compatible with [37signals' Once](https://once.com/) platform.

## Installation

When the Once installer prompts for a Docker image path, enter:

```
ghcr.io/markhallen/slack-github-threads:latest
```

Or pin to a specific version (e.g., `ghcr.io/markhallen/slack-github-threads:1.0.0`).

## Compatibility

The app meets all Once requirements:

| Requirement | Status |
|-------------|--------|
| Docker container | Yes |
| HTTP on port 80 | Yes |
| Health check at `/up` | Yes (returns 200 OK) |
| Persistent data in `/storage` | Yes (logs only) |

## Environment Variables

**Required** (set in Once admin UI):

- `SLACK_BOT_TOKEN` — Slack bot token (starts with `xoxb-`)
- `GITHUB_TOKEN` — GitHub personal access token (starts with `ghp_`)

**Automatically provided by Once:**

- `NUM_CPUS` — Used to set Puma worker count (falls back to `WEB_CONCURRENCY`, then default of 2)
- `SECRET_KEY_BASE` — Injected but not used by this app
- `DISABLE_SSL` — Injected but not used (SSL is handled by the reverse proxy)

## Storage

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.

## Backup and Restore

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.
3 changes: 3 additions & 0 deletions once/hooks/post-restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
# No-op: app has no database or stateful data to restore.
exit 0
3 changes: 3 additions & 0 deletions once/hooks/pre-backup
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh
# No-op: app has no database or stateful data to flush before backup.
exit 0
Loading