| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583 |
- name: UNA CI
- on:
- push:
- branches: [ "master" ]
- pull_request:
- branches: [ "master" ]
- types: [opened, synchronize, reopened, closed]
- permissions:
- contents: read
- pull-requests: write
- # A newer push to a PR, or closing it, cancels that PR's run in progress, so the teardown can't
- # race a preview deploy that is still creating the environment. Master pushes never cancel.
- concurrency:
- group: ci-${{ github.event.pull_request.number && format('pr-{0}', github.event.pull_request.number) || github.sha }}
- cancel-in-progress: ${{ github.event_name == 'pull_request' }}
- jobs:
- dependencies:
- # Composer (plugins/) and Yarn (plugins_public/, via the postinstall build) are the same
- # tree for every PHP version, so they are installed once and the test legs download them.
- name: Install dependencies
- if: github.event_name == 'push' || github.event.action != 'closed'
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- # WARNING: after `composer update` or `composer upgrade`, replace this commit SHA
- # with the current shivammathur/setup-php release. Both Setup PHP steps must match.
- - name: Setup PHP
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
- with:
- php-version: '8.5'
- extensions: intl, curl, gd, mbstring, zip, exif, pdo_mysql
- ini-values: short_open_tag=On, memory_limit=256M
- tools: composer
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: '24'
- cache: yarn
- - name: Install PHP dependencies
- run: composer install --prefer-dist --no-progress --no-dev
- - name: Install JS dependencies
- run: yarn install --frozen-lockfile
- - name: Upload dependencies
- uses: actions/upload-artifact@v4
- with:
- name: una-dependencies
- retention-days: 1
- include-hidden-files: true
- if-no-files-found: error
- path: |
- plugins
- plugins_public
- test:
- # Units and Integration each get a runner per PHP version. Deploy waits on every leg, and a
- # failure in one does not cancel the other. The package is built only on the PHP 8.5 Units
- # leg so it is uploaded once.
- name: ${{ matrix.suite == 'Units' && 'Unit tests' || 'Integration tests' }} (PHP ${{ matrix.php }})
- needs: dependencies
- if: github.event_name == 'push' || github.event.action != 'closed'
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- suite: [Units, Integration]
- php: ['8.3', '8.4', '8.5']
- services:
- mysql:
- image: mariadb:11
- env:
- MYSQL_ROOT_PASSWORD: root
- MYSQL_DATABASE: una
- MYSQL_USER: una
- MYSQL_PASSWORD: una
- ports:
- - 3306:3306
- options: >-
- --health-cmd="healthcheck.sh --connect --innodb_initialized"
- --health-interval=10s
- --health-timeout=5s
- --health-retries=10
- steps:
- - uses: actions/checkout@v4
- # WARNING: after `composer update` or `composer upgrade`, replace this commit SHA
- # with the current shivammathur/setup-php release. Both Setup PHP steps must match.
- - name: Setup PHP
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
- with:
- php-version: ${{ matrix.php }}
- extensions: intl, curl, gd, mbstring, zip, exif, pdo_mysql
- ini-values: short_open_tag=On, memory_limit=256M
- tools: phing, phpunit:12
- - name: Download dependencies
- uses: actions/download-artifact@v4
- with:
- name: una-dependencies
- - name: Prepare writable paths
- run: |
- mkdir -p cache cache_public logs tmp storage
- chmod a+rwX inc cache cache_public logs tmp storage
- test -f plugins/ffmpeg/ffmpeg.exe && chmod 0755 plugins/ffmpeg/ffmpeg.exe
- - name: Install UNA
- working-directory: install
- run: >
- php cmd.php
- -m bx_antispam
- --db_host=127.0.0.1
- --db_name=una
- --db_user=una
- --db_password=una
- --server_http_host=localhost
- --server_php_self=/install/index.php
- - name: Run PHPUnit
- run: phpunit -c tests/phpunit.xml --testsuite "${{ matrix.suite }}"
- - name: Build package
- if: success() && matrix.suite == 'Units' && matrix.php == '8.5'
- run: phing package -Dbuild=${{ github.run_number }}
- - name: Upload package artifact
- if: success() && matrix.suite == 'Units' && matrix.php == '8.5'
- uses: actions/upload-artifact@v4
- with:
- name: una-package
- path: packages/*.zip
- if-no-files-found: error
- neo-checks:
- # The jobs above don't look at neo/. `yarn lint` and `yarn typecheck` aren't clean on master
- # yet, so this fails only on errors the PR adds: both run on the PR and on its base, and
- # neo/scripts/check-new-errors.js compares them per file and rule (line numbers ignored).
- name: NEO lint and typecheck
- if: github.event_name == 'pull_request' && github.event.action != 'closed'
- runs-on: ubuntu-latest
- env:
- BASE_REF: ${{ github.event.pull_request.base.ref }}
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
- - name: Check for NEO changes
- id: changes
- run: |
- if git diff --quiet "origin/$BASE_REF...HEAD" -- neo/; then
- echo "neo=false" >> "$GITHUB_OUTPUT"
- echo "No changes under neo/, nothing to check."
- else
- echo "neo=true" >> "$GITHUB_OUTPUT"
- fi
- - name: Setup Node
- if: steps.changes.outputs.neo == 'true'
- uses: actions/setup-node@v4
- with:
- node-version: '24'
- - name: Install NEO dependencies
- if: steps.changes.outputs.neo == 'true'
- working-directory: neo
- # yarn.lock isn't committed (see neo/AGENTS.md), so this resolves the package.json ranges.
- # The npm registry now and then answers 502 for one tarball; retry instead of failing the PR.
- run: |
- # --ignore-scripts keeps dependencies' install scripts from running in CI. NEO's own
- # postinstall is only patch-package, so run that one explicitly: the result is the
- # same lint and typecheck output as a plain install.
- for attempt in 1 2 3; do
- if yarn install --ignore-scripts --non-interactive --network-timeout 600000; then
- yarn patch-package
- exit 0
- fi
- echo "yarn install failed (attempt $attempt of 3)"
- sleep 15
- done
- exit 1
- - name: Lint and typecheck the PR
- if: steps.changes.outputs.neo == 'true'
- working-directory: neo
- run: |
- yarn --silent lint -f json -o "$RUNNER_TEMP/lint-head.json" || true
- node scripts/typecheck.js > "$RUNNER_TEMP/typecheck-head.txt" 2>&1 || true
- - name: Lint and typecheck the base branch
- if: steps.changes.outputs.neo == 'true'
- run: |
- git worktree add "$RUNNER_TEMP/base" "origin/$BASE_REF"
- cd "$RUNNER_TEMP/base/neo"
- # Reuse the PR's install instead of a second `yarn install`.
- ln -s "$GITHUB_WORKSPACE/neo/node_modules" node_modules
- for app in apps/next apps/expo; do
- if [ -d "$GITHUB_WORKSPACE/neo/$app/node_modules" ]; then
- ln -s "$GITHUB_WORKSPACE/neo/$app/node_modules" "$app/node_modules"
- fi
- done
- yarn --silent lint -f json -o "$RUNNER_TEMP/lint-base.json" || true
- node scripts/typecheck.js > "$RUNNER_TEMP/typecheck-base.txt" 2>&1 || true
- - name: Fail on new errors
- if: steps.changes.outputs.neo == 'true'
- working-directory: neo
- run: >-
- node scripts/check-new-errors.js
- --lint "$RUNNER_TEMP/lint-head.json" "$RUNNER_TEMP/lint-base.json"
- --typecheck "$RUNNER_TEMP/typecheck-head.txt" "$RUNNER_TEMP/typecheck-base.txt"
- preview-env:
- # Same-repo PRs only: forks and Dependabot get no secrets, and Dependabot PRs need no preview.
- name: Prepare PR environment
- if: >-
- github.event_name == 'pull_request' && github.event.action != 'closed'
- && github.event.pull_request.head.repo.full_name == github.repository
- && github.event.pull_request.user.login != 'dependabot[bot]'
- runs-on: ubuntu-latest
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_BASE_ENVIRONMENT: ${{ secrets.RAILWAY_ENVIRONMENT }}
- RAILWAY_TEMPLATE_ENVIRONMENT: pr-template
- RAILWAY_PR_ENVIRONMENT: pr-${{ github.event.pull_request.number }}
- UNA_PREVIEW_API_KEY: ${{ secrets.UNA_PREVIEW_API_KEY }}
- steps:
- - uses: actions/checkout@v4
- with:
- sparse-checkout: |
- .github/actions
- scripts/railway
- - uses: ./.github/actions/railway-cli
- - name: Provision PR environment
- run: |
- if [ -z "$RAILWAY_API_TOKEN" ] || [ -z "$RAILWAY_PROJECT_ID" ]; then
- echo "Set secrets RAILWAY_API_TOKEN (account token) and RAILWAY_PROJECT_ID" >&2
- exit 1
- fi
- base="${RAILWAY_BASE_ENVIRONMENT:-production}"
- template="${RAILWAY_TEMPLATE_ENVIRONMENT:-pr-template}"
- railway link --project "$RAILWAY_PROJECT_ID" --environment "$base" --json
- # Railway services are project-scoped: mariadb, una and neo already exist, so `railway add`
- # cannot create them again and a plain new environment holds no instances of them
- # (every preview after the first failed this way). A PR environment therefore gets
- # its instances by duplicating the template environment. An environment left over
- # from a failed run has no una instance; recreate it.
- if ! railway service status --service una --environment "$RAILWAY_PR_ENVIRONMENT" >/dev/null 2>&1; then
- railway environment delete "$RAILWAY_PR_ENVIRONMENT" -y >/dev/null 2>&1 || true
- railway environment new "$RAILWAY_PR_ENVIRONMENT" --duplicate "$template" --json
- # a duplicate may carry the template's deployments; the builds below replace them
- railway down --service una --environment "$RAILWAY_PR_ENVIRONMENT" -y >/dev/null 2>&1 || true
- railway down --service neo --environment "$RAILWAY_PR_ENVIRONMENT" -y >/dev/null 2>&1 || true
- fi
- railway link --project "$RAILWAY_PROJECT_ID" --environment "$RAILWAY_PR_ENVIRONMENT" --json
- # An environment duplicated before the neo service existed lacks it.
- scripts/railway/neo.sh instance "$RAILWAY_PR_ENVIRONMENT"
- # The duplicated una instance is what `railway up` deploys into. Setting its variables
- # doubles as the readiness check: it fails with "ServiceInstance not found" until it exists.
- for i in $(seq 1 12); do
- railway variable set --service una --environment "$RAILWAY_PR_ENVIRONMENT" --skip-deploys \
- MYSQLHOST=mariadb.railway.internal \
- MYSQLPORT=3306 \
- MYSQLDATABASE=una \
- MYSQLUSER=una \
- MYSQLPASSWORD=una \
- UNA_DEMO_SEED=1 \
- ${UNA_PREVIEW_API_KEY:+UNA_API_KEY="$UNA_PREVIEW_API_KEY"} \
- && break
- sleep 5
- done
- # Previews live on their Railway domains (<service>-pr-<n>.up.railway.app), which share
- # Railway's wildcard certificate. Custom pr-<n> / api-pr-<n> hosts on unacms.app needed two
- # Let's Encrypt certificates per PR, and a busy week ran into Let's Encrypt's rate limit
- # (PR #5934 got none). UNA_AUTO_HOSTNAME makes UNA build its URLs from its host.
- railway domain --service una --environment "$RAILWAY_PR_ENVIRONMENT" || true
- railway domain --service neo --environment "$RAILWAY_PR_ENVIRONMENT" || true
- service_host() {
- railway domain list --service "$1" --environment "$RAILWAY_PR_ENVIRONMENT" --json | jq -r '.domains[] | select(.type == "service") | .domain' | head -n1
- }
- una_host=$(service_host una)
- neo_host=$(service_host neo)
- [ -n "$una_host" ] || { echo "No Railway domain for una" >&2; exit 1; }
- [ -n "$neo_host" ] || { echo "No Railway domain for neo" >&2; exit 1; }
- una_url="https://${una_host#https://}"
- neo_url="https://${neo_host#https://}"
- railway variable set --service una --environment "$RAILWAY_PR_ENVIRONMENT" --skip-deploys UNA_CLIENT_URL="$neo_url"
- scripts/railway/neo.sh vars "$RAILWAY_PR_ENVIRONMENT" "$una_url" "$neo_url"
- deploy:
- name: Deploy PR to Railway (una)
- needs: [test, preview-env]
- runs-on: ubuntu-latest
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_PR_ENVIRONMENT: pr-${{ github.event.pull_request.number }}
- steps:
- - uses: actions/checkout@v4
- - uses: actions/download-artifact@v4
- with:
- name: una-package
- path: packages
- - name: Prepare artefact
- run: |
- mkdir -p deploy
- unzip -q packages/*.zip -d /tmp/una-pkg
- pkg_dir=$(find /tmp/una-pkg -mindepth 1 -maxdepth 1 -type d | head -n 1)
- cp -a "$pkg_dir"/. deploy/
- cp scripts/railway/Dockerfile scripts/railway/entrypoint.sh scripts/railway/railway.toml scripts/railway/.railwayignore \
- scripts/railway/configure.php scripts/railway/seed.php deploy/
- chmod +x deploy/entrypoint.sh
- - uses: ./.github/actions/railway-cli
- - name: Deploy artefact
- run: railway up ./deploy --path-as-root --no-gitignore --verbose --ci --service una --environment "$RAILWAY_PR_ENVIRONMENT" --project "$RAILWAY_PROJECT_ID"
- deploy-neo:
- # NEO isn't covered by `test`, so it builds alongside it.
- name: Deploy PR to Railway (neo)
- needs: preview-env
- runs-on: ubuntu-latest
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_PR_ENVIRONMENT: pr-${{ github.event.pull_request.number }}
- steps:
- - uses: actions/checkout@v4
- - uses: ./.github/actions/railway-cli
- - name: Deploy NEO
- run: |
- scripts/railway/neo.sh stage /tmp/deploy-neo
- railway up /tmp/deploy-neo --path-as-root --no-gitignore --verbose --ci --service neo --environment "$RAILWAY_PR_ENVIRONMENT" --project "$RAILWAY_PROJECT_ID"
- preview-comment:
- name: Comment preview URLs
- needs: [preview-env, deploy, deploy-neo]
- if: always() && needs.preview-env.result == 'success'
- runs-on: ubuntu-latest
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_PR_ENVIRONMENT: pr-${{ github.event.pull_request.number }}
- UNA_RESULT: ${{ needs.deploy.result }}
- NEO_RESULT: ${{ needs.deploy-neo.result }}
- GH_TOKEN: ${{ github.token }}
- PR_NUMBER: ${{ github.event.pull_request.number }}
- steps:
- - uses: actions/checkout@v4
- with:
- sparse-checkout: .github/actions
- - uses: ./.github/actions/railway-cli
- - name: Comment preview URLs on PR
- run: |
- railway link --project "$RAILWAY_PROJECT_ID" --environment "$RAILWAY_PR_ENVIRONMENT" --json >/dev/null
- service_url() {
- local host
- host=$(railway domain list --service "$1" --environment "$RAILWAY_PR_ENVIRONMENT" --json | jq -r '.domains[] | select(.type == "service") | .domain' | head -n1)
- [ -n "$host" ] && echo "https://${host#https://}"
- }
- una_url=$(service_url una || true)
- neo_url=$(service_url neo || true)
- state() { [ "$1" = success ] && echo "" || echo " (deploy: $1)"; }
- marker="<!-- railway-preview -->"
- body=$(printf '%s\n**Preview**: %s (NEO web client%s)\n**API**: %s (UNA%s)\n\nRailway environment: `%s`. A fresh UNA install takes about 10 minutes.' \
- "$marker" "$neo_url" "$(state "$NEO_RESULT")" "$una_url" "$(state "$UNA_RESULT")" "$RAILWAY_PR_ENVIRONMENT")
- comment_id=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate \
- --jq ".[] | select(.user.login == \"github-actions[bot]\" and (.body | contains(\"${marker}\"))) | .id" \
- | head -n1)
- if [ -n "$comment_id" ]; then
- gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${comment_id}" -f body="$body"
- else
- gh pr comment "$PR_NUMBER" --body "$body"
- fi
- deploy-production:
- # Runs next to the Jenkins deploy to ci.una.io, which is unchanged.
- name: Deploy master to Railway (api.unacms.app)
- needs: test
- if: github.event_name == 'push' && github.ref == 'refs/heads/master'
- runs-on: ubuntu-latest
- concurrency:
- group: railway-production
- cancel-in-progress: false
- environment:
- name: production
- url: https://api.unacms.app
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_ENVIRONMENT: production
- VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- VERCEL_TEAM: yasko
- UNA_API_KEY: ${{ secrets.UNA_API_KEY }}
- steps:
- - uses: actions/checkout@v4
- - uses: actions/download-artifact@v4
- with:
- name: una-package
- path: packages
- - name: Prepare artefact
- run: |
- mkdir -p deploy
- unzip -q packages/*.zip -d /tmp/una-pkg
- pkg_dir=$(find /tmp/una-pkg -mindepth 1 -maxdepth 1 -type d | head -n 1)
- cp -a "$pkg_dir"/. deploy/
- cp scripts/railway/Dockerfile scripts/railway/entrypoint.sh scripts/railway/railway.toml scripts/railway/.railwayignore \
- scripts/railway/configure.php scripts/railway/seed.php deploy/
- chmod +x deploy/entrypoint.sh
- - uses: ./.github/actions/railway-cli
- - name: Configure production
- run: |
- if [ -z "$RAILWAY_API_TOKEN" ] || [ -z "$RAILWAY_PROJECT_ID" ]; then
- echo "Set secrets RAILWAY_API_TOKEN (account token) and RAILWAY_PROJECT_ID" >&2
- exit 1
- fi
- railway link --project "$RAILWAY_PROJECT_ID" --environment "$RAILWAY_ENVIRONMENT" --json
- # Database credentials and UNA_HASH_SECRET live in the Railway environment, not here.
- railway variable set --service una --environment "$RAILWAY_ENVIRONMENT" --skip-deploys \
- UNA_CLIENT_URL=https://unacms.app \
- ${UNA_API_KEY:+UNA_API_KEY="$UNA_API_KEY"}
- - name: Deploy artefact
- run: railway up ./deploy --path-as-root --no-gitignore --verbose --ci --service una --environment "$RAILWAY_ENVIRONMENT" --project "$RAILWAY_PROJECT_ID"
- - name: api.unacms.app DNS
- if: env.VERCEL_TOKEN != ''
- run: scripts/railway/domains.sh railway-domain api.unacms.app "$RAILWAY_ENVIRONMENT" una
- - name: Smoke test
- # A fresh production install takes about 4 minutes; allow 10.
- run: |
- for i in $(seq 1 60); do
- code=$(curl -s -o /dev/null -w '%{http_code}' https://api.unacms.app/ || true)
- [ "$code" = "200" ] && { echo "api.unacms.app is up"; exit 0; }
- # Until the certificate is issued, check the Railway hostname instead.
- host=$(railway domain list --service una --environment "$RAILWAY_ENVIRONMENT" --json | jq -r '.domains[] | select(.type == "service") | .domain' | head -n1)
- if [ -n "$host" ] && [ "$(curl -s -o /dev/null -w '%{http_code}' "https://${host#https://}/" || true)" = "200" ]; then
- echo "up at https://$host (api.unacms.app answered $code; certificate may still be pending)"
- exit 0
- fi
- sleep 10
- done
- echo "UNA did not come up" >&2
- exit 1
- deploy-production-neo:
- # The NEO web client for unacms.app. NEO_PRODUCTION_HOST (a repository variable) is the
- # public host; until it is set, the client is only on its Railway domain.
- name: Deploy master to Railway (neo)
- if: github.event_name == 'push' && github.ref == 'refs/heads/master'
- runs-on: ubuntu-latest
- concurrency:
- group: railway-production-neo
- cancel-in-progress: false
- environment:
- name: production-neo
- url: ${{ steps.urls.outputs.app_url }}
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_ENVIRONMENT: production
- VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- VERCEL_TEAM: yasko
- NEO_PRODUCTION_HOST: ${{ vars.NEO_PRODUCTION_HOST }}
- steps:
- - uses: actions/checkout@v4
- - uses: ./.github/actions/railway-cli
- - name: Configure neo
- id: urls
- run: |
- railway link --project "$RAILWAY_PROJECT_ID" --environment "$RAILWAY_ENVIRONMENT" --json
- railway domain --service neo --environment "$RAILWAY_ENVIRONMENT" || true
- neo_host=$(railway domain list --service neo --environment "$RAILWAY_ENVIRONMENT" --json | jq -r '.domains[] | select(.type == "service") | .domain' | head -n1)
- neo_host=${neo_host#https://}
- [ -n "$neo_host" ] || { echo "No Railway domain for neo" >&2; exit 1; }
- app_url="https://${NEO_PRODUCTION_HOST:-$neo_host}"
- scripts/railway/neo.sh vars "$RAILWAY_ENVIRONMENT" https://api.unacms.app "$app_url"
- echo "app_url=$app_url" >> "$GITHUB_OUTPUT"
- echo "neo_url=https://$neo_host" >> "$GITHUB_OUTPUT"
- - name: Deploy NEO
- run: |
- scripts/railway/neo.sh stage /tmp/deploy-neo
- railway up /tmp/deploy-neo --path-as-root --no-gitignore --verbose --ci --service neo --environment "$RAILWAY_ENVIRONMENT" --project "$RAILWAY_PROJECT_ID"
- - name: Production host DNS
- if: env.NEO_PRODUCTION_HOST != '' && env.VERCEL_TOKEN != ''
- run: scripts/railway/domains.sh railway-domain "$NEO_PRODUCTION_HOST" "$RAILWAY_ENVIRONMENT" neo
- - name: Smoke test
- env:
- NEO_URL: ${{ steps.urls.outputs.neo_url }}
- run: |
- for i in $(seq 1 30); do
- code=$(curl -s -o /dev/null -w '%{http_code}' "$NEO_URL/" || true)
- [ "$code" = "200" ] && { echo "NEO is up at $NEO_URL"; exit 0; }
- sleep 10
- done
- echo "NEO did not answer 200 at $NEO_URL (last: $code)" >&2
- exit 1
- teardown:
- name: Remove PR Railway environment
- if: >-
- github.event_name == 'pull_request' && github.event.action == 'closed'
- && github.event.pull_request.head.repo.full_name == github.repository
- && github.event.pull_request.user.login != 'dependabot[bot]'
- runs-on: ubuntu-latest
- env:
- RAILWAY_API_TOKEN: ${{ secrets.RAILWAY_API_TOKEN }}
- RAILWAY_PROJECT_ID: ${{ secrets.RAILWAY_PROJECT_ID }}
- RAILWAY_BASE_ENVIRONMENT: ${{ secrets.RAILWAY_ENVIRONMENT }}
- RAILWAY_PR_ENVIRONMENT: pr-${{ github.event.pull_request.number }}
- VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
- VERCEL_TEAM: yasko
- PREVIEW_API_HOST: api-pr-${{ github.event.pull_request.number }}.unacms.app
- PREVIEW_CLIENT_HOST: pr-${{ github.event.pull_request.number }}.unacms.app
- steps:
- - uses: actions/checkout@v4
- with:
- sparse-checkout: |
- .github/actions
- scripts/railway
- # Previews created before they moved to Railway domains (2026-10-20) still have DNS records
- # on unacms.app; this step can go once those PRs are closed.
- - name: Remove preview domains
- if: env.VERCEL_TOKEN != ''
- continue-on-error: true
- run: |
- scripts/railway/domains.sh client-unalias "$PREVIEW_CLIENT_HOST"
- scripts/railway/domains.sh dns-remove "$PREVIEW_CLIENT_HOST"
- scripts/railway/domains.sh dns-remove "$PREVIEW_API_HOST"
- - uses: ./.github/actions/railway-cli
- - name: Delete PR environment
- run: |
- if [ -z "$RAILWAY_API_TOKEN" ] || [ -z "$RAILWAY_PROJECT_ID" ]; then
- echo "Set secrets RAILWAY_API_TOKEN (account token) and RAILWAY_PROJECT_ID" >&2
- exit 1
- fi
- base="${RAILWAY_BASE_ENVIRONMENT:-production}"
- railway link --project "$RAILWAY_PROJECT_ID" --environment "$base" --json
- # The run for the PR's last push is cancelled above (workflow concurrency), so nothing
- # can create the environment after this. A PR closed before its first deploy has none.
- if railway environment delete "$RAILWAY_PR_ENVIRONMENT" -y; then
- echo "Deleted $RAILWAY_PR_ENVIRONMENT"
- else
- echo "No environment $RAILWAY_PR_ENVIRONMENT to delete"
- fi
|