Skip to content

jeffdc/gallformers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,716 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Gallformers

Uptime Response Time

The gallformers.org website - a comprehensive database and reference guide for galls.

Quick Start

# Install Elixir dependencies
mix setup

# Start the dev server
mix phx.server

Visit localhost:4000 in your browser.

Prerequisites

  • Elixir 1.19+ and OTP 28+
  • Node.js 20+ (for asset compilation)
  • PostgreSQL 16+
  • libvips (for image processing - resizing uploaded images)
  • Playwright (for E2E tests only — make e2e-setup installs browsers)

Installing Elixir

The easiest way on macOS:

brew install elixir

Or use asdf for version management:

asdf plugin add elixir
asdf plugin add erlang
asdf install erlang 28.0
asdf install elixir 1.19.0-otp-28

Installing PostgreSQL

# macOS
brew install postgresql@16
brew services start postgresql@16

# Ubuntu/Debian
sudo apt-get install postgresql postgresql-contrib

Installing libvips (for image processing)

# macOS
brew install libvips

# Ubuntu/Debian
sudo apt-get install libvips libvips-dev

Installing Playwright Browsers (for E2E tests)

make e2e-setup

Verify with make e2e-setup.

Database Setup

Ensure PostgreSQL is running locally, then:

# Create the database and run migrations
mix ecto.setup

# Or download a snapshot from production and restore locally
make download-db

Development

mix phx.server          # Start dev server
make test               # Rebuild test DB + run tests (excludes E2E)
mix test                # Run tests without rebuilding DB
mix format              # Format code
mix credo --strict      # Code quality
mix precommit           # Run all checks (do this before committing)
make ci                 # Full CI check (same as GitHub Actions)

Test Database

Tests use a separate PostgreSQL database (gallformers_test) built from:

  • Ecto migrations - Schema only (no production data)
  • priv/repo/test_seeds.sql - Minimal seed data for tests

make test rebuilds this automatically. Use make test-db to rebuild manually.

E2E Testing

Browser-based E2E tests use phoenix_test_playwright with Firefox. All tests run against a production data copy and are excluded from regular test runs and CI.

Requires Playwright browsers — see Prerequisites.

Running E2E Tests

make e2e                # Run all E2E tests
make e2e-changed        # Run only tests affected by changed files (smart)
make e2e-public         # Public pages only
make e2e-search         # Search functionality only
make e2e-browse         # Species/hosts/galls browsing only
make e2e-admin          # Admin pages only
make e2e-auth           # Authentication flows only

Debugging

make e2e-headed         # Run with visible browser
E2E_HEADED=1 make e2e-public   # Specific area with visible browser

Test Organization

E2E tests are organized by functional area in test/e2e/:

Directory Coverage
public/ Home, about, glossary, resources, explore
search/ Global search, ID tool
browse/ Species, hosts, galls detail pages
admin/ Admin dashboard, taxonomy admin, reclassify modal
auth/ Login, logout, protected routes

Writing E2E Tests

See test/support/e2e_case.ex for documentation. All E2E tests must be tagged:

defmodule GallformersWeb.E2E.MyTest do
  use GallformersWeb.E2ECase

  @moduletag :e2e
  @moduletag :e2e_public  # Area tag

  test "page loads", %{conn: conn} do
    conn
    |> visit("/")
    |> assert_has("h1", text: "Welcome")
  end
end

Project Structure

gallformers/
├── lib/                 # Elixir application code
├── assets/              # Frontend (JS, CSS, Tailwind)
├── priv/                # Static files, migrations, database
├── test/                # Tests
├── config/              # Phoenix configuration
└── services/            # Auxiliary services (boundaries, usda_plants)

Boundary Tiles (Range Maps)

Range maps use PMTiles vector tiles generated from Natural Earth shapefiles. In production, tiles are served via CloudFront from S3. In dev, tiles are served locally from priv/static/data/boundaries.pmtiles.

# Build tiles locally (~2 minutes)
cd services/boundaries
./build_boundaries.sh ../../priv/static/data/boundaries.pmtiles

# Or skip the local build and use production tiles:
TILES_URL=https://gallformers.org/tiles/boundaries.pmtiles mix phx.server

Requires: gdal, tippecanoe, jq. See services/boundaries/README.md and runbooks/map-tiles.md for full details.

Deployment

Production runs on Fly.io:

fly deploy              # Deploy to production
fly logs                # View logs
fly status              # Check status

See runbooks/ for operational procedures.

Creating Releases

The full release workflow:

  1. Commit and push to maingit push origin main
  2. Wait for CI — The "CI V2" workflow runs format, compile, credo, and tests
  3. Wait for deploy — On CI success, "Deploy V2" automatically deploys to Fly.io and runs smoke tests
  4. Verify deploy — Check that the site is working: fly status or visit gallformers.org
  5. Create the release — Run /release in Claude Code, review the generated notes, and approve

The /release skill handles tag naming, commit collection, and release note generation. Tags use CalVer format: v2026.2.6, with .2, .3 suffixes for multiple same-day releases. Release notes are published at github.com/jeffdc/gallformers/releases.

Backup Strategy

The PostgreSQL database backup strategy is TBD as part of the Postgres migration. Daily snapshots continue to be stored in S3:

  • Public (s3://gallformers-backups/public/) - Sanitized, PII removed
  • Private (s3://gallformers-full-backups/) - Full backup with PII

For restore procedures, see runbooks/restore-database.md. For AWS bucket details, see docs/ops/aws-private-backup-bucket.md.

PII Handling

The users table contains personally identifiable information:

Field Description
auth0_id Unique identifier from Auth0
display_name User's chosen display name
nickname Fallback name from Auth0
inaturalist_url Link to iNaturalist profile
social_url Link to social media
personal_url Link to personal website

Public database downloads are sanitized - all PII fields are set to NULL and auth0_id is replaced with a placeholder.

Authentication

  • Public site requires no authentication
  • Admin/curation features require Auth0 login
  • User management is handled via Auth0 console

External Resources

  • Production: gallformers.org
  • Images: AWS S3
  • Auth: Auth0
  • Domains: Namecheap (gallformers.org, gallformers.com)

Monitoring

Fly.io also sends automatic email alerts on OOM (out-of-memory) events.

Application Logs

All application logs (requests, errors, crashes) are structured JSON via LoggerJSON, written to a persistent file in production.

  • Production: /data/logs/app.log (size-rotated, 1 GB max)

Retrieve logs from production:

fly ssh sftp get /data/logs/app.log

Analyze with jq:

# Find request errors
cat app.log | jq -c 'select(.conn.status >= 500)'

# Find application errors
cat app.log | jq -c 'select(.severity == "error")'

See CODING_STANDARDS.md for detailed format and analysis examples.

Contributing

See CLAUDE.md for detailed development guidelines.

About

The gallformers site

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors