forked from salesforce/dr-cla
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
87d7edc
commit 6e4db86
Showing
4 changed files
with
282 additions
and
1 deletion.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
name: 'Build Containers with Nixpacks and' | ||
description: 'Build a docker container with nixpacks on GitHub CI and push to a registry' | ||
author: 'Michael Bianco' | ||
branding: | ||
icon: 'box' | ||
color: 'green' | ||
|
||
inputs: | ||
tags: | ||
description: "List of tags to apply to the built image" | ||
required: false | ||
labels: | ||
description: "List of metadata for an image" | ||
required: false | ||
platforms: | ||
description: "List of target platforms for the build" | ||
required: false | ||
context: | ||
description: "Build's context is the set of files located in the specified PATH" | ||
required: true | ||
default: "." | ||
pkgs: | ||
description: "Provide additional nix packages to install in the environment" | ||
required: false | ||
apt: | ||
description: "Provide additional apt packages to install in the environment" | ||
required: false | ||
env: | ||
description: "Provide additional environment variables to set" | ||
required: false | ||
push: | ||
description: "Boolean to determine if the built image should be pushed" | ||
required: false | ||
default: 'false' | ||
cache: | ||
description: "Boolean to determine if the build should use the cache" | ||
required: false | ||
default: 'false' | ||
cache_tag: | ||
description: "Image to use for the cache image. Defaults to `ghcr.io/org/app:latest` where org/app is the repository the workflow runs into. (NOTE: Provide a lowercase name, even if the repo or tbe org contain uppercase characters)" | ||
required: false | ||
|
||
runs: | ||
using: 'composite' | ||
steps: | ||
# TODO can we remove this? | ||
- name: Set GitHub Path | ||
run: echo "$GITHUB_ACTION_PATH" >> $GITHUB_PATH | ||
shell: bash | ||
env: | ||
GITHUB_ACTION_PATH: ${{ github.action_path }} | ||
|
||
# TODO should detect if qemu is needed based on current vs target platform | ||
- uses: docker/setup-qemu-action@v3 | ||
if: inputs.platforms != '' | ||
|
||
- run: entrypoint.sh | ||
shell: bash | ||
env: | ||
INPUT_TAGS: ${{ inputs.tags }} | ||
INPUT_LABELS: ${{ inputs.labels }} | ||
INPUT_PLATFORMS: ${{ inputs.platforms }} | ||
INPUT_CONTEXT: ${{ inputs.context }} | ||
INPUT_PKGS: ${{ inputs.pkgs }} | ||
INPUT_APT: ${{ inputs.apt }} | ||
INPUT_PUSH: ${{ inputs.push }} | ||
INPUT_ENV: ${{ inputs.env }} | ||
INPUT_CACHE: ${{ inputs.cache }} | ||
INPUT_CACHE_TAG: ${{ inputs.cache_tag }} | ||
|
||
# raw SHA of the commit which triggered the workflow | ||
GIT_SHA: ${{ github.sha }} | ||
# username/repo | ||
GITHUB_REPOSITORY: ${{ github.repository }} | ||
# github.com/username/repo | ||
GITHUB_REPOSITORY_URL: ${{ github.server_url }}/${{ github.repository }} | ||
# for author extraction | ||
GH_TOKEN: ${{ github.token }} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
#!/bin/bash | ||
|
||
set -e | ||
|
||
if ! command -v nixpacks &>/dev/null; then | ||
echo "Installing Nixpacks..." | ||
curl -sSL https://nixpacks.com/install.sh | bash | ||
fi | ||
|
||
repository_author() { | ||
local repo=$1 | ||
local owner_login owner_name owner_email owner_info | ||
|
||
if [ -z "$repo" ]; then | ||
echo "Error: Repository not specified." | ||
return 1 | ||
fi | ||
|
||
# Fetch the owner's login (username) | ||
owner_login=$(gh repo view "$repo" --json owner --jq '.owner.login' | tr -d '[:space:]') | ||
|
||
# Fetch the owner's name, remove trailing and leading whitespace | ||
owner_name=$(gh api "users/$owner_login" --jq '.name' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') | ||
|
||
# Attempt to fetch the owner's publicly available email | ||
owner_email=$(gh api "users/$owner_login" --jq '.email' | tr -d '[:space:]') | ||
|
||
# Check if an email was fetched; if not, use just the name | ||
if [ -z "$owner_email" ] || [ "$owner_email" = "null" ]; then | ||
owner_info="$owner_name" | ||
else | ||
owner_info="$owner_name <$owner_email>" | ||
fi | ||
|
||
echo "$owner_info" | ||
} | ||
|
||
repository_license() { | ||
local repo=$1 | ||
gh api /repos/iloveitaly/dotfiles/license 2>/dev/null | jq -r '.license.key // ""' | ||
} | ||
|
||
BUILD_CMD="nixpacks build $INPUT_CONTEXT" | ||
GHCR_IMAGE_NAME=$(echo "ghcr.io/$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]') | ||
|
||
# add NIXPACKS_ prefixed environment variables to the build command | ||
# https://nixpacks.com/docs/configuration/environment | ||
for var in $(env | grep ^NIXPACKS_); do | ||
BUILD_CMD="$BUILD_CMD --env $var" | ||
done | ||
|
||
# Incorporate provided input parameters from actions.yml into the Nixpacks build command | ||
if [ -n "${INPUT_TAGS}" ]; then | ||
read -ra TAGS <<<"$(echo "$INPUT_TAGS" | tr ',\n' ' ')" | ||
else | ||
# if not tags are provided, assume ghcr.io as the default registry | ||
echo "No tags provided. Defaulting to ghcr.io registry." | ||
BUILD_DATE_TIMESTAMP=$(date +%s) | ||
TAGS=("$GHCR_IMAGE_NAME:$GIT_SHA" "$GHCR_IMAGE_NAME:latest" "$GHCR_IMAGE_NAME:$BUILD_DATE_TIMESTAMP") | ||
fi | ||
|
||
if [ -n "${INPUT_LABELS}" ]; then | ||
read -ra LABELS <<<"$(echo "$INPUT_LABELS" | tr ',\n' ' ')" | ||
fi | ||
|
||
if [[ "$INPUT_CACHE" == "true" ]]; then | ||
if [ -z "$INPUT_CACHE_TAG" ]; then | ||
INPUT_CACHE_TAG=$(echo "$GHCR_IMAGE_NAME" | tr '[:upper:]' '[:lower:]') | ||
fi | ||
BUILD_CMD="$BUILD_CMD --inline-cache --cache-from $INPUT_CACHE_TAG" | ||
fi | ||
|
||
# TODO should check if these labels are already defined | ||
LABELS+=("org.opencontainers.image.source=$GITHUB_REPOSITORY_URL") | ||
LABELS+=("org.opencontainers.image.revision=$GITHUB_SHA") | ||
LABELS+=("org.opencontainers.image.created=\"$(date -u +"%Y-%m-%dT%H:%M:%SZ")\"") | ||
|
||
REPO_AUTHOR=$(repository_author "$GITHUB_REPOSITORY") | ||
if [ -n "$REPO_AUTHOR" ]; then | ||
LABELS+=("org.opencontainers.image.authors=\"$REPO_AUTHOR\"") | ||
fi | ||
|
||
REPO_LICENSE=$(repository_license "$GITHUB_REPOSITORY") | ||
if [ -n "$REPO_LICENSE" ]; then | ||
LABELS+=("org.opencontainers.image.licenses=\"$REPO_LICENSE\"") | ||
fi | ||
|
||
# TODO add the description label as well? Does this add any value? | ||
# TODO add org.opencontainers.image.title? | ||
|
||
for label in "${LABELS[@]}"; do | ||
BUILD_CMD="$BUILD_CMD --label $label" | ||
done | ||
|
||
if [ -n "${INPUT_PKGS}" ]; then | ||
read -ra PKGS_ARR <<<"$(echo "$INPUT_PKGS" | tr ',\n' ' ')" | ||
BUILD_CMD="$BUILD_CMD --pkgs '${PKGS_ARR[*]}'" | ||
fi | ||
|
||
if [ -n "${INPUT_APT}" ]; then | ||
read -ra APT_ARR <<<"$(echo "$INPUT_APT" | tr ',\n' ' ')" | ||
BUILD_CMD="$BUILD_CMD --apt '${APT_ARR[*]}'" | ||
fi | ||
|
||
# Include environment variables in the build command | ||
if [ -n "${INPUT_ENV}" ]; then | ||
IFS=',' read -ra ENVS <<<"$INPUT_ENV" | ||
for env_var in "${ENVS[@]}"; do | ||
BUILD_CMD="$BUILD_CMD --env $env_var" | ||
done | ||
fi | ||
|
||
if [ -n "${INPUT_PLATFORMS}" ]; then | ||
read -ra PLATFORMS <<<"$(echo "$INPUT_PLATFORMS" | tr ',\n' ' ')" | ||
fi | ||
|
||
if [ "${#PLATFORMS[@]}" -gt 1 ] && [ "$INPUT_PUSH" != "true" ]; then | ||
echo "Multi-platform builds *must* be pushed to a registry. Please set 'push: true' in your action configuration or do a single architecture build." | ||
exit 1 | ||
fi | ||
|
||
function build_and_push() { | ||
local build_cmd=$BUILD_CMD | ||
|
||
if [ -n "$PLATFORMS" ]; then | ||
build_cmd="$build_cmd --platform $PLATFORMS" | ||
fi | ||
|
||
for tag in "${TAGS[@]}"; do | ||
build_cmd="$build_cmd --tag $tag" | ||
done | ||
|
||
echo "Executing Nixpacks build command:" | ||
echo "$build_cmd" | ||
|
||
eval "$build_cmd" | ||
|
||
# Conditionally push the images based on the 'push' input | ||
if [[ "$INPUT_PUSH" == "true" ]]; then | ||
for tag in "${TAGS[@]}"; do | ||
echo "Pushing Docker image: $tag" | ||
docker push "$tag" | ||
done | ||
else | ||
echo "Skipping Docker image push." | ||
fi | ||
} | ||
|
||
function build_and_push_multiple_architectures() { | ||
echo "Building for multiple architectures: ${PLATFORMS[*]}" | ||
|
||
local manifest_list=() | ||
|
||
for platform in "${PLATFORMS[@]}"; do | ||
local build_cmd=$BUILD_CMD | ||
# Replace '/' with '-' | ||
local normalized_platform=${platform//\//-} | ||
local architecture_image_name=${GHCR_IMAGE_NAME}:$normalized_platform | ||
|
||
build_cmd="$build_cmd --platform $platform" | ||
build_cmd="$build_cmd --tag $architecture_image_name" | ||
|
||
echo "Executing Nixpacks build command for $platform:" | ||
echo "$build_cmd" | ||
|
||
eval "$build_cmd" | ||
|
||
manifest_list+=("$architecture_image_name") | ||
done | ||
|
||
echo "All architectures built. Pushing images..." | ||
for architecture_image_name in "${manifest_list[@]}"; do | ||
# if we don't push the images the multi-architecture manifest will not be created | ||
# best practice here seems to be to push `base:platform` images to the registry | ||
# when they are overwritten by the next architecture build, the previous manifest | ||
# will reference the sha of the image instead of the tag | ||
docker push "$architecture_image_name" | ||
done | ||
|
||
echo "Constructing manifest and pushing to registry..." | ||
|
||
# now, with all architectures built locally, we can construct a manifest and push to the registry | ||
for tag in "${TAGS[@]}"; do | ||
local manifest_creation="docker manifest create $tag ${manifest_list[@]}" | ||
echo "Creating manifest: $manifest_creation" | ||
eval "$manifest_creation" | ||
|
||
docker manifest push "$tag" | ||
done | ||
} | ||
|
||
if [ "${#PLATFORMS[@]}" -gt 1 ]; then | ||
build_and_push_multiple_architectures | ||
else | ||
build_and_push | ||
fi | ||
|
||
echo "Nixpacks Build & Push completed successfully." |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"description": "Build and push images with nixpacks", | ||
"homepage": "https://github.com/iloveitaly/github-action-nixpacks", | ||
"keywords": ["nixpacks", "docker"] | ||
} |
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