Skip to content
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
65 changes: 65 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Build

on:
workflow_call:
inputs:
checkout_ref:
description: "Git ref to checkout and build from"
required: true
type: string
image_name:
description: "GHCR image name (without registry prefix)"
required: true
type: string
outputs:
short_sha:
description: "Short SHA of the built commit"
value: ${{ jobs.build.outputs.short_sha }}
image_ref:
description: "Full image reference with tag (ghcr.io/owner/name:sha)"
value: ${{ jobs.build.outputs.image_ref }}

permissions:
contents: read
packages: write

jobs:
build:
name: Build & Push Image
runs-on: ubuntu-latest
outputs:
short_sha: ${{ steps.sha.outputs.short }}
image_ref: ghcr.io/${{ steps.sha.outputs.owner }}/${{ steps.sha.outputs.image }}:${{ steps.sha.outputs.short }}
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
ref: ${{ inputs.checkout_ref }}

- name: Set up Docker Buildx
Comment thread
Pierre-Demessence marked this conversation as resolved.
Dismissed
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract short SHA and image prefix
id: sha
run: |
echo "short=$(git rev-parse --short=7 HEAD)" >> "$GITHUB_OUTPUT"
echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
echo "image=$(echo '${{ inputs.image_name }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ steps.sha.outputs.owner }}/${{ steps.sha.outputs.image }}:${{ steps.sha.outputs.short }}
ghcr.io/${{ steps.sha.outputs.owner }}/${{ steps.sha.outputs.image }}:latest
cache-from: type=gha
Comment thread
Pierre-Demessence marked this conversation as resolved.
cache-to: type=gha,mode=max
16 changes: 13 additions & 3 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,26 @@ concurrency:
cancel-in-progress: false

jobs:
build:
uses: ./.github/workflows/build.yml
with:
checkout_ref: master
image_name: udc-bot
permissions:
contents: read
packages: write

deploy:
uses: ./.github/workflows/build-and-deploy.yml
needs: build
uses: ./.github/workflows/deploy.yml
with:
image_name: udc-bot
short_sha: ${{ needs.build.outputs.short_sha }}
manifest_path: k8s/prod/bot.yaml
checkout_ref: master
manifest_ref: master
commit_message: "chore(k8s): update prod image to {sha}"
secrets:
APP_ID: ${{ secrets.APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
permissions:
contents: write
packages: write
21 changes: 15 additions & 6 deletions .github/workflows/deploy-to-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,33 @@ jobs:
core.setOutput('is_pr', 'false');
}

deploy:
build:
needs: resolve
uses: ./.github/workflows/build-and-deploy.yml
uses: ./.github/workflows/build.yml
with:
checkout_ref: ${{ needs.resolve.outputs.checkout_ref }}
image_name: udc-bot-dev
permissions:
contents: read
packages: write

deploy:
needs: [resolve, build]
uses: ./.github/workflows/deploy.yml
with:
image_name: udc-bot-dev
short_sha: ${{ needs.build.outputs.short_sha }}
manifest_path: k8s/dev/bot.yaml
checkout_ref: ${{ needs.resolve.outputs.checkout_ref }}
manifest_ref: master
commit_message: ${{ needs.resolve.outputs.commit_message }}
secrets:
APP_ID: ${{ secrets.APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
permissions:
contents: write
packages: write

notify:
needs: [resolve, deploy]
needs: [resolve, build, deploy]
if: always() && needs.resolve.outputs.is_pr == 'true'
name: Notify PR
runs-on: ubuntu-latest
Expand All @@ -97,7 +106,7 @@ jobs:
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ inputs.pr_number }}
SHORT_SHA: ${{ needs.resolve.outputs.short_sha }}
SHORT_SHA: ${{ needs.build.outputs.short_sha }}
REPO_OWNER: ${{ github.repository_owner }}
with:
script: |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build & Deploy
name: Deploy

on:
workflow_call:
Expand All @@ -7,20 +7,20 @@ on:
description: "GHCR image name (without registry prefix)"
required: true
type: string
manifest_path:
description: "Path to the k8s manifest to update"
short_sha:
description: "Short SHA to use as the image tag"
required: true
type: string
checkout_ref:
description: "Git ref to checkout and build from"
manifest_path:
description: "Path to the k8s manifest to update"
required: true
type: string
manifest_ref:
description: "Branch where the manifest lives (for commit+push)"
required: false
description: "Branch where the manifest lives (for checkout + commit)"
required: true
type: string
commit_message:
description: "Commit message for the manifest update"
description: "Commit message template ({sha} will be replaced with short SHA)"
required: true
type: string
secrets:
Expand All @@ -33,48 +33,12 @@ on:

permissions:
contents: write
packages: write

jobs:
build-and-deploy:
name: Build & Deploy
deploy:
name: Update Manifest
runs-on: ubuntu-latest
outputs:
short_sha: ${{ steps.sha.outputs.short }}
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
ref: ${{ inputs.checkout_ref }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract short SHA and image prefix
id: sha
run: |
echo "short=$(git rev-parse --short=7 HEAD)" >> "$GITHUB_OUTPUT"
echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
echo "image=$(echo '${{ inputs.image_name }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
ghcr.io/${{ steps.sha.outputs.owner }}/${{ steps.sha.outputs.image }}:${{ steps.sha.outputs.short }}
ghcr.io/${{ steps.sha.outputs.owner }}/${{ steps.sha.outputs.image }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@v1
Expand All @@ -83,25 +47,30 @@ jobs:
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Checkout manifest branch
if: inputs.manifest_ref != ''
uses: actions/checkout@v4
with:
ref: ${{ inputs.manifest_ref }}
token: ${{ steps.app-token.outputs.token }}

- name: Update manifest image tag
env:
OWNER: ${{ steps.sha.outputs.owner }}
IMAGE: ${{ steps.sha.outputs.image }}
SHORT_SHA: ${{ steps.sha.outputs.short }}
OWNER: ${{ github.repository_owner }}
IMAGE: ${{ inputs.image_name }}
SHORT_SHA: ${{ inputs.short_sha }}
MANIFEST_PATH: ${{ inputs.manifest_path }}
run: |
sed -i "s|image: ghcr.io/${OWNER}/${IMAGE}:.*|image: ghcr.io/${OWNER}/${IMAGE}:${SHORT_SHA}|" "${MANIFEST_PATH}"
OWNER_LC=$(echo "${OWNER}" | tr '[:upper:]' '[:lower:]')
IMAGE_LC=$(echo "${IMAGE}" | tr '[:upper:]' '[:lower:]')
sed -i "s|image: ghcr.io/${OWNER_LC}/${IMAGE_LC}:.*|image: ghcr.io/${OWNER_LC}/${IMAGE_LC}:${SHORT_SHA}|" "${MANIFEST_PATH}"
if ! grep -q "image: ghcr.io/${OWNER_LC}/${IMAGE_LC}:${SHORT_SHA}" "${MANIFEST_PATH}"; then
echo "::error::Failed to update image tag in ${MANIFEST_PATH}"
exit 1
fi

- name: Commit and push manifest update
env:
COMMIT_MSG: ${{ inputs.commit_message }}
SHORT_SHA: ${{ steps.sha.outputs.short }}
SHORT_SHA: ${{ inputs.short_sha }}
MANIFEST_PATH: ${{ inputs.manifest_path }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
Expand Down
112 changes: 112 additions & 0 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
name: PR Build on Comment

on:
issue_comment:
types: [created]

permissions:
contents: read
packages: write
pull-requests: write
issues: write

jobs:
check:
name: Validate trigger
if: |
github.event.issue.pull_request &&
contains(github.event.comment.body, '/build') &&
github.event.comment.author_association != 'NONE' &&
github.event.comment.author_association != 'FIRST_TIMER' &&
github.event.comment.author_association != 'FIRST_TIME_CONTRIBUTOR'
Comment thread
Pierre-Demessence marked this conversation as resolved.
runs-on: ubuntu-latest
outputs:
head_sha: ${{ steps.pr.outputs.head_sha }}
pr_number: ${{ steps.pr.outputs.pr_number }}
steps:
- name: Get PR details
id: pr
uses: actions/github-script@v7
with:
script: |
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
if (pr.state !== 'open') {
core.setFailed(`PR #${context.issue.number} is not open`);
return;
}
core.setOutput('head_sha', pr.head.sha);
core.setOutput('pr_number', context.issue.number);

- name: React to comment
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket',
});

build:
needs: check
uses: ./.github/workflows/build.yml
with:
checkout_ref: ${{ needs.check.outputs.head_sha }}
image_name: udc-bot-dev
permissions:
contents: read
packages: write

notify:
needs: [check, build]
if: needs.check.result != 'skipped' && always()
name: Post result
runs-on: ubuntu-latest
permissions:
pull-requests: write
issues: write
steps:
- name: Post success comment
if: needs.build.result == 'success'
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ needs.check.outputs.pr_number }}
IMAGE_REF: ${{ needs.build.outputs.image_ref }}
SHORT_SHA: ${{ needs.build.outputs.short_sha }}
with:
script: |
const { PR_NUMBER, IMAGE_REF, SHORT_SHA } = process.env;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(PR_NUMBER),
body: [
'🐳 **Build complete!**',
'',
`Image: \`${IMAGE_REF}\``,
'',
'To deploy with manifest changes:',
`1. Update \`k8s/dev/bot.yaml\` image tag to \`${SHORT_SHA}\` on your branch`,
'2. Switch ArgoCD dev target revision to your branch',
].join('\n'),
});

- name: Post failure comment
if: needs.build.result == 'failure'
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ needs.check.outputs.pr_number }}
with:
script: |
const { PR_NUMBER } = process.env;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: Number(PR_NUMBER),
body: `❌ Build failed. Check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.`,
});
11 changes: 9 additions & 2 deletions .github/workflows/pr-deploy-instructions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,15 @@ jobs:
body: [
'### 🚀 Deploy this PR',
'',
`To deploy this branch to the **dev** environment, go to the [Deploy to Dev](${context.payload.repository.html_url}/actions/workflows/deploy-to-dev.yml) workflow and click **Run workflow** with this PR number.`,
'#### Code-only changes',
`1. Make sure the **udc-bot-dev** ArgoCD app target revision is set to \`master\`.`,
`2. Go to the [Deploy to Dev](${context.payload.repository.html_url}/actions/workflows/deploy-to-dev.yml) workflow and click **Run workflow** with this PR number.`,
'',
'This will build a Docker image from your branch, push it to GHCR, and trigger an ArgoCD sync.',
'#### Manifest changes (e.g. new services in `k8s/dev/`)',
'1. Comment `/build` on this PR to build the Docker image (without deploying to master).',
'2. Once the build succeeds, update the image tag in `k8s/dev/bot.yaml` on your branch with the SHA from the build comment, then push.',
'3. In ArgoCD, switch the **udc-bot-dev** app target revision from `master` to your branch (App Details → Source → Edit → Save).',
'4. ArgoCD will sync your manifest changes + new image. Test your changes.',
'5. When done, switch ArgoCD target revision back to `master`.',
].join('\n'),
});
Loading