Skip to content

Commit

Permalink
feat: break argus-docker-build workflow into composite actions (#283)
Browse files Browse the repository at this point in the history
  • Loading branch information
hspitzley-czi authored Jul 15, 2024
1 parent 2d3cc94 commit d9d1014
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 150 deletions.
123 changes: 123 additions & 0 deletions .github/actions/argus-builder/build-prep/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: argus-docker-build-prep
description: Prepare for building a Docker Image for Argus

inputs:
path_filters:
description: 'Glob patterns to match against changed files in the repository, comma delimited'
required: false
default: '**/*'
path_filters_base:
description: |
Git reference (e.g. branch name) against which the changes will be detected. Defaults to the current branch.
If it references same branch it was pushed to, changes are detected against the most recent commit before the push.
This option is ignored if action is triggered by pull_request event.
required: false
default: ${{ github.ref }}
branches:
description: 'Branch names to run this job on, supports wildcards, comma delimited'
required: false
default: '*'
branches_ignore:
description: 'Branch names to run this job on, supports wildcards, comma delimited'
required: false
default: ''

outputs:
image_tag:
description: A custom tag to apply to the images that are built
value: ${{ steps.build_tags.outputs.IMAGE_TAG }}
should_build:
description: Whether the job should run
value: ${{ steps.final_check.outputs.should_build }}

runs:
using: composite
steps:
- name: Check for matching branch
id: branch_filter
uses: actions/github-script@v7
with:
script: |
function wildcardMatch(text, pattern) {
const regexPattern =
new RegExp('^' + pattern.replace(/\?/g, '.').replace(/\*/g, '.*') + '$');
return regexPattern.test(text);
}
const branches = `${{ inputs.branches }}`.split(',').map(b => b.trim()).filter(b => b.length > 0);
console.log('Branches to run against:', branches);
const branchesIgnore = `${{ inputs.branches_ignore }}`.split(',').map(b => b.trim()).filter(b => b.length > 0);
console.log('Branches to ignore:', branchesIgnore);
const branch = `${{ github.ref }}`.replace('refs/heads/', '');
const shouldRun = branches.some(b => wildcardMatch(branch, b)) && !branchesIgnore.some(b => wildcardMatch(branch, b));
if (shouldRun) {
console.log('Job will run');
} else {
console.log(`Job will be skipped because branch name "${branch}" does not match the filters`);
}
core.setOutput('match', shouldRun);
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get build tag
id: build_tags
shell: bash
run: |
echo "IMAGE_TAG=sha-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Validate build tag
uses: actions/github-script@v7
with:
script: |
const imageTag = `${{ steps.build_tags.outputs.IMAGE_TAG }}`;
if (imageTag === '' || imageTag === 'sha-') {
core.setFailed('The image tag [${imageTag}] is invalid.');
}
- name: Parse inputs
id: parse_filters
uses: actions/github-script@v7
with:
script: |
const filters = `${{ inputs.path_filters }}`.split(',').map(f => f.trim()).filter(b => b.length > 0);
const filtersStr = "run_on:\n" + filters.map(f => ` - '${f}'`).join('\n');
core.setOutput('filters', filtersStr);
- name: Check for force push
id: force_push
uses: actions/github-script@v7
with:
# if the push was forced, use the default branch as the base -- otherwise, use the most recent commit before the push
# this is necessary because when you force push the previous commit is not available in the repo, thus no changes can be detected
script: |
if (`${{ github.event_name }}` === 'push' && ${{ github.event.forced }}) {
core.info(`Force push detected, using the repo's default branch (${{ github.event.repository.default_branch }}) as the base`)
core.setOutput('base', `${{ github.event.repository.default_branch }}`);
} else {
core.info(`Push was not forced, using the most recent commit before the push as the base`)
core.setOutput('base', `${{ inputs.path_filters_base }}`);
}
- name: Check for matching file changes
uses: dorny/paths-filter@v3
id: file_filter
with:
filters: |
${{ steps.parse_filters.outputs.filters }}
base: ${{ steps.force_push.outputs.base }}
list-files: json

- name: Check if build should run
id: final_check
uses: actions/github-script@v7
with:
script: |
const branchMatched = `${{ steps.branch_filter.outputs.match }}` === 'true';
const filesMatched = `${{ steps.file_filter.outputs.run_on }}` === 'true';
core.setOutput('should_build', filesMatched && branchMatched);
98 changes: 98 additions & 0 deletions .github/actions/argus-builder/docker-build/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: argus-docker-build
description: Build a Docker Image for Argus

inputs:
image_name:
description: 'Name of the image to build'
required: true
dockerfile:
description: 'Path to the Dockerfile'
required: true
context:
description: 'Path to the build context'
required: true
platform:
description: 'Platform to build for'
required: false
default: 'linux/arm64'
build_args:
description: 'Args for docker build'
required: false
default: ''
image_tag:
description: 'Additional tag to apply to the image this is built'
required: true
github_app_id:
description: 'GitHub App ID'
required: true
github_private_key:
description: 'GitHub App private key'
required: true

outputs:
image_uri:
description: 'URI of the image that was built'
value: ${{ steps.ecr_metadata.outputs.IMAGE_URI }}

runs:
using: composite
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
path: ${{ github.event.repository.name }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-west-2
role-to-assume: arn:aws:iam::533267185808:role/gh_actions_core_platform_infra_prod_eks
role-session-name: ArgusContainerBuilder
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ inputs.github_app_id }}
private_key: ${{ inputs.github_private_key }}
- uses: actions/checkout@v4
with:
repository: chanzuckerberg/core-platform-settings
path: core-platform-settings
token: ${{ steps.generate_token.outputs.token }}
- name: ECR Metadata
id: ecr_metadata
shell: bash
run: |
ECR_REGISTRY="533267185808.dkr.ecr.us-west-2.amazonaws.com"
echo "ECR_REGISTRY=$ECR_REGISTRY" >> $GITHUB_OUTPUT
ECR_REPO_NAME="core-platform/${{ github.event.repository.name }}/${{ inputs.image_name }}"
echo "ECR_REPO_NAME=$ECR_REPO_NAME" >> $GITHUB_OUTPUT
IMAGE_URI="$ECR_REGISTRY/$ECR_REPO_NAME:${{ inputs.image_tag }}"
echo "IMAGE_URI=$IMAGE_URI" >> $GITHUB_OUTPUT
- name: Create ECR repo if necessary
uses: int128/create-ecr-repository-action@v1
with:
repository: ${{ steps.ecr_metadata.outputs.ECR_REPO_NAME }}
lifecycle-policy: core-platform-settings/ecr/lifecycle-policy.json
repository-policy: core-platform-settings/ecr/repository-policy.json
- name: Build And Push
uses: chanzuckerberg/github-actions/.github/actions/docker-build-push@docker-build-push-v1.6.0
with:
dockerfile: ${{ github.event.repository.name }}/${{ inputs.dockerfile }}
context: ${{ github.event.repository.name }}/${{ inputs.context }}
name: ${{ steps.ecr_metadata.outputs.ECR_REPO_NAME }}
registry: ${{ steps.ecr_metadata.outputs.ECR_REGISTRY }}
custom_tag: ${{ inputs.image_tag }}
platforms: ${{ inputs.platform }}
build_args: |
IMAGE_TAG=${{ inputs.image_tag }}
${{ inputs.build_args }}
# TODO: scan image for vulnerabilities
# - name: Scan for vulnerabilities
# uses: chanzuckerberg/github-actions/.github/actions/argus-builder/scan-for-vulnerabilities@main
# with:
# image_uri: ${{ steps.ecr_metadata.outputs.ECR_REGISTRY }}/${{ steps.ecr_metadata.outputs.ECR_REPO_NAME }}:${{ inputs.image_tag }}
# github_app_id: ${{ inputs.github_app_id }}
# github_private_key: ${{ inputs.github_private_key }}
70 changes: 70 additions & 0 deletions .github/actions/argus-builder/manifest-update/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: argus-docker-manifest-update
description: Updates manifests for Argus after the Docker image is built

inputs:
envs:
description: 'Env names, comma delimited'
required: true
image_tag:
description: The tag of the image that should be updated
required: true
working_directory:
description: 'The Argus project root (parent directory that contains the .infra/ directory)'
default: '.'
required: false
github_app_id:
description: 'GitHub App ID'
required: true
github_private_key:
description: 'GitHub App private key'
required: true

runs:
using: composite
steps:
- run: |
echo "Image Tag: ${{ inputs.image_tag }}"
shell: bash
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@v2
with:
app_id: ${{ inputs.github_app_id }}
private_key: ${{ inputs.github_private_key }}
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}
- name: Parse envs
id: parse_envs
uses: actions/github-script@v7
with:
script: |
const envs = `${{ inputs.envs }}`.split(',').map(env => env.trim()).filter(b => b.length > 0);
core.setOutput('envs', envs.join(' '));
- name: Determine .infra path
uses: actions/github-script@v7
id: path
with:
script: |
const path = require('path');
const fs = require('fs');
const infraDirPath = path.join(`${{ inputs.working_directory }}`, '.infra');
if (!fs.existsSync(infraDirPath)) {
throw new Error(`.infra directory not found at ${infraDirPath}`);
}
core.setOutput('infra_dir_path', infraDirPath);
- name: Update Manifest
shell: bash
run: |
for env in ${{ steps.parse_envs.outputs.envs }}
do
sed -i 's/tag: sha-\w\+/tag: ${{ inputs.image_tag }}/g' ${{ steps.path.outputs.infra_dir_path }}/${env}/values.yaml
cat ${{ steps.path.outputs.infra_dir_path }}/${env}/values.yaml
done
- name: Update Argus manifests
uses: EndBug/add-and-commit@v9
with:
add: -A
message: 'chore: Updated [${{ steps.parse_envs.outputs.envs }}] values.yaml image tags to ${{ inputs.image_tag }}'

Loading

0 comments on commit d9d1014

Please sign in to comment.