Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2d6ce5c
Cleanup for GitHub CI and Dependabot.
sprocktech-dev Mar 27, 2026
87a14ce
Added more options for external DB hosts, like TiDB.
sprocktech-dev Mar 27, 2026
d6f856f
Bump actions/checkout from 4 to 6
dependabot[bot] Mar 27, 2026
3149dd2
Bump actions/download-artifact from 4 to 8
dependabot[bot] Mar 27, 2026
f98d2eb
Bump actions/setup-python from 5 to 6
sprocktech-dev Mar 28, 2026
40f95eb
Bump actions/upload-artifact from 4 to 7
sprocktech-dev Mar 28, 2026
4835cb5
Bump aws-actions/configure-aws-credentials from 4 to 6
sprocktech-dev Mar 28, 2026
6e7d881
Bump python from 3.12-slim to 3.14-slim
sprocktech-dev Mar 28, 2026
2ec53b7
Update Python code to work with 3.14.
sprocktech-dev Mar 28, 2026
b616035
Revert to Python 3.12 code floor.
sprocktech-dev Mar 28, 2026
c0ce9cf
Improve AWS Lambda app startup post-deploy.
sprocktech-dev Mar 28, 2026
7d69be8
Simplified CHANGELOG.
sprocktech-dev Mar 28, 2026
d0374a1
Minor poetry lock update.
sprocktech-dev Mar 28, 2026
7abfa2a
Added notes about naming conventions to CONTRIBUTING.
sprocktech-dev Mar 28, 2026
a0f32de
Improved deploy script when vars conflict between local and cloud.
sprocktech-dev Mar 28, 2026
93a42ac
Merge branch 'feature/better-external-db-support' into test and resol…
sprocktech-dev Mar 28, 2026
bab1e49
Adjusted app DB user creation to better accomodate other DB providers…
sprocktech-dev Mar 28, 2026
1196b5b
Changed to automatically put in dot separator.
sprocktech-dev Mar 28, 2026
eb26d70
Changing env var to EXISTING_DATABASE_USERNAME_PREFIX.
sprocktech-dev Mar 28, 2026
5936b61
Merge branch 'feature/better-external-db-support' into test.
sprocktech-dev Mar 28, 2026
a05f13d
Adjustment for app DB username being too long.
sprocktech-dev Mar 28, 2026
2dd9275
Update to display name for synced messaged from mapped users.
sprocktech-dev Mar 28, 2026
d368465
Better handling in code for display name for synced messages from map…
sprocktech-dev Mar 28, 2026
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# Option A — MySQL (default): legacy vars or DATABASE_URL
DATABASE_BACKEND=mysql
DATABASE_HOST=127.0.0.1
# DATABASE_PORT defaults to 3306 (MySQL) or 5432 (PostgreSQL) when unset.
# Set explicitly for non-standard ports (e.g. TiDB Cloud often uses 4000).
# DATABASE_PORT=3306
DATABASE_USER=root
DATABASE_PASSWORD=rootpass
Expand Down
18 changes: 3 additions & 15 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/syncbot"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
groups:
minor-and-patch:
update-types: ["minor", "patch"]

- package-ecosystem: "pip"
directory: "/infra/aws/db_setup"
schedule:
interval: "weekly"
open-pull-requests-limit: 10

- package-ecosystem: "github-actions"
directory: "/"
schedule:
Expand All @@ -26,3 +11,6 @@ updates:
schedule:
interval: "weekly"
open-pull-requests-limit: 10
ignore:
- dependency-name: "python"
update-types: ["version-update:semver-major"]
24 changes: 13 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
with:
ref: ${{ github.head_ref || github.ref_name }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
fetch-depth: 0
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install Poetry and export plugin
Expand All @@ -34,24 +34,26 @@ jobs:
PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: |
poetry export -f requirements.txt --without-hashes -o syncbot/requirements.txt
if git diff --quiet syncbot/requirements.txt; then
echo "requirements.txt is already in sync."
echo "# Required for MySQL 8+ caching_sha2_password; pin for reproducible CI (sam build)." > infra/aws/db_setup/requirements.txt
grep -E "^(pymysql|psycopg2-binary|cryptography)==" syncbot/requirements.txt >> infra/aws/db_setup/requirements.txt
if git diff --quiet syncbot/requirements.txt infra/aws/db_setup/requirements.txt; then
echo "requirements.txt files are already in sync."
elif [[ -n "${PR_HEAD_REPO}" && "${PR_HEAD_REPO}" != "${GITHUB_REPOSITORY}" ]]; then
echo "::error::syncbot/requirements.txt is out of sync with poetry.lock. From the repo root run: poetry export -f requirements.txt --without-hashes -o syncbot/requirements.txt"
echo "::error::Requirements files are out of sync with poetry.lock. Commit with pre-commit installed (sync-requirements hook) or follow docs/DEVELOPMENT.md."
exit 1
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add syncbot/requirements.txt
git commit -m "chore: sync requirements.txt with poetry.lock"
git add syncbot/requirements.txt infra/aws/db_setup/requirements.txt
git commit -m "chore: sync requirements.txt files with poetry.lock"
git push
echo "::notice::requirements.txt was out of sync and has been auto-fixed."
echo "::notice::requirements.txt files were out of sync and have been auto-fixed."
fi

sam-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: aws-actions/setup-sam@v2
with:
use-installer: true
Expand All @@ -63,8 +65,8 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install dependencies
Expand Down
52 changes: 44 additions & 8 deletions .github/workflows/deploy-aws.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
if: vars.DEPLOY_TARGET != 'gcp'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- uses: aws-actions/setup-sam@v2
Expand All @@ -36,7 +36,7 @@ jobs:
sam validate -t infra/aws/template.yaml --lint
sam validate -t infra/aws/template.bootstrap.yaml --lint

- uses: aws-actions/configure-aws-credentials@v4
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}
Expand All @@ -50,7 +50,7 @@ jobs:
pip-audit -r infra/aws/db_setup/requirements.txt

- name: Publish artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: build-artifact
path: './.aws-sam/build'
Expand All @@ -61,13 +61,13 @@ jobs:
environment: test
needs: sam-build
steps:
- uses: aws-actions/configure-aws-credentials@v4
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}

- name: Download artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-artifact
path: './.aws-sam/build'
Expand Down Expand Up @@ -103,6 +103,11 @@ jobs:
ExistingDatabaseNetworkMode=${{ vars.EXISTING_DATABASE_NETWORK_MODE || 'public' }} \
ExistingDatabaseSubnetIdsCsv=${{ vars.EXISTING_DATABASE_SUBNET_IDS_CSV }} \
ExistingDatabaseLambdaSecurityGroupId=${{ vars.EXISTING_DATABASE_LAMBDA_SECURITY_GROUP_ID }} \
ExistingDatabasePort=${{ vars.EXISTING_DATABASE_PORT }} \
ExistingDatabaseCreateAppUser=${{ vars.EXISTING_DATABASE_CREATE_APP_USER || 'true' }} \
ExistingDatabaseCreateSchema=${{ vars.EXISTING_DATABASE_CREATE_SCHEMA || 'true' }} \
ExistingDatabaseUsernamePrefix=${{ vars.EXISTING_DATABASE_USERNAME_PREFIX }} \
ExistingDatabaseAppUsername=${{ vars.EXISTING_DATABASE_APP_USERNAME }} \
DatabaseSchema=${{ vars.DATABASE_SCHEMA }} \
LogLevel=${{ vars.LOG_LEVEL || 'INFO' }} \
RequireAdmin=${{ vars.REQUIRE_ADMIN || 'true' }} \
Expand All @@ -117,19 +122,32 @@ jobs:
SlackSigningSecret=${{ secrets.SLACK_SIGNING_SECRET }} \
$OVERRIDE_PARAM"

- name: Run migrations and warm up Lambda
run: |
FUNCTION_ARN=$(aws cloudformation describe-stacks \
--stack-name ${{ vars.AWS_STACK_NAME }} \
--query "Stacks[0].Outputs[?OutputKey=='SyncBotFunctionArn'].OutputValue" \
--output text)
aws lambda invoke \
--function-name "$FUNCTION_ARN" \
--payload '{"action":"migrate"}' \
--cli-binary-format raw-in-base64-out \
/tmp/migrate-response.json
cat /tmp/migrate-response.json

sam-deploy-prod:
if: github.ref == 'refs/heads/prod'
runs-on: ubuntu-latest
environment: prod
needs: sam-build
steps:
- uses: aws-actions/configure-aws-credentials@v4
- uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ vars.AWS_ROLE_TO_ASSUME }}
aws-region: ${{ vars.AWS_REGION }}

- name: Download artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: build-artifact
path: './.aws-sam/build'
Expand Down Expand Up @@ -165,6 +183,11 @@ jobs:
ExistingDatabaseNetworkMode=${{ vars.EXISTING_DATABASE_NETWORK_MODE || 'public' }} \
ExistingDatabaseSubnetIdsCsv=${{ vars.EXISTING_DATABASE_SUBNET_IDS_CSV }} \
ExistingDatabaseLambdaSecurityGroupId=${{ vars.EXISTING_DATABASE_LAMBDA_SECURITY_GROUP_ID }} \
ExistingDatabasePort=${{ vars.EXISTING_DATABASE_PORT }} \
ExistingDatabaseCreateAppUser=${{ vars.EXISTING_DATABASE_CREATE_APP_USER || 'true' }} \
ExistingDatabaseCreateSchema=${{ vars.EXISTING_DATABASE_CREATE_SCHEMA || 'true' }} \
ExistingDatabaseUsernamePrefix=${{ vars.EXISTING_DATABASE_USERNAME_PREFIX }} \
ExistingDatabaseAppUsername=${{ vars.EXISTING_DATABASE_APP_USERNAME }} \
DatabaseSchema=${{ vars.DATABASE_SCHEMA }} \
LogLevel=${{ vars.LOG_LEVEL || 'INFO' }} \
RequireAdmin=${{ vars.REQUIRE_ADMIN || 'true' }} \
Expand All @@ -178,3 +201,16 @@ jobs:
SlackClientSecret=${{ secrets.SLACK_CLIENT_SECRET }} \
SlackSigningSecret=${{ secrets.SLACK_SIGNING_SECRET }} \
$OVERRIDE_PARAM"

- name: Run migrations and warm up Lambda
run: |
FUNCTION_ARN=$(aws cloudformation describe-stacks \
--stack-name ${{ vars.AWS_STACK_NAME }} \
--query "Stacks[0].Outputs[?OutputKey=='SyncBotFunctionArn'].OutputValue" \
--output text)
aws lambda invoke \
--function-name "$FUNCTION_ARN" \
--payload '{"action":"migrate"}' \
--cli-binary-format raw-in-base64-out \
/tmp/migrate-response.json
cat /tmp/migrate-response.json
2 changes: 1 addition & 1 deletion .github/workflows/deploy-gcp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
if: vars.DEPLOY_TARGET == 'gcp'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

# Workload Identity Federation: authenticate without a key file
# - uses: google-github-actions/auth@v2
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ repos:
- repo: local
hooks:
- id: sync-requirements
name: Sync requirements.txt with poetry.lock
entry: bash -c 'poetry export -f requirements.txt --without-hashes -o syncbot/requirements.txt && git add syncbot/requirements.txt'
name: Sync requirements.txt files with poetry.lock
entry: bash -c 'poetry export -f requirements.txt --without-hashes -o syncbot/requirements.txt && echo "# Required for MySQL 8+ caching_sha2_password; pin for reproducible CI (sam build)." > infra/aws/db_setup/requirements.txt && grep -E "^(pymysql|psycopg2-binary|cryptography)==" syncbot/requirements.txt >> infra/aws/db_setup/requirements.txt && git add syncbot/requirements.txt infra/aws/db_setup/requirements.txt'
language: system
files: ^poetry\.lock$
pass_filenames: false
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,27 @@ All notable changes to this project are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- External DB deploy parameters: `ExistingDatabasePort`, `ExistingDatabaseCreateAppUser`, `ExistingDatabaseCreateSchema`, `ExistingDatabaseUsernamePrefix`, `ExistingDatabaseAppUsername` (AWS) / GCP equivalents — support TiDB Cloud and other managed DB providers with cluster-prefixed usernames and 32-char limits

### Changed

- Synced message author shows local display name and avatar for mapped users, including federated messages (no workspace suffix)
- Shortened default DB usernames: `sbadmin_{stage}` (was `syncbot_admin_{stage}`), `sbapp_{stage}` (was `syncbot_user_{stage}`). Existing RDS instances keep their original master username.
- Bumped GitHub Actions: `actions/checkout` v6, `actions/setup-python` v6, `actions/upload-artifact` v7, `actions/download-artifact` v8, `aws-actions/configure-aws-credentials` v6
- Dependabot: ignore semver-major updates for the Docker `python` image (keeps base image on Python 3.12.x line)
- AWS Lambda: Alembic migrations now run via a post-deploy invoke instead of on every cold start, fixing Slack ack timeouts after deployment; Cloud Run and local dev unchanged
- AWS Lambda memory increased from 128 MB to 256 MB for faster cold starts
- EventBridge keep-warm invokes now return a clean JSON response instead of falling through to Slack Bolt
- AWS bootstrap deploy policy: added `lambda:InvokeFunction` -- **re-run the deploy script (Bootstrap task) or `aws cloudformation deploy` the bootstrap stack to pick up this permission**

### Fixed

- Replaced deprecated `datetime.utcnow()` with `datetime.now(UTC)` in backup/migration export helpers

## [1.0.1] - 2026-03-26

### Changed
Expand Down
24 changes: 22 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing

Thanks for helping improve SyncBot.
Thanks for helping to improve SyncBot!

## Branching (upstream vs downstream)

Expand All @@ -11,7 +11,27 @@ The **upstream** repository ([F3Nation-Community/syncbot](https://github.com/F3N
| **`main`** | Tracks upstream. Use it to merge PRs and to **sync with the upstream repository** (`git pull upstream main`, etc.). |
| **`test`** / **`prod`** | On your fork, use these for **deployments**: GitHub Actions deploy workflows run on **push** to `test` and `prod` (see [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)). |

Typical flow: develop on a feature branch → open a PR to **`main`** → merge → when ready to deploy, merge **`main`** into **`test`** or **`prod`** on your fork.
Typical flow: develop a fix or new feature on a branch in your repo → test and deploy to your infra → open a PR to **`upstream/main`**.

### Branch Naming Conventions

Format: `<type>/<description>` or `<type>/<ticket>-<description>`

Types:

- feature/ New functionality
- bugfix/ Bug fixes for existing features
- hotfix/ Urgent production issues
- refactor/ Code improvements without behavior changes
- docs/ Documentation only changes
- chore/ Build process, dependency updates, etc.

Rules:

- Use lowercase
- Separate words with hyphens
- Keep descriptions under 50 characters
- Be specific: feature/user-auth not feature/auth

## Workflow

Expand Down
4 changes: 3 additions & 1 deletion deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,9 @@ main() {
poetry update --quiet
if poetry self show plugins 2>/dev/null | grep -q poetry-plugin-export; then
poetry export -f requirements.txt --without-hashes -o "$REPO_ROOT/syncbot/requirements.txt"
echo "syncbot/requirements.txt updated from poetry.lock."
echo "# Required for MySQL 8+ caching_sha2_password; pin for reproducible CI (sam build)." > "$REPO_ROOT/infra/aws/db_setup/requirements.txt"
grep -E "^(pymysql|psycopg2-binary|cryptography)==" "$REPO_ROOT/syncbot/requirements.txt" >> "$REPO_ROOT/infra/aws/db_setup/requirements.txt"
echo "syncbot/requirements.txt and infra/aws/db_setup/requirements.txt updated from poetry.lock."
else
echo "Warning: poetry-plugin-export not installed. Run: poetry self add poetry-plugin-export" >&2
echo "Skipping requirements.txt sync." >&2
Expand Down
2 changes: 2 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ flowchart TB

All infrastructure is defined in `infra/aws/template.yaml` (AWS SAM). Dashed lines indicate resources that are conditionally created — when `Existing*` parameters are set, those resources are skipped.

**Lambda cold start vs Slack acks:** The main function uses **256 MB** memory (faster init than 128 MB). Alembic migrations run only when the function is invoked with `{"action":"migrate"}` (post-deploy in CI), not on every cold start, so the first Slack interaction after deploy can ack within Slack’s time limit. EventBridge keep-warm ScheduleV2 invokes are handled in `app.handler` with a trivial JSON response instead of the Slack Bolt adapter.

## Security & Hardening

| Layer | Protection |
Expand Down
Loading
Loading