Skip to content

Preview (deploy) #31669

Preview (deploy)

Preview (deploy) #31669

name: Preview (deploy)
on:
workflow_run:
workflows:
- 'Preview (build)'
types:
- completed
jobs:
cache-manifests-file:
name: Cache Manifests File
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
permissions:
# "If you specify the access for any of these scopes, all of those that are not specified are set to none."
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
actions: read # Access cache
outputs:
manifests-cache-key: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }}
git-ref: ${{ steps.event.outputs.GIT_REF }}
pr-number: ${{ steps.event.outputs.PR_NUMBER }}
action: ${{ steps.event.outputs.ACTION }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
disable-sudo: true
egress-policy: block
allowed-endpoints: >
api.github.com:443
- name: 'Download artifacts'
# Fetch output (zip archive) from the workflow run that triggered this workflow.
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "preview-spec"
})[0];
if (matchArtifact === undefined) {
throw TypeError('Build Artifact not found!');
}
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data));
- name: 'Accept event from first stage'
run: unzip preview-spec.zip event.json
- name: Read Event into ENV
id: event
run: |
echo PR_NUMBER=$(jq '.number | tonumber' < event.json) >> $GITHUB_OUTPUT
echo ACTION=$(jq --raw-output '.action | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT
echo GIT_REF=$(jq --raw-output '.pull_request.head.sha | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT
- name: Hash Rendered Manifests File
id: hash
# If the previous workflow was triggered by a PR close event, we will not have a manifests file artifact.
if: ${{ steps.event.outputs.ACTION != 'closed' }}
run: |
unzip preview-spec.zip manifests.rendered.yml
ls
echo "MANIFESTS_FILE_HASH=$(md5sum manifests.rendered.yml | awk '{ print $1 }')" >> $GITHUB_OUTPUT
- name: Cache Manifests File
if: ${{ steps.event.outputs.ACTION != 'closed' }}
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
with:
path: manifests.rendered.yml
key: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }}
- name: DEBUG - Print Job Outputs
if: ${{ runner.debug }}
run: |
echo "PR number: ${{ steps.event.outputs.PR_NUMBER }}"
echo "Git Ref: ${{ steps.event.outputs.GIT_REF }}"
echo "Action: ${{ steps.event.outputs.ACTION }}"
echo "Manifests file hash: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }}"
cat event.json
deploy-uffizzi-preview:
name: Deploy to Uffizzi Virtual Cluster
needs:
- cache-manifests-file
if: ${{ github.event.workflow_run.conclusion == 'success' && needs.cache-manifests-file.outputs.action != 'closed' }}
permissions:
contents: read
pull-requests: write
id-token: write
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
# Identify comment to be updated
- name: Find comment for Ephemeral Environment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
id: find-comment
with:
issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }}
comment-author: 'github-actions[bot]'
body-includes: pr-${{ needs.cache-manifests-file.outputs.pr-number }}
direction: last
# Create/Update comment with action deployment status
- name: Create or Update Comment with Deployment Notification
id: notification
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }}
body: |
## Uffizzi Ephemeral Environment - Virtual Cluster
:cloud: deploying cluster `pr-${{ needs.cache-manifests-file.outputs.pr-number }}`
:gear: Updating now by workflow run [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
Download the Uffizzi CLI to interact with the upcoming virtual cluster
https://docs.uffizzi.com/install
edit-mode: replace
- name: Connect to Virtual Cluster
uses: UffizziCloud/cluster-action@main
with:
cluster-name: pr-${{ needs.cache-manifests-file.outputs.pr-number }}
server: https://app.uffizzi.com
- name: Fetch cached Manifests File
id: cache
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4
with:
path: manifests.rendered.yml
key: ${{ needs.cache-manifests-file.outputs.manifests-cache-key }}
- name: Kustomize and Apply Manifests
id: prev
run: |
# Apply kustomized manifests to virtual cluster.
export KUBECONFIG=`pwd`/kubeconfig
kubectl apply -f manifests.rendered.yml --kubeconfig ./kubeconfig
# Allow uffizzi to sync the resources
sleep 10
# Get the hostnames assigned by uffizzi
export BACKSTAGE_HOST=$(kubectl get ingress backstage --kubeconfig kubeconfig -o json | jq '.spec.rules[0].host' | tr -d '"')
export UFFIZZI_CLUSTER_APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
# Patch backstage deployment to use UFFIZZI_URL
kubectl patch deployment backstage --kubeconfig kubeconfig -p '{"spec": {"template": {"spec": {"containers": [{"name": "backstage", "args":["-c", "APP_CONFIG_app_baseUrl='https://${BACKSTAGE_HOST}' APP_CONFIG_backend_baseUrl='https://${BACKSTAGE_HOST}' APP_CONFIG_auth_environment='production' node packages/backend --config app-config.yaml"], "env": [{"name": "UFFIZZI_URL", "value": "'https://${BACKSTAGE_HOST}'"}, {"name": "UFFIZZI_CLUSTER_APISERVER", "value": "'${UFFIZZI_CLUSTER_APISERVER}'"}, {"name": "GITHUB_SHA", "value": "'${GITHUB_SHA}'"}, {"name": "REF_NAME", "value": "'${{ needs.cache-manifests-file.outputs.git-ref }}'"}]}]}}}}'
if [[ ${RUNNER_DEBUG} == 1 ]]; then
kubectl get all --kubeconfig ./kubeconfig
fi
echo "backstage_url=${BACKSTAGE_HOST}" >> $GITHUB_OUTPUT
echo "Access the \`backstage\` endpoint at [\`${BACKSTAGE_HOST}\`](http://${BACKSTAGE_HOST})" >> $GITHUB_STEP_SUMMARY
- name: Create or Update Comment with Deployment URL
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.notification.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
## Uffizzi Ephemeral Environment - Virtual Cluster
Your cluster `pr-${{ needs.cache-manifests-file.outputs.pr-number }}` was successfully created. Learn more about [Uffizzi virtual clusters](https://docs.uffizzi.com/topics/virtual-clusters)
To connect to this cluster, follow these steps:
1. Download and install the Uffizzi CLI from https://docs.uffizzi.com/install
2. Login to Uffizzi, then select the `backstage` account and project:
```
uffizzi login
```
```
Select an account:
‣ ${{ github.event.repository.name }}
jdoe
Select a project or create a new project:
‣ ${{ github.event.repository.name }}-6783521
```
3. Update your kubeconfig: `uffizzi cluster update-kubeconfig pr-${{ needs.cache-manifests-file.outputs.pr-number }} --kubeconfig=[PATH_TO_KUBECONFIG]`
After updating your kubeconfig, you can manage your cluster with `kubectl`, `kustomize`, `helm`, and other tools that use kubeconfig files: `kubectl get namespace --kubeconfig [PATH_TO_KUBECONFIG]`
Access the `backstage` endpoint at [`https://${{ steps.prev.outputs.backstage_url }}`](https://${{ steps.prev.outputs.backstage_url }})
edit-mode: replace
delete-uffizzi-preview:
permissions:
contents: read
pull-requests: write
id-token: write
name: Delete the Uffizzi Virtual Cluster
needs:
- cache-manifests-file
if: ${{ needs.cache-manifests-file.outputs.action == 'closed' }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
- name: Delete Virtual Cluster
uses: UffizziCloud/cluster-action@main
with:
cluster-name: pr-${{ needs.cache-manifests-file.outputs.pr-number }}
server: https://app.uffizzi.com
action: delete
# Identify comment to be updated
- name: Find comment for Ephemeral Environment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
id: find-comment
with:
issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }}
comment-author: 'github-actions[bot]'
body-includes: pr-${{ needs.cache-manifests-file.outputs.pr-number }}
direction: last
- name: Update Comment with Deletion
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }}
body: |
Uffizzi Cluster `pr-${{ needs.cache-manifests-file.outputs.pr-number }}` was deleted.
edit-mode: replace