Update GitHub deployment action to support multiple test servers #5
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy to Athena Test Server | |
on: | |
pull_request: | |
types: [labeled] | |
concurrency: test-servers | |
env: | |
RAW_URL: https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }} | |
jobs: | |
# Get an up to date version of the label list. github.event.pull_request.labels seems to sometimes be outdated | |
# if the run was waiting for a while, which can cause duplicate deployments | |
get-labels: | |
runs-on: ubuntu-latest | |
outputs: | |
labels: ${{ steps.get-labels.outputs.result }} | |
steps: | |
- name: Get PR labels | |
id: get-labels | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const response = await github.rest.issues.listLabelsOnIssue({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
issue_number: context.issue.number | |
}) | |
const labels = response.data | |
return labels.map(label => label.name) | |
# Check that the build job has run successfully before deploying | |
check-build-status: | |
needs: [ get-labels ] | |
runs-on: ubuntu-latest | |
# Only run workflow if the added label is a deploy label | |
if: contains(needs.get-labels.outputs.labels, 'deploy:athena-test') | |
steps: | |
- name: Get latest successful build for branch | |
id: check_build | |
uses: octokit/[email protected] | |
with: | |
route: GET /repos/${{ github.repository }}/actions/workflows/build.yml/runs?event=pull_request&status=success&head_sha=${{ github.event.pull_request.head.sha }} | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
# Remove deployment-error label if new run is started | |
- uses: actions-ecosystem/action-remove-labels@v1 | |
if: fromJSON(steps.check_build.outputs.data).total_count > 0 | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
labels: | | |
deployment-error | |
# In case of invalid build status, remove deploy labels | |
- uses: actions-ecosystem/action-remove-labels@v1 | |
if: fromJSON(steps.check_build.outputs.data).total_count == 0 | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
labels: | | |
deploy:athena-test1 | |
deploy:athena-test2 | |
- name: Check if latest push had successful build | |
if: fromJSON(steps.check_build.outputs.data).total_count == 0 | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
github.rest.issues.createComment({ | |
issue_number: context.issue.number, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: '### ⚠️ Unable to deploy to test servers ⚠️\nThe docker build needs to run through before deploying.' | |
}) | |
core.setFailed('The build needs to run through first. Please wait for the build to finish and then try again.') | |
# Check which test server to deploy to based on the label | |
filter-matrix: | |
needs: [ get-labels, check-build-status ] | |
runs-on: ubuntu-latest | |
strategy: | |
matrix: | |
include: | |
- environment: athena-test1.ase.cit.tum.de | |
label-identifier: athena-test1 | |
url: https://athena-test1.ase.cit.tum.de | |
user: ${{ vars.DEPLOYMENT_USER }} | |
hosts: athena-test1.ase.cit.tum.de | |
folder: ${{ vars.DEPLOYMENT_FOLDER }} | |
- environment: athena-test2.ase.cit.tum.de | |
label-identifier: athena-test2 | |
url: https://athena-test2.ase.cit.tum.de | |
user: ${{ vars.DEPLOYMENT_USER }} | |
hosts: athena-test2.ase.cit.tum.de | |
folder: ${{ vars.DEPLOYMENT_FOLDER }} | |
outputs: | |
TS1: ${{ steps.filter.outputs.athena-test1 || '' }} | |
TS2: ${{ steps.filter.outputs.athena-test2 || '' }} | |
steps: | |
- run: | | |
echo "$DEPLOY_LABEL" | |
echo '${{ contains(fromJSON(needs.get-labels.outputs.labels), format('deploy:{0}', matrix.label-identifier)) }}' | |
- id: filter | |
env: | |
MATRIX_JSON: ${{ toJSON(matrix) }} | |
if: ${{ contains(fromJSON(needs.get-labels.outputs.labels), format('deploy:{0}', matrix.label-identifier)) }} | |
run: | | |
MATRIX_JSON=${MATRIX_JSON//$'\n'/} | |
echo "${{ matrix.label-identifier }}=$MATRIX_JSON" >> $GITHUB_OUTPUT | |
# Process the output of the filter step to create a valid matrix for the deploy step | |
process-matrix: | |
needs: [ filter-matrix ] | |
runs-on: ubuntu-latest | |
outputs: | |
matrix: ${{ steps.process.outputs.matrix }} | |
steps: | |
- id: process | |
env: | |
MATRIX_JSON: ${{ toJSON(needs.filter-matrix.outputs.*) }} | |
run: | | |
MATRIX_JSON=${MATRIX_JSON//$'\n'/} | |
MATRIX_JSON=${MATRIX_JSON//$'"{'/'{'} | |
MATRIX_JSON=${MATRIX_JSON//$'}"'/'}'} | |
MATRIX_JSON=${MATRIX_JSON//$'\\"'/'"'} | |
echo "$MATRIX_JSON" | |
echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT | |
# Deploy to the test servers | |
deploy: | |
needs: [ process-matrix ] | |
runs-on: ubuntu-latest | |
concurrency: test-servers-deploy | |
strategy: | |
fail-fast: false | |
matrix: | |
include: ${{ fromJSON(needs.process-matrix.outputs.matrix) }} | |
environment: | |
name: ${{ matrix.environment }} | |
url: ${{ matrix.url }} | |
env: | |
DEPLOYMENT_USER: ${{ matrix.user }} | |
DEPLOYMENT_HOST: ${{ matrix.hosts }} | |
DEPLOYMENT_FOLDER: ${{ matrix.folder }} | |
GATEWAY_USER: "jump" | |
GATEWAY_HOST: "gateway.artemis.in.tum.de:2010" | |
GATEWAY_HOST_PUBLIC_KEY: "[gateway.artemis.in.tum.de]:2010 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKtTLiKRILjKZ+Qg4ReWKsG7mLDXkzHfeY5nalSQUNQ4" | |
steps: | |
- uses: actions-ecosystem/action-remove-labels@v1 | |
with: | |
github_token: ${{ secrets.GITHUB_TOKEN }} | |
labels: | | |
deploy:${{ matrix.label-identifier }} | |
- name: Check "lock:${{ matrix.environment }}" label | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const opts = github.rest.issues.listForRepo.endpoint.merge({ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
labels: ['lock:${{ matrix.label-identifier }}'] | |
}) | |
const issues = await github.paginate(opts) | |
if (issues.length == 1 && (!context.issue || issues[0].number != context.issue.number)) { | |
github.rest.issues.createComment({ | |
issue_number: context.issue.number, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: `#### ⚠️ Unable to deploy to test server ⚠️\nAthena Testserver "${{ matrix.environment }}" is already in use by PR #${issues[0].number}.` | |
}) | |
core.setFailed(`Athena Testserver "${{ matrix.environment }}" is already in use by PR #${issues[0].number}.`); | |
} else if (issues.length > 1) { | |
github.rest.issues.createComment({ | |
issue_number: context.issue.number, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
body: '#### ⚠️ Unable to deploy to test server ⚠️\nAthena Testserver "${{ matrix.environment }}" is already in use by multiple PRs. Check PRs with label "lock:${{ matrix.label-identifier }}"!' | |
}) | |
core.setFailed('Athena Testserver "${{ matrix.environment }}" is already in use by multiple PRs. Check PRs with label "lock:${{ matrix.label-identifier }}"!'); | |
} | |
- name: Compute Tag | |
uses: actions/github-script@v7 | |
id: compute-tag | |
with: | |
result-encoding: string | |
script: | | |
if (context.eventName === "pull_request") { | |
return "pr-" + context.issue.number; | |
} | |
if (context.eventName === "release") { | |
return "latest"; | |
} | |
if (context.eventName === "push") { | |
if (context.ref.startsWith("refs/tags/")) { | |
return context.ref; | |
} | |
if (context.ref === "refs/heads/develop") { | |
return "develop"; | |
} | |
} | |
return "FALSE"; | |
# Download athena-server-cli from GH without cloning the Repo | |
- name: Fetch Athena CLI | |
run: | | |
wget ${{ env.RAW_URL }}/athena-server-cli | |
chmod +x athena-server-cli | |
# Configure SSH Key | |
- name: Setup SSH Keys and known_hosts | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
GATEWAY_SSH_KEY: "${{ secrets.DEPLOYMENT_GATEWAY_SSH_KEY }}" | |
DEPLOYMENT_SSH_KEY: "${{ secrets.DEPLOYMENT_SSH_KEY }}" | |
run: | | |
mkdir -p ~/.ssh | |
ssh-agent -a $SSH_AUTH_SOCK > /dev/null | |
ssh-add - <<< $GATEWAY_SSH_KEY | |
ssh-add - <<< $DEPLOYMENT_SSH_KEY | |
cat - <<< $GATEWAY_HOST_PUBLIC_KEY >> ~/.ssh/known_hosts | |
- name: Deploy Athena with Docker | |
env: | |
SSH_AUTH_SOCK: /tmp/ssh_agent.sock | |
TAG: ${{ steps.compute-tag.outputs.result }} | |
run: | | |
./athena-server-cli docker-deploy "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" -g "$GATEWAY_USER@$GATEWAY_HOST" -t $TAG -b $GITHUB_HEAD_REF -d $DEPLOYMENT_FOLDER -y | |
- name: Add "lock:${{ matrix.environment }}" label | |
uses: actions/github-script@v7 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
if (context.issue && context.issue.number) { | |
await github.rest.issues.addLabels({ | |
issue_number: context.issue.number, | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
labels: ['lock:${{ matrix.label-identifier }}'] | |
}) | |
} | |
- name: Update badge | |
uses: RubbaBoy/[email protected] | |
with: | |
NAME: ${{ matrix.label-identifier }} | |
LABEL: ${{ matrix.environment }} | |
STATUS: ${{ github.event.pull_request.head.ref }} | |
COLOR: red | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
# Check that the build job has run successfully before deploying, otherwise add an error label | |
add-error-label: | |
needs: [ get-labels, check-build-status, filter-matrix, process-matrix, deploy ] | |
runs-on: ubuntu-latest | |
if: ${{ failure() }} | |
steps: | |
- name: Add error label | |
uses: actions-ecosystem/action-add-labels@v1 | |
with: | |
labels: deployment-error |