Moved actions to remote repo
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Start or replace a Next.js Docker container
|
||||
set -euo pipefail
|
||||
|
||||
APP_NAME=""
|
||||
TAG=""
|
||||
PORT=""
|
||||
ENV=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--name) APP_NAME="$2"; shift 2 ;;
|
||||
--tag) TAG="$2"; shift 2 ;;
|
||||
--port) PORT="$2"; shift 2 ;;
|
||||
--env) ENV="$2"; shift 2 ;;
|
||||
*) echo "Unknown arg: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
CONTAINER="${APP_NAME}-${TAG}"
|
||||
|
||||
echo "→ Deploying container: ${CONTAINER} on port ${PORT}"
|
||||
|
||||
docker stop "${CONTAINER}" 2>/dev/null && docker rm "${CONTAINER}" 2>/dev/null || true
|
||||
|
||||
ENV_FILE_ARG=""
|
||||
if [[ -f "/opt/apps/${APP_NAME}/.env.${ENV}" ]]; then
|
||||
ENV_FILE_ARG="--env-file /opt/apps/${APP_NAME}/.env.${ENV}"
|
||||
fi
|
||||
|
||||
docker run -d \
|
||||
--name "${CONTAINER}" \
|
||||
--restart unless-stopped \
|
||||
-p "0.0.0.0:${PORT}:3000" \
|
||||
-e NODE_ENV=production \
|
||||
-e PORT=3000 \
|
||||
${ENV_FILE_ARG} \
|
||||
"${APP_NAME}:${TAG}"
|
||||
|
||||
echo "✓ Container ${CONTAINER} running on 0.0.0.0:${PORT}"
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Remove a Pangolin resource by name
|
||||
#
|
||||
# Required env vars:
|
||||
# PANGOLIN_API_URL, PANGOLIN_API_KEY, PANGOLIN_ORG_ID
|
||||
set -euo pipefail
|
||||
|
||||
RESOURCE_NAME=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--resource-name) RESOURCE_NAME="$2"; shift 2 ;;
|
||||
*) echo "Unknown arg: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
API="${PANGOLIN_API_URL}/v1"
|
||||
AUTH="Authorization: Bearer ${PANGOLIN_API_KEY}"
|
||||
|
||||
echo "→ Pangolin delete: ${RESOURCE_NAME}"
|
||||
|
||||
RESOURCE_ID=$(curl -sf \
|
||||
-H "${AUTH}" \
|
||||
"${API}/org/${PANGOLIN_ORG_ID}/resources?limit=1000" \
|
||||
| jq -r --arg name "${RESOURCE_NAME}" \
|
||||
'.data.resources[] | select(.name == $name) | .resourceId' \
|
||||
|| echo "")
|
||||
|
||||
if [[ -z "${RESOURCE_ID}" ]]; then
|
||||
echo " No resource found with name '${RESOURCE_NAME}', skipping."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo " Found resource ${RESOURCE_ID}, deleting…"
|
||||
|
||||
curl -sf -X DELETE \
|
||||
-H "${AUTH}" \
|
||||
"${API}/resource/${RESOURCE_ID}" \
|
||||
> /dev/null
|
||||
|
||||
echo "✓ Resource ${RESOURCE_ID} (${RESOURCE_NAME}) removed from Pangolin"
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Idempotently register an HTTP resource + target in Pangolin
|
||||
#
|
||||
# Required env vars:
|
||||
# PANGOLIN_API_URL, PANGOLIN_API_KEY, PANGOLIN_ORG_ID,
|
||||
# PANGOLIN_DOMAIN_ID, PANGOLIN_SITE_ID, PANGOLIN_TARGET_IP
|
||||
set -euo pipefail
|
||||
|
||||
SUBDOMAIN=""
|
||||
PORT=""
|
||||
RESOURCE_NAME=""
|
||||
TARGET_IP=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--subdomain) SUBDOMAIN="$2"; shift 2 ;;
|
||||
--port) PORT="$2"; shift 2 ;;
|
||||
--resource-name) RESOURCE_NAME="$2"; shift 2 ;;
|
||||
--target-ip) TARGET_IP="$2"; shift 2 ;;
|
||||
*) echo "Unknown arg: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
API="${PANGOLIN_API_URL}/v1"
|
||||
AUTH="Authorization: Bearer ${PANGOLIN_API_KEY}"
|
||||
|
||||
echo "→ Pangolin upsert: ${RESOURCE_NAME} → ${SUBDOMAIN} on port ${PORT}"
|
||||
|
||||
# Check if resource already exists
|
||||
EXISTING=$(curl -sf \
|
||||
-H "${AUTH}" \
|
||||
"${API}/org/${PANGOLIN_ORG_ID}/resources?limit=1000" \
|
||||
| jq -r --arg name "${RESOURCE_NAME}" \
|
||||
'.data.resources[] | select(.name == $name) | .resourceId' \
|
||||
|| echo "")
|
||||
|
||||
if [[ -n "${EXISTING}" ]]; then
|
||||
echo " Resource already exists (id=${EXISTING}), updating…"
|
||||
RESOURCE_ID="${EXISTING}"
|
||||
|
||||
curl -sf -X POST \
|
||||
-H "${AUTH}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"subdomain\": \"${SUBDOMAIN}\"}" \
|
||||
"${API}/resource/${RESOURCE_ID}" \
|
||||
> /dev/null
|
||||
|
||||
# Remove existing targets so we can re-register with potentially new port
|
||||
TARGETS=$(curl -sf \
|
||||
-H "${AUTH}" \
|
||||
"${API}/resource/${RESOURCE_ID}/targets" \
|
||||
| jq -r '.data.targets[].targetId' || echo "")
|
||||
|
||||
for TID in ${TARGETS}; do
|
||||
curl -sf -X DELETE \
|
||||
-H "${AUTH}" \
|
||||
"${API}/target/${TID}" > /dev/null
|
||||
echo " Removed old target ${TID}"
|
||||
done
|
||||
else
|
||||
echo " Creating new resource…"
|
||||
CREATE_RESP=$(curl -sf -X PUT \
|
||||
-H "${AUTH}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"${RESOURCE_NAME}\",
|
||||
\"http\": true,
|
||||
\"subdomain\": \"${SUBDOMAIN}\",
|
||||
\"domainId\": \"${PANGOLIN_DOMAIN_ID}\",
|
||||
\"protocol\": \"tcp\"
|
||||
}" \
|
||||
"${API}/org/${PANGOLIN_ORG_ID}/resource")
|
||||
|
||||
RESOURCE_ID=$(echo "${CREATE_RESP}" | jq -r '.data.resourceId')
|
||||
FULL_DOMAIN=$(echo "${CREATE_RESP}" | jq -r '.data.fullDomain')
|
||||
echo " Created resource ${RESOURCE_ID} → ${FULL_DOMAIN}"
|
||||
fi
|
||||
|
||||
# Add target
|
||||
echo " Adding target localhost:${PORT} on site ${PANGOLIN_SITE_ID}…"
|
||||
TARGET_RESP=$(curl -sf -X PUT \
|
||||
-H "${AUTH}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"ip\": \"${TARGET_IP}\",
|
||||
\"port\": ${PORT},
|
||||
\"method\": \"http\",
|
||||
\"siteId\": ${PANGOLIN_SITE_ID}
|
||||
}" \
|
||||
"${API}/resource/${RESOURCE_ID}/target")
|
||||
|
||||
TARGET_ID=$(echo "${TARGET_RESP}" | jq -r '.data.targetId')
|
||||
echo "✓ Target ${TARGET_ID} registered for resource ${RESOURCE_ID}"
|
||||
@@ -2,11 +2,17 @@ name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- '**'
|
||||
branches: ["main", "**"]
|
||||
delete:
|
||||
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
PANGOLIN_API_KEY: ${{ secrets.PANGOLIN_API_KEY }}
|
||||
PANGOLIN_ORG_ID: ${{ secrets.PANGOLIN_ORG_ID }}
|
||||
PANGOLIN_DOMAIN_ID: ${{ secrets.PANGOLIN_DOMAIN_ID }}
|
||||
PANGOLIN_SITE_ID: ${{ secrets.PANGOLIN_SITE_ID }}
|
||||
PANGOLIN_TARGET_IP: ${{ secrets.PANGOLIN_TARGET_IP }}
|
||||
|
||||
jobs:
|
||||
deploy-production:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
@@ -14,34 +20,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_APP_ENV=production \
|
||||
-t ${{ vars.APP_NAME }}:production \
|
||||
-f dockerfile .
|
||||
|
||||
- name: Deploy production container
|
||||
run: |
|
||||
bash .gitea/scripts/deploy.sh \
|
||||
--name "${{ vars.APP_NAME }}" \
|
||||
--tag "production" \
|
||||
--port "${{ vars.PROD_PORT }}" \
|
||||
--env "production"
|
||||
|
||||
- name: Register Pangolin resource
|
||||
run: |
|
||||
bash .gitea/scripts/pangolin-upsert.sh \
|
||||
--subdomain "${{ vars.PROD_SUBDOMAIN }}" \
|
||||
--port "${{ vars.PROD_PORT }}" \
|
||||
--resource-name "${{ vars.APP_NAME }}-production" \
|
||||
--target-ip "${{ secrets.PANGOLIN_TARGET_IP }}"
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
PANGOLIN_API_KEY: ${{ secrets.PANGOLIN_API_KEY }}
|
||||
PANGOLIN_ORG_ID: ${{ secrets.PANGOLIN_ORG_ID }}
|
||||
PANGOLIN_DOMAIN_ID: ${{ secrets.PANGOLIN_DOMAIN_ID }}
|
||||
PANGOLIN_SITE_ID: ${{ secrets.PANGOLIN_SITE_ID }}
|
||||
- uses: zyrrus/node-deploy-action/deploy@v1
|
||||
with:
|
||||
app-name: ${{ vars.APP_NAME }}
|
||||
tag: production
|
||||
port: ${{ vars.PROD_PORT }}
|
||||
environment: production
|
||||
subdomain: ${{ vars.PROD_SUBDOMAIN }}
|
||||
|
||||
deploy-preview:
|
||||
if: github.event_name == 'push' && github.ref != 'refs/heads/main'
|
||||
@@ -51,41 +36,17 @@ jobs:
|
||||
|
||||
- name: Compute preview slug
|
||||
id: slug
|
||||
run: |
|
||||
BRANCH="${GITHUB_REF_NAME}"
|
||||
SLUG=$(echo "$BRANCH" | tr '/' '-' | tr '_' '-' | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]//g' | cut -c1-40)
|
||||
echo "slug=$SLUG" >> "$GITHUB_OUTPUT"
|
||||
PORT=$(( 20000 + ( $(echo -n "$SLUG" | cksum | awk '{print $1}') % 10000 ) ))
|
||||
echo "port=$PORT" >> "$GITHUB_OUTPUT"
|
||||
uses: zyrrus/node-deploy-action/slug@v1
|
||||
with:
|
||||
branch: ${{ github.ref_name }}
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
docker build \
|
||||
--build-arg NEXT_PUBLIC_APP_ENV=preview \
|
||||
-t ${{ vars.APP_NAME }}:${{ steps.slug.outputs.slug }} \
|
||||
-f dockerfile .
|
||||
|
||||
- name: Deploy preview container
|
||||
run: |
|
||||
bash .gitea/scripts/deploy.sh \
|
||||
--name "${{ vars.APP_NAME }}" \
|
||||
--tag "${{ steps.slug.outputs.slug }}" \
|
||||
--port "${{ steps.slug.outputs.port }}" \
|
||||
--env "preview"
|
||||
|
||||
- name: Register Pangolin preview resource
|
||||
run: |
|
||||
bash .gitea/scripts/pangolin-upsert.sh \
|
||||
--subdomain "${{ steps.slug.outputs.slug }}.${{ vars.APP_NAME }}" \
|
||||
--port "${{ steps.slug.outputs.port }}" \
|
||||
--resource-name "${{ vars.APP_NAME }}-${{ steps.slug.outputs.slug }}" \
|
||||
--target-ip "${{ secrets.PANGOLIN_TARGET_IP }}"
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
PANGOLIN_API_KEY: ${{ secrets.PANGOLIN_API_KEY }}
|
||||
PANGOLIN_ORG_ID: ${{ secrets.PANGOLIN_ORG_ID }}
|
||||
PANGOLIN_DOMAIN_ID: ${{ secrets.PANGOLIN_DOMAIN_ID }}
|
||||
PANGOLIN_SITE_ID: ${{ secrets.PANGOLIN_SITE_ID }}
|
||||
- uses: zyrrus/node-deploy-action/deploy@v1
|
||||
with:
|
||||
app-name: ${{ vars.APP_NAME }}
|
||||
tag: ${{ steps.slug.outputs.slug }}
|
||||
port: ${{ steps.slug.outputs.port }}
|
||||
environment: preview
|
||||
subdomain: ${{ steps.slug.outputs.slug }}.${{ vars.APP_NAME }}
|
||||
|
||||
cleanup-preview:
|
||||
if: github.event_name == 'delete' && github.event.ref_type == 'branch'
|
||||
@@ -93,27 +54,15 @@ jobs:
|
||||
steps:
|
||||
- name: Compute preview slug
|
||||
id: slug
|
||||
run: |
|
||||
BRANCH="${{ github.event.ref }}"
|
||||
SLUG=$(echo "$BRANCH" | tr '/' '-' | tr '_' '-' | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]//g' | cut -c1-40)
|
||||
echo "slug=$SLUG" >> "$GITHUB_OUTPUT"
|
||||
uses: zyrrus/node-deploy-action/slug@v1
|
||||
with:
|
||||
branch: ${{ github.event.ref }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Remove Pangolin resource
|
||||
run: |
|
||||
bash .gitea/scripts/pangolin-delete.sh \
|
||||
--resource-name "${{ vars.APP_NAME }}-${{ steps.slug.outputs.slug }}"
|
||||
env:
|
||||
PANGOLIN_API_URL: ${{ secrets.PANGOLIN_API_URL }}
|
||||
PANGOLIN_API_KEY: ${{ secrets.PANGOLIN_API_KEY }}
|
||||
PANGOLIN_ORG_ID: ${{ secrets.PANGOLIN_ORG_ID }}
|
||||
|
||||
- name: Stop preview container
|
||||
run: |
|
||||
CONTAINER="${{ vars.APP_NAME }}-${{ steps.slug.outputs.slug }}"
|
||||
docker stop "$CONTAINER" 2>/dev/null || true
|
||||
docker rm "$CONTAINER" 2>/dev/null || true
|
||||
docker rmi "${{ vars.APP_NAME }}:${{ steps.slug.outputs.slug }}" 2>/dev/null || true
|
||||
- uses: zyrrus/node-deploy-action/cleanup@v1
|
||||
with:
|
||||
app-name: ${{ vars.APP_NAME }}
|
||||
slug: ${{ steps.slug.outputs.slug }}
|
||||
|
||||
10
dockerfile
10
dockerfile
@@ -1,4 +1,4 @@
|
||||
# ── Stage 1: deps
|
||||
# === Stage 1: deps ===
|
||||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
|
||||
@@ -9,19 +9,19 @@ RUN \
|
||||
else npm ci; \
|
||||
fi
|
||||
|
||||
# ── Stage 2: build
|
||||
# === Stage 2: build ===
|
||||
FROM node:20-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
ARG NEXT_PUBLIC_APP_ENV=production
|
||||
ENV NEXT_PUBLIC_APP_ENV=$NEXT_PUBLIC_APP_ENV
|
||||
ARG APP_ENV=production
|
||||
ENV NEXT_PUBLIC_APP_ENV=$APP_ENV
|
||||
|
||||
RUN npm run build
|
||||
|
||||
# ── Stage 3: runner
|
||||
# === Stage 3: runner ===
|
||||
FROM node:20-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
Reference in New Issue
Block a user