Skip to content
Closed
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
35 changes: 35 additions & 0 deletions plugins/upsun/skills/upsun/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,41 @@ You are a developer's assistant for Upsun. Help them ship, debug, and iterate fa
Docs reference: https://developer.upsun.com/docs/get-started
Full LLM-friendly doc index: https://developer.upsun.com/llms.txt

Per-framework references

When generating or adjusting .upsun/config.yaml for a detected framework or language, consult the corresponding framework-specific reference below. These files contain runtime- and framework-specific hints, example hooks, build/deploy notes, and typical service relationships.

- [references/directus.md](references/directus.md)
- [references/django.md](references/django.md)
- [references/drupal.md](references/drupal.md)
- [references/echo.md](references/echo.md)
- [references/express.md](references/express.md)
- [references/flask.md](references/flask.md)
- [references/gatsby.md](references/gatsby.md)
- [references/gin.md](references/gin.md)
- [references/go.md](references/go.md)
- [references/hugo.md](references/hugo.md)
- [references/jekyll.md](references/jekyll.md)
- [references/js.md](references/js.md)
- [references/laravel.md](references/laravel.md)
- [references/nextjs.md](references/nextjs.md)
- [references/nuxt.md](references/nuxt.md)
- [references/php.md](references/php.md)
- [references/python.md](references/python.md)
- [references/rails.md](references/rails.md)
- [references/reactjs.md](references/reactjs.md)
- [references/ruby.md](references/ruby.md)
- [references/sinatra.md](references/sinatra.md)
- [references/static.md](references/static.md)
- [references/strapi.md](references/strapi.md)
- [references/sylius.md](references/sylius.md)
- [references/symfony.md](references/symfony.md)
- [references/vite.md](references/vite.md)
- [references/vuejs.md](references/vuejs.md)
- [references/wordpress.md](references/wordpress.md)

See [references/config.md](references/config.md) for the canonical .upsun/config.yaml structure and common examples.

## How Upsun works

Upsun is a git-driven cloud application platform. Key concepts:
Expand Down
124 changes: 124 additions & 0 deletions plugins/upsun/skills/upsun/references/directus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
## Directus Application Configuration

Reference configuration for Directus applications on Upsun.

**Framework Priority**: Use Directus-specific configuration instead of generic Node.js guidance when both apply.

**Minimum Requirements**: Node.js 18+ required for Directus compatibility

```yaml
applications:
directus:
type: nodejs:22

build:
flavor: none

hooks:
build: |
set -ex
npm install
npm run argon2-rebuild

# Create .environment file with dynamic variables
cat > .environment <<EOF
CACHE_ENABLED=true
CACHE_STORE=redis
REDIS_HOST=$REDIS_HOST
REDIS_PORT=$REDIS_PORT
RATE_LIMITER_ENABLED=true
RATE_LIMITER_STORE=redis
RATE_LIMITER_REDIS_HOST=$REDIS_HOST
RATE_LIMITER_REDIS_PORT=$REDIS_PORT
KEY=$PLATFORM_PROJECT_ENTROPY
SECRET=$PLATFORM_PROJECT_ENTROPY
Comment on lines +23 to +34
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .environment heredoc will expand $REDIS_HOST / $REDIS_PORT at build time, but build hooks don’t have service relationship env vars (so these will be empty). Also, the lines written are not exported, so they won’t reach the Node process. Use a quoted heredoc (to defer expansion to runtime) and write export … entries instead (or avoid duplicating REDIS_* entirely and let Directus read the relationship vars directly).

Suggested change
# Create .environment file with dynamic variables
cat > .environment <<EOF
CACHE_ENABLED=true
CACHE_STORE=redis
REDIS_HOST=$REDIS_HOST
REDIS_PORT=$REDIS_PORT
RATE_LIMITER_ENABLED=true
RATE_LIMITER_STORE=redis
RATE_LIMITER_REDIS_HOST=$REDIS_HOST
RATE_LIMITER_REDIS_PORT=$REDIS_PORT
KEY=$PLATFORM_PROJECT_ENTROPY
SECRET=$PLATFORM_PROJECT_ENTROPY
# Create .environment file with runtime-expanded variables
cat > .environment <<'EOF'
export CACHE_ENABLED="true"
export CACHE_STORE="redis"
export REDIS_HOST="$REDIS_HOST"
export REDIS_PORT="$REDIS_PORT"
export RATE_LIMITER_ENABLED="true"
export RATE_LIMITER_STORE="redis"
export RATE_LIMITER_REDIS_HOST="$REDIS_HOST"
export RATE_LIMITER_REDIS_PORT="$REDIS_PORT"
export KEY="$PLATFORM_PROJECT_ENTROPY"
export SECRET="$PLATFORM_PROJECT_ENTROPY"

Copilot uses AI. Check for mistakes.
EOF

deploy: |
set -ex
if [ ! -f var/upsun.installed ]; then
echo 'Bootstrapping Directus on first deploy...'

export PROJECT_NAME='Directus on Upsun'
export ADMIN_EMAIL='admin@example.com'
export ADMIN_PASSWORD='password'
Comment on lines +43 to +44
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deploy hook hard-codes default admin credentials (admin@example.com / password). Even if this is “first deploy only”, it’s an insecure default that can be accidentally exposed. Prefer generating a random password, requiring explicit env vars, or documenting a manual bootstrap step instead of committing defaults.

Suggested change
export ADMIN_EMAIL='admin@example.com'
export ADMIN_PASSWORD='password'
: "${ADMIN_EMAIL:?Set ADMIN_EMAIL in the environment before first deploy}"
: "${ADMIN_PASSWORD:?Set ADMIN_PASSWORD in the environment before first deploy}"

Copilot uses AI. Check for mistakes.

npx directus bootstrap

touch var/upsun.installed
else
npx directus database migrate:latest
fi

web:
commands:
start: npx directus start

variables:
env:
NODE_ENV: production
DB_CLIENT: pg
EXTENSIONS_PATH: ./extensions
UPLOADS_LOCATION: local
UPLOADS_LOCAL_ROOT: ./uploads

mounts:
var:
source: storage
uploads:
source: storage

relationships:
db: {}
redis: {}
Comment on lines +72 to +73
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relationships entries should bind to a service endpoint (string form), not an empty mapping. The canonical structure in references/config.md uses e.g. relationships: database: 'db:postgresql' (config.md:64). Update this example to use the correct relationship binding for db and redis.

Suggested change
db: {}
redis: {}
db: 'db:postgresql'
redis: 'redis:redis'

Copilot uses AI. Check for mistakes.

services:
db:
type: postgresql:16

redis:
type: redis:7.4

routes:
https://{default}/:
type: upstream
upstream: directus:http
cache:
enabled: true
default_ttl: 0
cookies: ['*']
headers: ['Accept', 'Accept-Language']

https://www.{default}/:
type: redirect
to: https://{default}/
```

## Configuration Details

- **Database Service**: PostgreSQL 16 service. Service named "db" exposes standard DB_ environment variables.
- **Caching**: Single Redis service for both application caching and rate limiting. Service named "redis" exposes standard REDIS_ environment variables (redis:7.4).
- **Environment File**: Dynamic variables written to `.environment` file during build hook for proper substitution.
- **Authentication**: Uses platform project entropy for key/secret generation ensuring unique values per environment.
- **File Storage**: Local upload storage with persistent mount for file retention.
- **First Deploy**: Automated bootstrap process creates admin user on initial deployment.
- **Database Migration**: Automatic schema updates on subsequent deployments.
- **Argon2 Rebuild**: Required dependency rebuild for password hashing compatibility.
- **Upload Directory**: Persistent storage mount for file uploads with local storage driver.
- **Extensions**: Support for custom Directus extensions in dedicated directory.

## Security Notes

- Change default admin credentials immediately after first login.
- Project entropy provides cryptographically secure keys unique to each environment.
- Database credentials managed automatically via service relationships.
- File uploads restricted to designated mount for security isolation.

## Build Process

1. Install Node.js dependencies including Directus core.
2. Rebuild Argon2 native bindings for platform compatibility.
3. Create `.environment` file with dynamic Redis and authentication variables.
4. Bootstrap database and admin user on first deployment.
5. Run schema migrations on subsequent deployments.
6. Start Directus server with production configuration.
93 changes: 93 additions & 0 deletions plugins/upsun/skills/upsun/references/django.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
## Django Application Configuration

Reference configuration for Django web applications on Upsun.

Framework Variants: For Django derivatives (nanodjango, microdjango), adapt management commands accordingly.

Example configuration:

```yaml
applications:
site:
type: python:3.13

dependencies:
python3:
uv: '*'

hooks:
build: |
set -ex
uv sync --frozen --no-dev --no-managed-python

# Auto-detect WSGI module name
export WSGI_NAME=$(basename "$(dirname "$(find . -maxdepth 4 -path './.*' -prune -o -name wsgi.py -print | head -n1)")")
if [ -z "$WSGI_NAME" ]; then
echo >&2 'Failed to find WSGI module name'
exit 1
fi

# Configure Django settings for Upsun
export settings_dir=$(basename "$(dirname "$(find . -maxdepth 4 -path './.*' -prune -o -name settings.py -print | head -n1)")")
if [ -n "$settings_dir" ]; then
echo >> "$settings_dir"/settings.py "\n# Upsun configuration"
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

echo without -e won’t reliably interpret \n in POSIX shells (Upsun hooks run under Dash). This line is likely to literally write \n# Upsun configuration into settings.py. Use printf (or a separate blank echo) to ensure a real newline is written.

Suggested change
echo >> "$settings_dir"/settings.py "\n# Upsun configuration"
printf '\n# Upsun configuration\n' >> "$settings_dir"/settings.py

Copilot uses AI. Check for mistakes.
echo >> "$settings_dir"/settings.py 'ALLOWED_HOSTS = ["*"]'
if ! grep -q STATIC_ROOT "$settings_dir"/settings.py; then
echo >> "$settings_dir"/settings.py 'STATIC_ROOT = BASE_DIR / "static"'
echo >> "$settings_dir"/settings.py 'STATIC_URL = "/static/"'
fi
fi

uv run python manage.py collectstatic --noinput
echo >> .environment "export WSGI_NAME=$WSGI_NAME"
echo >> .environment 'export DATABASE_URL="$DB_SCHEME://$DB_USERNAME:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_PATH"'

deploy: |
set -ex
uv run python manage.py migrate --noinput

web:
commands:
start: uv run gunicorn "$WSGI_NAME".wsgi:application --bind 0.0.0.0:$PORT

locations:
/:
passthru: true
/static:
allow: true
expires: 1h
root: static
/media:
expires: 1h
root: media
scripts: false

variables:
env:
UV_LINK_MODE: copy

mounts:
media:
source: storage
logs:
source: storage

relationships:
db: {}
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

relationships entries should bind to a service endpoint (string form), not an empty mapping. The canonical structure in references/config.md uses e.g. relationships: database: 'db:postgresql' (config.md:64). Update this example to bind db to the db service endpoint.

Suggested change
db: {}
db: 'db:postgresql'

Copilot uses AI. Check for mistakes.

services:
db:
type: postgresql:16
```

Configuration Details:

- Python Version: Match project requirements from pyproject.toml or requirements.txt
- Package Manager: Adapt for pip, poetry, or pipenv based on project setup
- WSGI Detection: Automatically finds Django project structure
- Static Configuration: Auto-configures STATIC_ROOT and STATIC_URL if missing
- Database Integration: Sets up DATABASE_URL for django-environ or dj-database-url
- File Handling:
- /static: Cached static assets
- /media: User uploads with storage mount
- uv Optimization: UV_LINK_MODE=copy prevents symlink issues in containers
Loading
Loading