Skip to content
Open
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
185 changes: 185 additions & 0 deletions .github/workflows/supreme-ai-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
name: Reusable Supreme AI Deploy

on:
workflow_call:
inputs:
node-version:
description: Node.js version used during validation
required: false
default: '20'
type: string
app-port:
description: Container application port
required: false
default: 3000
type: number
host-port:
description: Host port exposed on the deploy server
required: false
default: 80
type: number
bind-ip:
description: Bind address on deploy host (127.0.0.1 recommended)
required: false
default: '127.0.0.1'
type: string
healthcheck-url:
description: Health endpoint URL checked from deploy host
required: false
default: 'http://localhost/health'
type: string
secrets:
DEPLOY_HOST:
required: true
DEPLOY_USER:
required: true
DEPLOY_SSH_KEY:
required: true
GHCR_USERNAME:
required: true
GHCR_TOKEN:
required: true

workflow_dispatch:

permissions:
contents: read

concurrency:
group: supreme-ai-deploy-${{ github.ref }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
CONTAINER_NAME: supreme-ai-hub

jobs:
validate:
name: Validate, test, and build
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version || '20' }}
cache: npm

- name: Install dependencies
run: npm ci
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard Node validation step for non-app repositories

This workflow is added to the .github repository, but it unconditionally runs npm ci on every push/PR; in this repo there is no package.json/lockfile, so the validate job fails immediately and the pipeline can never reach image build or deploy. If this file is intended as a template for another repo, it should be limited to manual/reusable execution or gated so it does not run in repos without a Node project.

Useful? React with 👍 / 👎.


- name: Lint
run: npm run lint --if-present

- name: Unit tests
run: npm test --if-present -- --ci

- name: Build
run: npm run build --if-present

containerize:
name: Build and push image
runs-on: ubuntu-latest
needs: validate
timeout-minutes: 30
permissions:
contents: read
packages: write
outputs:
image_tag: ${{ steps.vars.outputs.image_tag }}
image_repo: ${{ steps.vars.outputs.image_repo }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set image variables
id: vars
run: |
IMAGE_REPO=$(echo "${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')
echo "image_repo=${IMAGE_REPO}" >> "$GITHUB_OUTPUT"
echo "image_tag=sha-${GITHUB_SHA}" >> "$GITHUB_OUTPUT"

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ steps.vars.outputs.image_repo }}
tags: |
type=raw,value=${{ steps.vars.outputs.image_tag }}
type=raw,value=latest

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

deploy:
name: Deploy to server
runs-on: ubuntu-latest
needs: containerize
timeout-minutes: 20
environment:
name: production

steps:
- name: Deploy and verify
uses: appleboy/ssh-action@v1.0.3
env:
IMAGE_REPO: ${{ needs.containerize.outputs.image_repo }}
IMAGE_TAG: ${{ needs.containerize.outputs.image_tag }}
GHCR_USER: ${{ secrets.GHCR_USERNAME }}
GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
HEALTHCHECK_URL: ${{ inputs.healthcheck-url || 'http://localhost/health' }}
DEPLOY_BIND_IP: ${{ inputs.bind-ip || '127.0.0.1' }}
APP_PORT: ${{ inputs.app-port || 3000 }}
HOST_PORT: ${{ inputs.host-port || 80 }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_SSH_KEY }}
script_stop: true
envs: IMAGE_REPO,IMAGE_TAG,GHCR_USER,GHCR_TOKEN,HEALTHCHECK_URL,DEPLOY_BIND_IP,APP_PORT,HOST_PORT
script: |
set -euxo pipefail
IMAGE="${{ env.REGISTRY }}/${IMAGE_REPO}:${IMAGE_TAG}"

printf '%s' "$GHCR_TOKEN" | docker login ${{ env.REGISTRY }} -u "$GHCR_USER" --password-stdin
docker pull "$IMAGE"

docker stop "${{ env.CONTAINER_NAME }}" || true
docker rm "${{ env.CONTAINER_NAME }}" || true

docker run -d --name "${{ env.CONTAINER_NAME }}" --restart unless-stopped \
-p ${DEPLOY_BIND_IP}:${HOST_PORT}:${APP_PORT} \
"$IMAGE"

for attempt in {1..12}; do
if curl -fsS "$HEALTHCHECK_URL" > /dev/null; then
echo "Health check passed on attempt ${attempt}"
docker image prune -f || true
exit 0
fi
sleep 5
done

echo "Health check failed for ${HEALTHCHECK_URL}" >&2
docker logs --tail 100 "${{ env.CONTAINER_NAME }}" || true
exit 1
25 changes: 25 additions & 0 deletions .github/workflows/vercel-app-deploy-example.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Vercel Deploy (Example Caller)

on:
workflow_dispatch:

jobs:
deploy:
uses: wesship/.github/.github/workflows/vercel-deploy.yml@main
with:
environment: production
production: true
working-directory: .
node-version: '20'
vercel-version: 'latest'
secrets:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

announce:
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Print deployed URL
run: echo "Deployed URL -> ${{ needs.deploy.outputs.deployment_url }}"
111 changes: 111 additions & 0 deletions .github/workflows/vercel-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Reusable Vercel Deploy

on:
workflow_call:
inputs:
environment:
description: Vercel target environment
required: false
default: production
type: string
working-directory:
description: Directory of app source in caller repository
required: false
default: .
type: string
production:
description: Deploy to production (`--prod`)
required: false
default: true
type: boolean
node-version:
description: Node.js version used for CLI execution
required: false
default: '20'
type: string
vercel-version:
description: Vercel CLI version
required: false
default: 'latest'
type: string
secrets:
VERCEL_TOKEN:
required: true
VERCEL_ORG_ID:
required: true
VERCEL_PROJECT_ID:
required: true
outputs:
deployment_url:
description: URL returned by Vercel deploy
value: ${{ jobs.deploy.outputs.deployment_url }}

permissions:
contents: read

jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 20
outputs:
deployment_url: ${{ steps.deploy.outputs.deployment_url }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

- name: Install Vercel CLI
run: npm install --global "vercel@${{ inputs.vercel-version }}"

- name: Validate working directory
working-directory: ${{ inputs.working-directory }}
run: |
test -f package.json || {
echo "package.json not found in $PWD" >&2
exit 1
}

- name: Pull Vercel environment
working-directory: ${{ inputs.working-directory }}
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
run: |
vercel pull --yes --environment=${{ inputs.environment }} --token="$VERCEL_TOKEN"

- name: Build with Vercel
working-directory: ${{ inputs.working-directory }}
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
vercel build --token="$VERCEL_TOKEN"

- name: Deploy prebuilt output
id: deploy
working-directory: ${{ inputs.working-directory }}
env:
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
run: |
set -euo pipefail
DEPLOY_ARGS="--prebuilt --yes --token=$VERCEL_TOKEN"
if [ "${{ inputs.production }}" = "true" ]; then
DEPLOY_ARGS="$DEPLOY_ARGS --prod"
fi

DEPLOY_OUTPUT=$(vercel deploy $DEPLOY_ARGS 2>&1)
echo "$DEPLOY_OUTPUT"

DEPLOYMENT_URL=$(printf '%s\n' "$DEPLOY_OUTPUT" | grep -Eo "https://[A-Za-z0-9.-]+\.vercel\.app" | tail -n 1)
if [ -z "$DEPLOYMENT_URL" ]; then
echo "Could not detect deployment URL from Vercel output" >&2
exit 1
fi

echo "deployment_url=$DEPLOYMENT_URL" >> "$GITHUB_OUTPUT"
echo "### Vercel Deployment\n- URL: $DEPLOYMENT_URL" >> "$GITHUB_STEP_SUMMARY"
41 changes: 41 additions & 0 deletions profile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,44 @@ n8n is a low-code automation tool. With over 220 pre-built integrations and a ge
- 🌱 We were Sequoia's first seed investment in Germany, and recently raised a $12m Series A round, led by Felicis Ventures

We're on a mission to give technical superpowers to everyone with a computer. <a href="https://n8n.io/careers">Join us!</a>

## Supreme AI Deployment Hub: debug + deploy workflow

If your `wesship/supreme-ai-deployment-hub` app is failing in production, this repo now provides reusable workflows that your app repo can call.

Available workflows:
- `.github/workflows/supreme-ai-deploy.yml`: reusable CI/CD workflow for validate -> containerize -> SSH deploy.
- `.github/workflows/vercel-deploy.yml`: reusable Vercel workflow for `vercel pull`, `vercel build`, and `vercel deploy --prebuilt`.

For Supreme AI server deploy callers, provide these required secrets from the app repo:
- `DEPLOY_HOST`
- `DEPLOY_USER`
- `DEPLOY_SSH_KEY`
- `GHCR_USERNAME`
- `GHCR_TOKEN` (PAT with `read:packages`)

Recommended inputs when calling `supreme-ai-deploy.yml`:
- `healthcheck-url` (default: `http://localhost/health`)
- `bind-ip` (default: `127.0.0.1`; set `0.0.0.0` only when you intentionally expose publicly)
- `app-port` / `host-port` (defaults: `3000` / `80`)

Deployability checklist:
- Ensure your app repo includes a valid `package.json`, lockfile, and `Dockerfile`.
- Ensure target VM has Docker + curl installed and SSH key access configured.
- Ensure GHCR package visibility and token permissions allow pull from deploy host.

Security hardening included:
- Deployment binds service to `127.0.0.1` by default to reduce accidental public exposure.
- GHCR login on the server uses `--password-stdin` to avoid leaking tokens in process args.

Vercel deployment (for `https://vercel.com/wesships-projects`):
- Required app-repo secrets: `VERCEL_TOKEN`, `VERCEL_ORG_ID`, `VERCEL_PROJECT_ID`.
- Supports Lovable-generated apps too (example project: `https://lovable.dev/projects/b5eb8a4d-3709-4e3f-930c-ab5ab4b96560`) by deploying from a configurable `working-directory`.
- The reusable workflow exports `deployment_url` so callers can post the deployed URL to PR comments/check summaries.

Go-live quick start:
- 1) Add required secrets in your app repo.
- 2) Copy `.github/workflows/vercel-app-deploy-example.yml` into your app repo.
- 3) Add a second caller workflow in the app repo for `wesship/.github/.github/workflows/supreme-ai-deploy.yml@main`.
- 4) Run one manual `workflow_dispatch` in each workflow and confirm health checks / deployment URL output.