Skip to content

v1.1.5

v1.1.5 #186

Workflow file for this run

name: CI/CD Pipeline # GhostClass
on:
pull_request:
branches: [main]
push:
branches: [main]
merge_group:
branches: [main]
permissions:
contents: read
packages: write
id-token: write
jobs:
# This job acts as your "Guard". It runs on EVERY PR and Push.
guard:
name: Build Guard
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# This job only runs if guard passes AND we are pushing to main (merging)
build-push-sign:
name: Build, Push & Sign
needs: guard
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build & push image
id: build-push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/devakesu/ghostclass:main
ghcr.io/devakesu/ghostclass:${{ github.sha }}
secrets: |
sentry_token=${{ secrets.SENTRY_AUTH_TOKEN }}
build-args: |
APP_COMMIT_SHA=${{ github.sha }}
SOURCE_DATE_EPOCH=1704067200
# --- Secrets (must be set in GitHub Repo Secrets) ---
NEXT_PUBLIC_BACKEND_URL=${{ secrets.NEXT_PUBLIC_BACKEND_URL }}
NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
NEXT_PUBLIC_GITHUB_URL=${{ secrets.NEXT_PUBLIC_GITHUB_URL }}
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.NEXT_PUBLIC_SENTRY_DSN }}
NEXT_PUBLIC_TURNSTILE_SITE_KEY=${{ secrets.NEXT_PUBLIC_TURNSTILE_SITE_KEY }}
NEXT_PUBLIC_GA_ID=${{ secrets.NEXT_PUBLIC_GA_ID }}
# --- Sentry Config ---
SENTRY_ORG=devakesu
SENTRY_PROJECT=ghostclass
# --- Public App Info (Hardcoded for stability) ---
NEXT_PUBLIC_APP_NAME=GhostClass
NEXT_PUBLIC_APP_DOMAIN=ghostclass.devakesu.com
NEXT_PUBLIC_APP_URL=https://ghostclass.devakesu.com
NEXT_PUBLIC_SITEMAP_URL=https://ghostclass.devakesu.com/sitemap.xml
NEXT_PUBLIC_AUTHOR_NAME=@deva.kesu
NEXT_PUBLIC_AUTHOR_URL=https://devakesu.com
NEXT_PUBLIC_APP_EMAIL=@ghostclass.devakesu.com
NEXT_PUBLIC_LEGAL_EMAIL=legal@ghostclass.devakesu.com
NEXT_PUBLIC_LEGAL_EFFECTIVE_DATE="January 1, 2026"
labels: |
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.source=https://github.com/devakesu/GhostClass
org.opencontainers.image.created=${{ steps.prep.outputs.created }}
# Retrieve image digest
- name: Get image digest
run: |
echo "IMAGE_DIGEST=${{ steps.build-push.outputs.digest }}" >> $GITHUB_ENV
# Install cosign
- name: Install cosign
uses: sigstore/cosign-installer@v3
# GENERATE SBOM
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ghcr.io/devakesu/ghostclass@${{ env.IMAGE_DIGEST }}
format: cyclonedx-json
output-file: sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
# ATTEST SBOM
- name: Attest SBOM
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign attest \
--yes \
--predicate sbom.json \
--type cyclonedx \
ghcr.io/devakesu/ghostclass@${{ env.IMAGE_DIGEST }}
# GENERATE PROVENANCE (SLSA)
- name: Generate provenance
run: |
cat <<EOF > provenance.json
{
"builder": {
"id": "https://github.com/${{ github.repository }}/.github/workflows/${{ github.workflow }}"
},
"buildType": "https://github.com/slsa-framework/slsa-github-generator/container@v1",
"invocation": {
"configSource": {
"uri": "https://github.com/${{ github.repository }}",
"digest": {
"gitCommit": "${{ github.sha }}"
}
}
},
"metadata": {
"buildInvocationID": "${{ github.run_id }}",
"startedOn": "${{ github.run_started_at }}"
}
}
EOF
# ATTEST PROVENANCE (SLSA)
- name: Attest provenance (SLSA)
run: |
cosign attest \
--yes \
--predicate provenance.json \
--type slsaprovenance \
ghcr.io/devakesu/ghostclass@${{ env.IMAGE_DIGEST }}
# SIGN IMAGE (keyless)
- name: Sign image (keyless)
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign sign --yes \
ghcr.io/devakesu/ghostclass@${{ env.IMAGE_DIGEST }}
# Trigger deployment in Coolify
- name: Trigger Coolify deploy
env:
COOLIFY_BASE_URL: ${{ secrets.COOLIFY_BASE_URL }}
COOLIFY_APP_ID: ${{ secrets.COOLIFY_APP_ID }}
COOLIFY_API_TOKEN: ${{ secrets.COOLIFY_API_TOKEN }}
run: |
curl \
"$COOLIFY_BASE_URL/api/v1/deploy?uuid=$COOLIFY_APP_ID" \
-H "Authorization: Bearer $COOLIFY_API_TOKEN"