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
33 changes: 33 additions & 0 deletions plugins/upsun/skills/upsun/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,39 @@ 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

For framework- and language-specific starter configs and examples, load the appropriate on-demand reference file from the list below. These complement the core templates in [references/config.md](references/config.md).

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

## How Upsun works

Upsun is a git-driven cloud application platform. Key concepts:
Expand Down
130 changes: 130 additions & 0 deletions plugins/upsun/skills/upsun/references/config/directus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
## 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+ (this example uses nodejs:22 runtime)

Template Usage: Adapt database service and authentication configuration to project requirements. Exclude explanatory comments from production configurations.

```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
EOF
Comment on lines +25 to +37
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.

This heredoc expands $REDIS_HOST/$REDIS_PORT at build time, but relationship env vars are runtime-only on Upsun (the build hook has no service access). That means the generated .environment will contain empty Redis host/port values. Write literal variable references into .environment (escape $ or use a quoted heredoc delimiter) so they resolve when .environment is sourced at runtime.

Copilot uses AI. Check for mistakes.

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 +45 to +47
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 first-deploy bootstrap sets hard-coded Directus admin credentials (admin@example.com / password). Even with the note below, this is an insecure default that can be exploited on first deployment. Prefer generating a strong random password (e.g., derived from PLATFORM_PROJECT_ENTROPY) and/or requiring the user to provide credentials via Upsun variables/secrets before running bootstrap.

Suggested change
export ADMIN_EMAIL='admin@example.com'
export ADMIN_PASSWORD='password'
if [ -z "${ADMIN_EMAIL:-}" ]; then
echo 'ERROR: ADMIN_EMAIL must be provided via an Upsun environment variable or secret before first deploy.' >&2
exit 1
fi
if [ -z "${ADMIN_PASSWORD:-}" ]; then
export ADMIN_PASSWORD="$(node -e "const crypto = require('crypto'); const entropy = process.env.PLATFORM_PROJECT_ENTROPY || crypto.randomBytes(32).toString('hex'); process.stdout.write(crypto.createHash('sha256').update(entropy + ':directus-admin').digest('base64').replace(/[^A-Za-z0-9]/g, '').slice(0, 32));")"
echo 'ADMIN_PASSWORD was not provided; a strong password was generated for this bootstrap run. Store a permanent admin password in an Upsun secret to avoid rotation surprises on rebuilds.' >&2
fi

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: {}

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.
- 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

Notes and gotchas:
- If your application source lives in a repository subdirectory, add a `source:
root: ./path` block under `applications.directus` before `type:`.
- Ensure you change the default admin credentials set during bootstrap.
- Remove explanatory comments from production YAML to keep config minimal and deterministic.
93 changes: 93 additions & 0 deletions plugins/upsun/skills/upsun/references/config/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.

**Template Usage**: Customize this configuration for project-specific requirements. Do not include explanatory comments in production configurations.

```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 ... "\n# Upsun configuration" is not portable in POSIX sh: many echo implementations won’t interpret \n without -e, so this can literally write \n# Upsun configuration into settings.py. Use printf (or multiple echo calls without embedded escape sequences) to reliably add a blank line and comment.

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: {}

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