Skip to content
Merged
Changes from 4 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
241 changes: 241 additions & 0 deletions .github/workflows/build-push-greenhouse-pr-preview.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
# Workflow Flow:
# Initial Setup:
# 1. Developer sets 'greenhouse-pr-build' label on PR
# 2. Action builds Docker image with tag pr-{number}-{sha}
# 3. Action sets 'greenhouse-pr-preview' label (ArgoCD deploys)
#
# On subsequent commits:
# 1. Developer pushes new commit
# 2. PR-Build label already exists
# 3. Action removes PR-Preview label (ArgoCD deletes the app)
# 4. Action builds new Docker image with new tag pr-{number}-{new-sha}
# 5. Action sets PR-Preview label back (ArgoCD redeploys after 2-3 mins)
#
# On PR close/merge:
# 1. PR is closed or merged
# 2. Action deletes all Docker images matching pr-{number}-*
# 3. Action removes PR-Preview label

name: Build Greenhouse PR Preview 🔬

on:
pull_request:
types: [ labeled, synchronize, opened, reopened, closed ]
paths:
- "apps/greenhouse/**"
Comment thread
ArtieReus marked this conversation as resolved.
Outdated

Comment thread
ArtieReus marked this conversation as resolved.
# Ensure only one workflow runs per PR at a time
concurrency:
group: greenhouse-pr-preview-${{ github.event.pull_request.number }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
IMAGE_NAME: "juno-app-greenhouse-pr-preview"
PACKAGE_PATH: "apps/greenhouse"
PR_BUILD_LABEL: "greenhouse-pr-build"
PR_PREVIEW_LABEL: "greenhouse-pr-preview"

Comment thread
ArtieReus marked this conversation as resolved.
jobs:
build-and-push:
name: Build and Push PR Preview Image
# Skip if PR is closed or from a forked repository
if: github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
Comment thread
ArtieReus marked this conversation as resolved.
permissions:
contents: read
packages: write
issues: write
steps:
- name: Check for PR Build label
id: check-label
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const labels = context.payload.pull_request.labels.map(l => l.name);
const hasPrBuildLabel = labels.includes('${{ env.PR_BUILD_LABEL }}');
const hasPrPreviewLabel = labels.includes('${{ env.PR_PREVIEW_LABEL }}');

console.log(`PR Build Label present: ${hasPrBuildLabel}`);
console.log(`PR Preview Label present: ${hasPrPreviewLabel}`);
console.log(`Event action: ${context.payload.action}`);

core.setOutput('should-build', hasPrBuildLabel.toString());
core.setOutput('has-preview-label', hasPrPreviewLabel.toString());

- name: Exit if PR Build label not present
if: steps.check-label.outputs.should-build != 'true'
run: |
echo "Skipping build - '${{ env.PR_BUILD_LABEL }}' label not present"
exit 0

- name: Remove PR Preview label on new commit
if: steps.check-label.outputs.should-build == 'true' &&
steps.check-label.outputs.has-preview-label == 'true' &&
github.event.action == 'synchronize'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
console.log('Removing PR Preview label - new commit detected');
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
name: '${{ env.PR_PREVIEW_LABEL }}'
});
console.log('PR Preview label removed successfully');
} catch (error) {
if (error.status === 404) {
console.log('Label already removed or does not exist');
} else {
throw error;
}
}

- name: Checkout repository
if: steps.check-label.outputs.should-build == 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event.pull_request.head.sha }}

- name: Generate PR version tag
if: steps.check-label.outputs.should-build == 'true'
id: pr-version
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
SHORT_SHA=$(echo ${{ github.event.pull_request.head.sha }} | cut -c1-7)
PR_VERSION="pr-${PR_NUMBER}-${SHORT_SHA}"
echo "PR_VERSION=${PR_VERSION}" >> $GITHUB_OUTPUT
echo "Building version: ${PR_VERSION}"

- name: Log into registry ${{ env.REGISTRY }}
if: steps.check-label.outputs.should-build == 'true'
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Generate Docker metadata
if: steps.check-label.outputs.should-build == 'true'
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=${{ steps.pr-version.outputs.PR_VERSION }}
labels: |
org.opencontainers.image.description=PR Preview for Greenhouse Dashboard
org.opencontainers.image.title=Greenhouse-UI-PR-${{ github.event.pull_request.number }}

- name: Build and push Docker image
if: steps.check-label.outputs.should-build == 'true'
id: build-image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
file: ${{ env.PACKAGE_PATH }}/docker/Dockerfile

- name: Add PR Preview label
if: steps.check-label.outputs.should-build == 'true' && success()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: ['${{ env.PR_PREVIEW_LABEL }}']
});

cleanup:
name: Cleanup PR Preview Image
# Skip if PR is not closed or from a forked repository
if: github.event.action == 'closed' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
issues: write
steps:
- name: Log into registry ${{ env.REGISTRY }}
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Install crane
run: |
VERSION="v0.21.5"
OS="Linux"
ARCH="x86_64"
EXPECTED_SHA256="9f823ae5ee25803161110f957b5fd4538f714d40cdf25dacb4914fefafd246bf"

curl -fsSL "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_${OS}_${ARCH}.tar.gz" -o crane.tar.gz
echo "${EXPECTED_SHA256} crane.tar.gz" | sha256sum -c -
tar -xzf crane.tar.gz crane
sudo install -m 0755 crane /usr/local/bin/crane
rm crane.tar.gz

crane version

Comment thread
ArtieReus marked this conversation as resolved.
- name: Delete PR preview images
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
IMAGE_BASE="${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}"

echo "Looking for images matching pr-${PR_NUMBER}-*"

# List all tags and delete matching ones. Cleanup should be tolerant when
# no repository exists yet or when there are no matching PR tags.
TAGS="$(crane ls "${IMAGE_BASE}" 2>/dev/null || true)"
MATCHING_TAGS="$(printf '%s\n' "${TAGS}" | grep "^pr-${PR_NUMBER}-" || true)"

if [ -z "${MATCHING_TAGS}" ]; then
echo "No preview images found for PR ${PR_NUMBER}"
else
DELETE_FAILED=0

while IFS= read -r tag; do
echo "Deleting ${IMAGE_BASE}:${tag}"
if ! crane delete "${IMAGE_BASE}:${tag}"; then
echo "Failed to delete ${IMAGE_BASE}:${tag}"
DELETE_FAILED=1
fi
done <<EOF
${MATCHING_TAGS}
EOF
Comment thread
ArtieReus marked this conversation as resolved.
Outdated

if [ "${DELETE_FAILED}" -ne 0 ]; then
echo "Cleanup failed for PR ${PR_NUMBER}: one or more preview images could not be deleted"
exit 1
fi
fi

echo "Cleanup completed for PR ${PR_NUMBER}"

- name: Remove PR Preview label
Comment thread
ArtieReus marked this conversation as resolved.
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
name: '${{ env.PR_PREVIEW_LABEL }}'
});
console.log('PR Preview label removed successfully');
} catch (error) {
if (error.status === 404) {
console.log('Label does not exist or already removed');
} else {
console.error('Error removing label:', error);
Comment thread
ArtieReus marked this conversation as resolved.
throw error;
}
}
Loading