Skip to content

Point the default API base URL at api.purduehackers.com #482

Point the default API base URL at api.purduehackers.com

Point the default API base URL at api.purduehackers.com #482

Workflow file for this run

name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
pull-requests: write
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
validate:
runs-on: ubuntu-latest
env:
SKIP_ENV_VALIDATION: "1"
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: ./.github/actions/setup
- run: bunx oxfmt --check .
# `--deny-warnings` is CLI-only — oxlint's config has no equivalent — and it is the
# difference between the ~50 warn-level oxclippy rules gating CI and gating nothing.
# It has been dropped from the script twice; fail loudly rather than quietly weaken.
- name: Lint gate is intact
run: |
lint=$(bun -e 'console.log((await Bun.file("package.json").json()).scripts.lint)')
case "$lint" in
*--deny-warnings*) ;;
*) echo "::error::lint script lost --deny-warnings; warn-level rules would stop gating CI"; exit 1 ;;
esac
# oxlint is type-aware (tsgolint), so it reports TS errors too — there is no separate tsc pass.
- run: bun run lint
# Cross-file invariants a code review cannot see: a skill referencing a tool the registry
# does not define, a tool no role can reach, a half-wired subagent. Each package script does
# one thing, so CI composes them here rather than one script invoking another.
- run: bun run check:capabilities
working-directory: packages/agents
- run: bun run check:serialization
working-directory: packages/agents
- name: Validate database migrations
run: |
database="$(mktemp /tmp/wack-hacker-ci-XXXXXX.db)"
trap 'rm -f "$database"' EXIT
cd packages/shared
TURSO_DATABASE_URL="file:$database" bunx drizzle-kit check
TURSO_DATABASE_URL="file:$database" bun run db:migrate
- run: bun run build
- run: docker build --platform linux/amd64 -f packages/bot/Dockerfile -t wack-hacker-bot:ci .
# A schema change is the one thing CI cannot undo after the fact, so it is
# reviewed before it merges rather than discovered when `main` applies it.
migrations:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
# The reviewer diffs against the base branch, so a shallow clone has
# nothing to compare with.
fetch-depth: 0
- uses: ./.github/actions/setup
- name: Review schema changes
id: review
run: |
set -euo pipefail
bun packages/shared/scripts/review-migrations.ts \
"origin/${{ github.base_ref }}" | tee review.md
- name: Post the review to the pull request
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
marker="<!-- migration-review -->"
body="$marker"$'\n'"$(cat review.md)"
existing="$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
--jq "[.[] | select(.body | startswith(\"$marker\"))][0].id // empty")"
if [ -n "$existing" ]; then
gh api --method PATCH "repos/${GITHUB_REPOSITORY}/issues/comments/${existing}" \
-f body="$body" >/dev/null
else
gh api --method POST "repos/${GITHUB_REPOSITORY}/issues/${PR}/comments" \
-f body="$body" >/dev/null
fi
# Destructive statements are not forbidden, but they stop being something
# that can happen without anyone noticing: production migrates on merge.
- name: Block on data loss
if: steps.review.outputs.blocking == 'true'
run: |
echo "::error::this branch drops or rebuilds existing data — see the migration review comment" >&2
exit 1
# Slash commands are guild-scoped and registered by an explicit PUT that replaces the whole
# set, so this is idempotent and safe to repeat. It runs only after a merge to main, and only
# once `validate` has passed — never on a pull request, where a fork could otherwise reach a
# real Discord application.
register-commands:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: ./.github/actions/setup
- run: bun run register-commands
working-directory: packages/bot
env:
DISCORD_BOT_TOKEN: ${{ secrets.DISCORD_BOT_TOKEN }}
DISCORD_BOT_CLIENT_ID: ${{ secrets.DISCORD_BOT_CLIENT_ID }}
# The script refuses to run unless this matches the guild id compiled into
# @repo/shared/discord, so a misconfigured secret cannot register elsewhere.
CONFIRM_COMMAND_GUILD: "772576325897945119"