Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
0d16484
Add new images for Signadot CI/CD connection documentation
GeoSegun Nov 26, 2025
aae9db3
Add initial setup for Vercel and Signadot integration, including back…
GeoSegun Nov 26, 2025
d78e07d
Add initial setup for Vercel and Signadot integration
GeoSegun Nov 26, 2025
c0aa703
Revert root README.md to match main branch
GeoSegun Nov 26, 2025
622f80d
Add CI/CD workflows for backend Docker image and full-stack preview d…
GeoSegun Nov 27, 2025
42b12a0
Add backend configuration and Docker setup for Signadot integration
GeoSegun Nov 27, 2025
e21d629
Refactor frontend configuration and update CI/CD workflow
GeoSegun Nov 27, 2025
b1b7f54
Update Signadot sandbox and Kubernetes configurations
GeoSegun Nov 27, 2025
87f13e4
Update README.md to include repository link for configuration files a…
GeoSegun Nov 27, 2025
b2cec90
Update README.md to include instructions for updating the image refer…
GeoSegun Nov 27, 2025
ed5f215
Revise README.md for clarity and completeness in Vercel and Signadot …
GeoSegun Nov 27, 2025
7e0a356
Enhance README.md for Vercel and Signadot integration tutorial
GeoSegun Nov 27, 2025
9c18111
Update README.md to include visual reference for GitHub Action PR com…
GeoSegun Nov 27, 2025
28edd0e
Enhance README.md for Vercel and Signadot integration tutorial
GeoSegun Nov 27, 2025
92fa52c
docs: reframe tutorial around hot-reload DX and update architecture d…
GeoSegun Dec 3, 2025
fa9bcca
Update architecture overview image for Vercel and Signadot integratio…
GeoSegun Dec 3, 2025
4609a8a
docs: align sandbox example with fork-based spec
GeoSegun Dec 9, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# CI/CD Pipeline for building and pushing Docker images
# Builds image on PR and provides image ref for Signadot sandboxes
name: Build and Push Backend Image

on:
pull_request:
branches:
- main
- master
push:
branches:
- main
- master

env:
# Docker image configuration
IMAGE_NAME: vercel-signadot-backend
# Docker registry (e.g., docker.io, ghcr.io, gcr.io)
# Must be set as REGISTRY secret in GitHub
REGISTRY: ${{ secrets.REGISTRY }}
IMAGE_TAG: ${{ github.sha }}

jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}

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

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

- name: Log in to container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Sanitize branch name for Docker tags
id: branch
run: |
# Sanitize branch name for Docker tag compatibility
# Docker tags cannot start with hyphens, periods, or contain invalid characters
BRANCH_NAME="${{ github.head_ref || github.ref_name }}"

# Remove leading/trailing hyphens and periods, replace invalid chars
SANITIZED_BRANCH=$(echo "${BRANCH_NAME}" | sed 's/[^a-zA-Z0-9._-]/-/g' | sed 's/^[-.]*//' | sed 's/[-.]*$//' | tr '[:upper:]' '[:lower:]')

# Ensure it doesn't start with hyphen or period
if [[ "${SANITIZED_BRANCH}" =~ ^[-.] ]]; then
SANITIZED_BRANCH="branch-${SANITIZED_BRANCH}"
fi

# Fallback if empty or too short
if [ -z "${SANITIZED_BRANCH}" ] || [ "${#SANITIZED_BRANCH}" -lt 1 ]; then
SANITIZED_BRANCH="pr"
fi

# Limit length to avoid exceeding Docker tag limits
SANITIZED_BRANCH="${SANITIZED_BRANCH:0:50}"

echo "BRANCH=${SANITIZED_BRANCH}" >> $GITHUB_OUTPUT
echo "::notice::Original branch: ${BRANCH_NAME}"
echo "::notice::Sanitized branch: ${SANITIZED_BRANCH}"

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}
tags: |
type=sha,prefix=${{ steps.branch.outputs.BRANCH }}-
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Output image reference
run: |
IMAGE_REF="${{ env.REGISTRY }}/${{ env.DOCKERHUB_USERNAME }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}"
echo "IMAGE_REF=${IMAGE_REF}" >> $GITHUB_OUTPUT
echo "::notice::Docker image built: ${IMAGE_REF}"
echo "::notice::Use this image reference in your Signadot sandbox action"

Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Full-Stack Preview Deployment with Vercel + Signadot
# Creates Signadot sandbox for backend and deploys frontend to Vercel with sandbox URL
# Comments both preview URLs on the PR
name: Deploy Full-Stack Preview

on:
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch: {}

jobs:
deploy-preview:
name: Deploy Full-Stack Preview Environment
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
id-token: write
env:
DOCKER_REGISTRY: docker.io
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
IMAGE_NAME: vercel-signadot-backend

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

- name: Checkout Backend Code
uses: actions/checkout@v4
with:
repository: ${{ secrets.BACKEND_REPO }}
path: backend
token: ${{ secrets.GH_PAT }}
ref: main

- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}

- name: Configure kubectl for EKS
run: |
aws eks update-kubeconfig \
--name ${{ secrets.AWS_EKS_CLUSTER_NAME }} \
--region ${{ secrets.AWS_REGION }}
kubectl get nodes

- name: Install Signadot CLI
run: |
curl -sSLf https://raw.githubusercontent.com/signadot/cli/main/scripts/install.sh | sh
echo "$HOME/.signadot/bin" >> "$GITHUB_PATH"

- name: Configure Signadot credentials
env:
SIGNADOT_ORG: ${{ secrets.SIGNADOT_ORG }}
SIGNADOT_API_KEY: ${{ secrets.SIGNADOT_API_KEY }}
run: |
mkdir -p "$HOME/.signadot"
{
echo "org: ${SIGNADOT_ORG}"
echo "api_key: ${SIGNADOT_API_KEY}"
} > "$HOME/.signadot/config.yaml"

- name: Verify Signadot Operator
run: |
# Signadot Operator should be pre-installed on the cluster
# This step verifies the operator is running
if ! kubectl get namespace signadot &>/dev/null; then
echo "::error::Signadot namespace not found. Please install the Signadot Operator first."
echo "::notice::Installation guide: https://www.signadot.com/docs/getting-started/installation/install-signadot-operator"
exit 1
fi

echo "::notice::Checking Signadot Operator status..."
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=signadot-operator -n signadot --timeout=30s || {
echo "::warning::Signadot Operator may not be ready, but continuing..."
kubectl get pods -n signadot || true
}

- name: Get Backend Image Reference
id: backend-image
run: |
DOCKERHUB_USERNAME="${{ env.DOCKERHUB_USERNAME }}"
IMAGE_NAME="${{ env.IMAGE_NAME }}"
IMAGE_TAG="latest"
IMAGE_REF="${{ env.DOCKER_REGISTRY }}/${DOCKERHUB_USERNAME}/${IMAGE_NAME}:${IMAGE_TAG}"
echo "IMAGE_REF=${IMAGE_REF}" >> $GITHUB_OUTPUT
echo "SANDBOX_IMAGE_TAG=${IMAGE_TAG}" >> $GITHUB_OUTPUT

- name: Prepare Sandbox Configuration
run: |
IMAGE_REF="${{ steps.backend-image.outputs.IMAGE_REF }}"
SANDBOX_IMAGE_TAG="${{ steps.backend-image.outputs.SANDBOX_IMAGE_TAG }}"
DOCKERHUB_USERNAME="${{ env.DOCKERHUB_USERNAME }}"
CLUSTER_NAME="${{ secrets.AWS_EKS_CLUSTER_NAME }}"
NAMESPACE="default"

# Replace image placeholder
sed -i "s|image: docker.io/DOCKERHUB_USERNAME/vercel-signadot-backend:SANDBOX_IMAGE_TAG|image: ${IMAGE_REF}|g" backend/sandbox.yaml

# Replace cluster name placeholder
sed -i "s|cluster: CLUSTER_NAME|cluster: ${CLUSTER_NAME}|g" backend/sandbox.yaml

# Replace namespace placeholders
sed -i "s|namespace: NAMESPACE|namespace: ${NAMESPACE}|g" backend/sandbox.yaml
sed -i "s|NAMESPACE|${NAMESPACE}|g" backend/sandbox.yaml

# Update sandbox name with PR number
if [ -n "${{ github.event.pull_request.number }}" ]; then
PR_NUM="${{ github.event.pull_request.number }}"
SANDBOX_NAME="backend-pr-${PR_NUM}"
# Replace first name: field (sandbox name, not deployment name)
sed -i "0,/^name:/s/^name:.*/name: ${SANDBOX_NAME}/" backend/sandbox.yaml
else
# Replace PR_NUMBER placeholder for non-PR events
sed -i "s|name: backend-pr-PR_NUMBER|name: backend-main-${GITHUB_SHA:0:7}|g" backend/sandbox.yaml
fi

- name: Create Signadot Sandbox
id: sandbox
run: |
set -e

if [ -n "${{ github.event.pull_request.number }}" ]; then
PR_NUM="${{ github.event.pull_request.number }}"
SANDBOX_NAME="backend-pr-${PR_NUM}"
else
SANDBOX_NAME="backend-main-${GITHUB_SHA:0:7}"
fi

SANDBOX_OUTPUT=$(signadot sandbox apply -f backend/sandbox.yaml)
echo "$SANDBOX_OUTPUT"

# Extract sandbox URL using JSON output
sleep 5
SANDBOX_JSON=$(signadot sandbox get "${SANDBOX_NAME}" -o json 2>/dev/null || echo "")
if [ -n "$SANDBOX_JSON" ]; then
if command -v jq &> /dev/null; then
SANDBOX_URL=$(echo "$SANDBOX_JSON" | jq -r '.endpoints[]? | select(.name=="backend-api") | .url' | head -n1)
fi
fi

# Fallback: extract from text output
if [ -z "$SANDBOX_URL" ]; then
SANDBOX_URL=$(echo "$SANDBOX_OUTPUT" | grep -oE 'https://[^ \t]*\.preview\.signadot\.com' | head -n1)
fi

if [ -z "$SANDBOX_URL" ]; then
echo "::error::Failed to extract sandbox URL"
exit 1
fi

echo "sandbox-url=$SANDBOX_URL" >> "$GITHUB_OUTPUT"
echo "sandbox-name=${SANDBOX_NAME}" >> "$GITHUB_OUTPUT"

- name: Deploy to Vercel with Sandbox URL
id: vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: .
vercel-args: '--build-env NEXT_PUBLIC_API_URL=${{ steps.sandbox.outputs.sandbox-url }} --env SIGNADOT_API_KEY=${{ secrets.SIGNADOT_API_KEY }} --force'
env:
NEXT_PUBLIC_API_URL: ${{ steps.sandbox.outputs.sandbox-url }}
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

- name: Comment Preview URLs on PR
if: github.event.pull_request.number != null
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `## 🚀 Full-Stack Preview Deployed!

Your preview environment is ready for testing:

- **Frontend Preview:** ${{ steps.vercel.outputs.preview-url }}
- **Backend Sandbox:** ${{ steps.sandbox.outputs.sandbox-url }}

The frontend is automatically configured to use the backend sandbox URL.`
});

38 changes: 38 additions & 0 deletions vercel-preview-signadot-sandoxes-cicd-connection/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Dependencies
node_modules/
bun.lock
package-lock.json
yarn.lock

# Build outputs
.next/
out/
build/
dist/

# Environment files
.env
.env.local
.env*.local

# IDE
.vscode/
.idea/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*

# Vercel
.vercel

# TypeScript
*.tsbuildinfo
next-env.d.ts

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: backend-pr-PR_NUMBER
spec:
cluster: CLUSTER_NAME
description: Sandbox environment for vercel-signadot-backend
forks:
- forkOf:
kind: Deployment
namespace: default
name: vercel-signadot-backend
customizations:
images:
- image: docker.io/DOCKERHUB_USERNAME/vercel-signadot-backend:SANDBOX_IMAGE_TAG
command: ["node", "server.js"]
env:
- name: PORT
value: "3000"
defaultRouteGroup:
endpoints:
- name: backend-api
target: http://vercel-signadot-backend.default.svc:3000

Loading