name: CI/CD - Build, Push, Deploy (Ghaymah) # Runs automatically on pushes to the main branch, or manually for any branch/environment via workflow_dispatch on: push: branches: ["main"] workflow_dispatch: inputs: image_tag: description: "Optional extra image tag (default: git SHA)" required: false default: "" # Least privilege permissions permissions: contents: read packages: write env: # Docker Hub registry address DOCKER_HUB_REGISTRY: docker.io GHAYMAH_NAMESPACE: my-team IMAGE_NAME: sample-api PROJECT_STAGING_NAME: sample-api-staging PROJECT_PRODUCTION_NAME: sample-api-production jobs: # --------------------------------------------------------------------- # 1) Build the image, test it, and push it to Docker Hub # --------------------------------------------------------------------- build-and-push: name: Build & Push Image runs-on: ubuntu-latest outputs: image_ref: ${{ steps.vars.outputs.image_ref }} steps: - name: Checkout code uses: actions/checkout@v5.0.0 - name: Set image tag variables id: vars run: | SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7) TAG="${{ github.event.inputs.image_tag }}" if [ -z "$TAG" ]; then TAG="$SHORT_SHA"; fi IMAGE_REF="${DOCKER_HUB_REGISTRY}/${GHAYMAH_NAMESPACE}/${IMAGE_NAME}:${TAG}" echo "image_ref=${IMAGE_REF}" >> "$GITHUB_OUTPUT" echo "Building: ${IMAGE_REF}" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 # Log in to Docker Hub using credentials stored as GitHub Secrets - name: Log in to Ghaymah Container Registry uses: docker/login-action@v3 with: registry: ${{ env.DOCKER_HUB_REGISTRY }} username: ${{ secrets.GHAYMAH_REGISTRY_USER }} password: ${{ secrets.GHAYMAH_REGISTRY_TOKEN }} - name: Build image uses: docker/build-push-action@v6 with: context: . push: false load: true tags: | ${{ steps.vars.outputs.image_ref }} ${{ env.DOCKER_HUB_REGISTRY }}/${{ env.GHAYMAH_NAMESPACE }}/${{ env.IMAGE_NAME }}:latest cache-from: type=gha cache-to: type=gha,mode=max # Quick smoke test to confirm /health responds before pushing - name: Smoke test the built image run: | docker run -d --name smoke -p 8080:8080 ${{ steps.vars.outputs.image_ref }} for i in $(seq 1 10); do if curl -sf http://localhost:8080/health; then echo "Health check passed"; break; fi echo "Waiting for app to start... ($i/10)"; sleep 2 done curl -sf http://localhost:8080/health || (docker logs smoke && exit 1) docker stop smoke - name: Push image to Ghaymah Registry uses: docker/build-push-action@v6 with: context: . push: true tags: | ${{ steps.vars.outputs.image_ref }} ${{ env.DOCKER_HUB_REGISTRY }}/${{ env.GHAYMAH_NAMESPACE }}/${{ env.IMAGE_NAME }}:latest # --------------------------------------------------------------------- # 2) Automatic deployment to STAGING right after a successful build - no manual approval # --------------------------------------------------------------------- deploy-staging: name: Deploy to Staging needs: build-and-push runs-on: ubuntu-latest environment: name: staging steps: - name: Install Ghaymah CLI run: | curl -fsSL https://cli.ghaymah.systems/install.sh | sh gy version - name: Authenticate Ghaymah CLI run: gy auth login -e "${{ secrets.GHAYMAH_Email }}" -p "${{ secrets.GHAYMAH_PASSWORD }}" - name: create project run: gy resource project create -s .name=${{ env.PROJECT_STAGING_NAME }} || true - name: get project id id: get_project_id run: | echo "PROJECT_ID=$(gy resource project get | grep -oP '"id"\s*:\s*"\K[^"]+')" >> $GITHUB_ENV - name: initialize application in Ghaymah run: gy resource app init . -p ${{ env.PROJECT_ID }} - name: Deploy Application to Staging run: | gy resource app launch # --------------------------------------------------------------------- # 3) Deploy to PRODUCTION - requires manual approval # Approval is handled by the protected GitHub Environment "production" # with Required Reviewers in repository settings, not by custom code. # --------------------------------------------------------------------- deploy-production: name: Deploy to Production (Manual Approval Required) needs: [build-and-push, deploy-staging] runs-on: ubuntu-latest environment: name: production # This line enables the manual approval gate configured in repository settings steps: - name: Install Ghaymah CLI run: | curl -fsSL https://cli.ghaymah.systems/install.sh | sh gy version - name: Authenticate Ghaymah CLI run: gy auth login -e "${{ secrets.GHAYMAH_Email }}" -p "${{ secrets.GHAYMAH_PASSWORD }}" - name: create project run: gy resource project create -s .name=${{ env.PROJECT_PRODUCTION_NAME }} || true - name: get project id id: get_project_id run: | echo "PROJECT_ID=$(gy resource project get | grep -oP '"id"\s*:\s*"\K[^"]+')" >> $GITHUB_ENV - name: initialize application in Ghaymah run: gy resource app init . -p ${{ env.PROJECT_ID }} - name: Deploy Application to Production run: | gy resource app launch