Skip to content
Closed
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
45 changes: 45 additions & 0 deletions .github/workflows/studios-CICD-pipeline.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Studios Agent Hub Build and Deploy
on:
push:
branches:
- main
- AH-125_workflows
workflow_dispatch:

concurrency:
group: studios-ci-${{ github.ref }}
cancel-in-progress: true

jobs:
frontend:
uses: ./.github/workflows/studios-frontend.yaml
backend:
uses: ./.github/workflows/studios-backend.yaml

build:
needs: [frontend, backend]
uses: ./.github/workflows/studios-build.yml
with:
productEcrRegistry: 590183674594.dkr.ecr.eu-west-1.amazonaws.com
awsRole: arn:aws:iam::590183674594:role/dev-ai-agent-hub-service-gha
serviceName: studios-agent-hub
publish: ${{ github.ref == 'refs/heads/main' }}
runsOn: 'dev-studios-agent-hub'
stg_deploy:
needs: build
uses: ./.github/workflows/studios-deploy.yml
with:
imageTag: ${{ needs.build.outputs.image_tag || 'latest' }}
ecosystem: dev
gitRef: ${{ github.ref }}
secrets: inherit

prd_deploy:
needs: [build, stg_deploy]
uses: ./.github/workflows/studios-deploy.yml
with:
imageTag: ${{ needs.build.outputs.image_tag || 'latest' }}
ecosystem: prd
prdPromoteImage: true
gitRef: ${{ github.ref }}
secrets: inherit
45 changes: 45 additions & 0 deletions .github/workflows/studios-auto-tag-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Auto Tag Merged Sync PR

on:
push:
branches:
- main

permissions:
contents: write

jobs:
tag-release:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Check Merge Commit & Create Tag
run: |
# Get the last commit message on main
COMMIT_MSG=$(git log -1 --pretty=%B)

# Regex check if the commit came from a sync/vX.Y.Z branch merge
if [[ "$COMMIT_MSG" =~ sync/(v[0-9]+\.[0-9]+\.[0-9]+) ]]; then
UPSTREAM_VERSION="${BASH_REMATCH[1]}"
NEW_TAG="itv-$UPSTREAM_VERSION"

echo "Detected merge for version $UPSTREAM_VERSION."

# Check if tag already exists
if git rev-parse "$NEW_TAG" >/dev/null 2>&1; then
echo "Tag $NEW_TAG already exists. Skipping."
else
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

echo "Creating tag $NEW_TAG..."
git tag "$NEW_TAG"
git push origin "$NEW_TAG"
fi
else
echo "Commit is not a sync branch merge. Skipping tag creation."
fi
38 changes: 38 additions & 0 deletions .github/workflows/studios-backend.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# ─────────────────────────────────────────────────────────────────────────────
# Backend CI — Python formatting checks via Ruff
# Runs on pushes and PRs to main/dev when backend files change
# ─────────────────────────────────────────────────────────────────────────────
name: Python CI

on:
workflow_call:

concurrency:
group: backend-${{ github.ref }}
cancel-in-progress: true

jobs:
# ── Ruff format check across supported Python versions ───────────────────
format-check:
name: Ruff Format (${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python-version: ['3.11', '3.12']
steps:
- uses: actions/checkout@v5

- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Install formatter
run: pip install "ruff>=0.15.5"

- name: Verify formatting
run: ruff format --check . --exclude .venv --exclude venv

- name: Detect logic errors
run: ruff check --select=F --ignore=F401,F403,F405,F541,F811,F841 --output-format=github .
108 changes: 108 additions & 0 deletions .github/workflows/studios-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
name: Studios Agent Hub Build
on:
workflow_call:
inputs:
productEcrRegistry:
description: 'ECR Registry'
required: true
type: string
awsRole:
description: 'AWS IAM Role'
required: false
type: string
serviceName:
description: 'Name of service'
required: true
type: string
publish:
description: 'Publish to ECR'
required: false
type: boolean
default: false
runsOn:
description: 'Defaults to ubuntu-latest, for self hosted runners use cd-runner-$serviceName-$tShirtSize'
required: false
type: string
default: 'ubuntu-latest'
outputs:
image_tag:
description: 'Tag of the built image'
value: ${{ jobs.build.outputs.image_tag }}

secrets:
AWS_ACCESS_KEY_ID:
description: 'AWS Access Key'
required: false
AWS_SECRET_ACCESS_KEY:
description: 'AWS Secret Key'
required: false

env:
DOCKER_API_VERSION: "1.43"
jobs:
build:
timeout-minutes: 60
permissions:
id-token: write
contents: write
runs-on: ${{ inputs.runsOn }}
outputs:
image_tag: ${{ steps.set-tag.outputs.image_tag }}
steps:
- uses: actions/checkout@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: eu-west-1
role-to-assume: ${{ inputs.awsRole }}
role-duration-seconds: 1200

- name: Log in to the root ECR
uses: docker/login-action@v2
with:
registry: 655028521085.dkr.ecr.eu-west-1.amazonaws.com

- name: Extract metadata (tags, labels) for service image
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ inputs.productEcrRegistry }}/${{ inputs.serviceName }}
tags: |
type=ref,event=branch,prefix=gha-,suffix=-{{sha}}-${{ github.run_number }}
type=ref,event=pr,prefix=gha-pr-,suffix=-{{sha}}-${{ github.run_number }}

- name: Log in to the ECR
if: ${{ inputs.publish }}
uses: docker/login-action@v2
with:
registry: ${{ inputs.productEcrRegistry }}

- name: Set up docker context
run: docker context create builders

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
endpoint: builders

- name: Build and push service image
uses: docker/build-push-action@v4
with:
context: .
push: ${{ inputs.publish }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
# build-args: |
# ARTIFACTORY_USERNAME=${{ secrets.ARTIFACTORY_USERNAME }}
# ARTIFACTORY_PASSWORD=${{ secrets.ARTIFACTORY_PASSWORD }}

- name: Tag built version
id: set-tag
if: ${{ inputs.publish }}
env:
TAG_VALUE: ${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}
run: |
git tag $TAG_VALUE $GITHUB_SHA
git push origin $TAG_VALUE
echo "image_tag=$TAG_VALUE" >> $GITHUB_OUTPUT
42 changes: 42 additions & 0 deletions .github/workflows/studios-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Agent Hub Deploy

on:
workflow_call:
inputs:
imageTag:
description: 'Docker image tag to deploy'
required: true
type: string
ecosystem:
description: 'Target APLO ecosystem (dev for stg, prd for production)'
required: true
type: string
prdPromoteImage:
description: 'Enable production image promotion'
required: false
type: boolean
default: false
gitRef:
description: 'Git reference override for APLO'
required: false
type: string
default: 'refs/heads/main'
secrets:
INTERNAL_REPO_RO_PAT:
required: true

jobs:
deploy:
uses: ITV/cp-gha-workflows/.github/workflows/aplo.yml@378a119ba22f72f30b95ed76485504bbca6a189c
with:
ECOSYSTEM: ${{ inputs.ecosystem }}
PRODUCT: ca
SERVICE: studios-airtable-catalogue
GIT_BRANCH: main
GIT_REF_OVERRIDE: ${{ inputs.gitRef }}
IMAGE_TAG: ${{ inputs.imageTag }}
BUILD_ENABLED: false
PRD_PROMOTE_IMAGE: ${{ inputs.prdPromoteImage }}
DEPLOY_ENABLED: false
secrets:
INTERNAL_REPO_RO_PAT: ${{ secrets.INTERNAL_REPO_RO_PAT }}
110 changes: 110 additions & 0 deletions .github/workflows/studios-deploy_old.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: Agent Hub Deploy Old

on:
workflow_call:
inputs:
imageTag:
description: 'Docker image tag to deploy'
required: true
type: string
ecosystem:
description: 'Target APLO ecosystem (dev for stg, prd for production)'
required: true
type: string
prdPromoteImage:
description: 'Enable production image promotion'
required: false
type: boolean
default: false
gitRef:
description: 'Git reference override for APLO'
required: false
type: string
default: 'refs/heads/main'

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Log in to the root ECR
uses: docker/login-action@v2
with:
registry: 655028521085.dkr.ecr.eu-west-1.amazonaws.com

- name: Log in to the ECR
if: ${{ inputs.publish }}
uses: docker/login-action@v2
with:
registry: ${{ inputs.productEcrRegistry }}

- name: Check prd testing image exists
if: inputs.ECOSYSTEM == 'prd' && needs.generate-values.outputs.ENABLE_TEST_IMAGE == 'true' && !inputs.BUILD_IN_PRD
run: |
TEST_TAG="${{ env.IMAGE_TO_DEPLOY }}-testing-image"
if ! PRD_TEST_DESCRIBE=$(aws ecr describe-images --repository-name "${{ inputs.SERVICE }}" --region "${{ env.AWS_REGION }}" --output json); then
echo "Unable to describe PRD ECR repository for $TEST_TAG" >&2
exit 1
fi
PRD_TEST_TAGS=$(echo "$PRD_TEST_DESCRIBE" | jq -r '.imageDetails[].imageTags[]?' || true)

if echo "$PRD_TEST_TAGS" | grep -Fx "$TEST_TAG" >/dev/null; then
echo "TESTING_IMAGE_EXISTS=true" >> "$GITHUB_ENV"
echo "Detected testing image tag $TEST_TAG in PRD ECR"
else
echo "TESTING_IMAGE_EXISTS=" >> "$GITHUB_ENV"
echo "Testing image tag $TEST_TAG not found in PRD ECR; will attempt to copy from DEV if promotion is enabled"
fi

- name: Copy testing image to prd
if: inputs.ECOSYSTEM == 'prd' && needs.generate-values.outputs.ENABLE_TEST_IMAGE == 'true' && (inputs.GIT_REF_OVERRIDE == '' || inputs.PRD_PROMOTE_IMAGE) && !inputs.BUILD_IN_PRD
run: |
if [[ '${{ env.TESTING_IMAGE_EXISTS }}' == '' ]]; then
if docker pull ${{ needs.generate-values.outputs.DEV_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}-testing-image; then
docker tag ${{ needs.generate-values.outputs.DEV_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}-testing-image ${{ needs.generate-values.outputs.PRD_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}-testing-image
docker push ${{ needs.generate-values.outputs.PRD_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}-testing-image
else
if [[ '${{ env.IS_ROLLBACK }}' == 'true' ]]; then
echo "⚠️ Testing image ${{ env.IMAGE_TO_DEPLOY }}-testing-image not found in DEV ECR during rollback; deployment will continue but canary tests may be skipped or fail"
else
echo "Testing image ${{ env.IMAGE_TO_DEPLOY }}-testing-image missing in DEV ECR; rerun build or ensure testing images are available" >&2
exit 1
fi
fi
else
echo "Testing image with tag ${{ env.IMAGE_TO_DEPLOY }}-testing-image already exists in the repository ${{ inputs.SERVICE }}"
exit 0
fi

- name: Check prd image exists
if: inputs.ECOSYSTEM == 'prd' && !inputs.BUILD_IN_PRD
run: |
IMAGE_TAG="${{ env.IMAGE_TO_DEPLOY }}"
if ! PRD_DESCRIBE=$(aws ecr describe-images --repository-name "${{ inputs.SERVICE }}" --region "${{ env.AWS_REGION }}" --output json); then
echo "Unable to describe PRD ECR repository for $IMAGE_TAG" >&2
exit 1
fi
PRD_TAGS=$(echo "$PRD_DESCRIBE" | jq -r '.imageDetails[].imageTags[]?' || true)

if echo "$PRD_TAGS" | grep -Fx "$IMAGE_TAG" >/dev/null; then
echo "IMAGE_EXISTS=true" >> "$GITHUB_ENV"
echo "Detected image tag $IMAGE_TAG in PRD ECR"
else
echo "IMAGE_EXISTS=" >> "$GITHUB_ENV"
echo "Image tag $IMAGE_TAG not found in PRD ECR; attempting copy from DEV"
fi

- name: Copy image to prd
if: inputs.ECOSYSTEM == 'prd' && (inputs.GIT_REF_OVERRIDE == '' || inputs.PRD_PROMOTE_IMAGE) && !inputs.BUILD_IN_PRD
run: |
if [[ '${{ env.IMAGE_EXISTS }}' == '' ]]; then
if docker pull ${{ needs.generate-values.outputs.DEV_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}; then
docker tag ${{ needs.generate-values.outputs.DEV_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }} ${{ needs.generate-values.outputs.PRD_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}
docker push ${{ needs.generate-values.outputs.PRD_REGISTRY }}/${{ inputs.SERVICE }}:${{ env.IMAGE_TO_DEPLOY }}
else
echo "Failed to pull image ${{ env.IMAGE_TO_DEPLOY }} from DEV ECR; confirm the tag exists or rebuild the image before retrying."
exit 1
fi
else
echo "Image with tag ${{ env.IMAGE_TO_DEPLOY }} already exists in the repository ${{ inputs.SERVICE }}"
exit 0
fi
Loading