Skip to content
This repository was archived by the owner on May 22, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions .github/actions/ansible-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ runs:
using: composite
steps:
- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version }}

- name: Cache Ansible toolchain
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
~/.cache/pip
Expand Down
4 changes: 2 additions & 2 deletions .github/actions/python-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ runs:
using: composite
steps:
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v6
with:
python-version: ${{ inputs.python-version }}

Expand All @@ -42,7 +42,7 @@ runs:
run: poetry config virtualenvs.in-project true

- name: Cache Poetry dependencies
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: |
~/.cache/pypoetry
Expand Down
180 changes: 176 additions & 4 deletions .github/workflows/ansible-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ on:

permissions:
contents: read
actions: read

concurrency:
group: ansible-deploy-${{ github.ref }}
Expand All @@ -47,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup Ansible toolchain
uses: ./.github/actions/ansible-setup
Expand All @@ -58,9 +59,180 @@ jobs:
ansible-directory: ${{ env.ANSIBLE_DIRECTORY }}
vault-password: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}

wait-for-prerequisites:
name: Wait for Prerequisite Workflows
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Wait for required workflow runs on current commit
env:
GITHUB_TOKEN: ${{ github.token }}
REPOSITORY: ${{ github.repository }}
COMMIT_SHA: ${{ github.sha }}
EVENT_NAME: ${{ github.event_name }}
REF_NAME: ${{ github.ref_name }}
BEFORE_SHA: ${{ github.event.before || '' }}
PR_NUMBER: ${{ github.event.pull_request.number || '' }}
run: |
set -euo pipefail

api() {
curl -fsSL \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"$1"
}

declare -A required_workflows=()

add_workflow() {
required_workflows["$1"]=1
}

changed_files=()
if [[ "$EVENT_NAME" == "pull_request" && -n "$PR_NUMBER" ]]; then
while IFS= read -r path; do
[[ -n "$path" ]] && changed_files+=("$path")
done < <(
api "https://api.github.com/repos/$REPOSITORY/pulls/$PR_NUMBER/files?per_page=100" \
| jq -r '.[].filename'
)
elif [[ -n "$BEFORE_SHA" && "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]]; then
while IFS= read -r path; do
[[ -n "$path" ]] && changed_files+=("$path")
done < <(
api "https://api.github.com/repos/$REPOSITORY/compare/$BEFORE_SHA...$COMMIT_SHA" \
| jq -r '.files[]?.filename'
)
fi

for path in "${changed_files[@]}"; do
case "$path" in
app_go/*|.github/workflows/go-docker.yml)
if [[ "$REF_NAME" == lab* ]]; then
add_workflow "Go Docker Publish"
fi
;;
esac

case "$path" in
app_python/*|.github/actions/python-setup/*|.github/workflows/python-ci.yml)
add_workflow "Python CI"
;;
esac

case "$path" in
app_python/*|.github/workflows/python-docker.yml)
if [[ "$REF_NAME" == lab* ]]; then
add_workflow "Python Docker Publish"
fi
;;
esac
done

deadline=$(( $(date +%s) + 900 ))
grace_deadline=$(( $(date +%s) + 60 ))
tracked_workflows=(
"Go Docker Publish"
"Python CI"
"Python Docker Publish"
)

while (( $(date +%s) < deadline )); do
runs_json="$(
api "https://api.github.com/repos/$REPOSITORY/actions/runs?head_sha=$COMMIT_SHA&per_page=100"
)"

while IFS= read -r workflow_name; do
[[ -n "$workflow_name" ]] && add_workflow "$workflow_name"
done < <(
jq -r '
.workflow_runs[]
| select(
.name == "Go Docker Publish"
or .name == "Python CI"
or .name == "Python Docker Publish"
)
| .name
' <<<"$runs_json"
)

if (( ${#required_workflows[@]} == 0 )); then
echo "No prerequisite workflows apply to this commit."
exit 0
fi

pending=0
failures=()

for workflow_name in "${tracked_workflows[@]}"; do
if [[ -z "${required_workflows[$workflow_name]+x}" ]]; then
continue
fi

run_json="$(
jq -c \
--arg name "$workflow_name" \
'.workflow_runs
| map(select(.name == $name))
| sort_by(.run_started_at // .created_at)
| last // empty' <<<"$runs_json"
)"

if [[ -z "$run_json" ]]; then
if (( $(date +%s) < grace_deadline )); then
echo "Waiting for workflow record: $workflow_name"
pending=1
continue
fi

echo "No run found for $workflow_name on $COMMIT_SHA after grace period; treating it as not triggered."
continue
fi

status="$(jq -r '.status' <<<"$run_json")"
conclusion="$(jq -r '.conclusion // ""' <<<"$run_json")"
event="$(jq -r '.event' <<<"$run_json")"
html_url="$(jq -r '.html_url' <<<"$run_json")"

echo "$workflow_name: status=$status conclusion=${conclusion:-n/a} event=$event"

if [[ "$status" != "completed" ]]; then
pending=1
continue
fi

case "$conclusion" in
success|skipped)
;;
*)
failures+=("$workflow_name ($conclusion) $html_url")
;;
esac
done

if (( ${#failures[@]} > 0 )); then
printf 'Prerequisite workflow failed: %s\n' "${failures[@]}" >&2
exit 1
fi

if (( pending == 0 )); then
echo "All prerequisite workflows finished successfully."
exit 0
fi

sleep 15
done

echo "Timed out while waiting for prerequisite workflows on $COMMIT_SHA." >&2
exit 1

deploy:
name: Deploy Application
needs: lint
needs:
- lint
- wait-for-prerequisites
if: github.event_name != 'pull_request'
runs-on:
- self-hosted
Expand All @@ -69,7 +241,7 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Setup Ansible toolchain
uses: ./.github/actions/ansible-setup
Expand Down Expand Up @@ -131,7 +303,7 @@ jobs:

- name: Upload deployment log
if: always() && steps.deploy.outputs.log-path != ''
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: ansible-deploy-log
path: ${{ steps.deploy.outputs.log-path }}
Expand Down
82 changes: 82 additions & 0 deletions .github/workflows/go-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Go Docker Publish

on:
push:
branches:
- "lab*"
paths:
- app_go/**
- .github/workflows/go-docker.yml
pull_request:
branches:
- master
types:
- closed
paths:
- app_go/**
- .github/workflows/go-docker.yml

jobs:
build-and-push-branch:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Derive lab+sha tag from branch
id: version
run: |
source_branch="${{ github.ref_name }}"
if [[ "$source_branch" =~ ([0-9]+) ]]; then
lab_number="${BASH_REMATCH[1]}"
lab_number=$((10#$lab_number))
short_sha="${GITHUB_SHA::7}"
echo "branch_tag=1.${lab_number}.${short_sha}" >> "$GITHUB_OUTPUT"
else
echo "Failed to extract lab number from branch: $source_branch" >&2
exit 1
fi
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image (branch)
uses: docker/build-push-action@v7
with:
context: ./app_go
file: ./app_go/Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/devops-app-go:${{ steps.version.outputs.branch_tag }}

build-and-push:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Derive lab version tag from merged branch
id: version
run: |
source_branch="${{ github.event.pull_request.head.ref }}"
if [[ "$source_branch" =~ ([0-9]+) ]]; then
lab_number="${BASH_REMATCH[1]}"
lab_number=$((10#$lab_number))
echo "version_tag=1.${lab_number}" >> "$GITHUB_OUTPUT"
else
echo "Failed to extract lab number from merged branch: $source_branch" >&2
exit 1
fi
- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: ./app_go
file: ./app_go/Dockerfile
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/devops-app-go:${{ steps.version.outputs.version_tag }}
${{ secrets.DOCKERHUB_USERNAME }}/devops-app-go:latest
4 changes: 2 additions & 2 deletions .github/workflows/python-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
run:
working-directory: ./app_python
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- name: Setup Python tooling and dependencies
uses: ./.github/actions/python-setup
with:
Expand All @@ -48,7 +48,7 @@ jobs:
--cov-report=xml:test-results/coverage.xml
- name: Upload pytest and coverage reports
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: python-test-reports
path: |
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/python-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Derive lab+sha tag from branch
id: version
run: |
Expand All @@ -36,12 +36,12 @@ jobs:
exit 1
fi
- name: Log in to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image (branch)
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: ./app_python
file: ./app_python/Dockerfile
Expand All @@ -53,7 +53,7 @@ jobs:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Derive lab version tag from merged branch
id: version
run: |
Expand All @@ -67,12 +67,12 @@ jobs:
exit 1
fi
- name: Log in to Docker Hub
uses: docker/login-action@v3
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v6
uses: docker/build-push-action@v7
with:
context: ./app_python
file: ./app_python/Dockerfile
Expand Down
Loading