Compare commits
37 Commits
c22cb9a8b0
...
v1.7
| Author | SHA1 | Date | |
|---|---|---|---|
| 01a1e24a7b | |||
| 59625a53b2 | |||
| afb5bf2bc8 | |||
| a5e252b3b1 | |||
| 011bc1c113 | |||
| e94e04cff3 | |||
| 7066e237db | |||
| 1410f4ff43 | |||
| ebccfaa6fc | |||
| 0cc1e2fb62 | |||
| a347242a24 | |||
|
|
583ae62c3b | ||
|
|
c5563d9dc1 | ||
|
|
4ebffa8c94 | ||
|
|
4f00812999 | ||
|
|
91ab1aaac0 | ||
|
|
65e2336237 | ||
|
|
4170b17ea6 | ||
|
|
133731d493 | ||
|
|
bfd6822394 | ||
|
|
012da683f6 | ||
|
|
50e0f562a3 | ||
|
|
cbb6163cc2 | ||
|
|
982f09e161 | ||
|
|
757fc65c40 | ||
|
|
6bf910dff1 | ||
|
|
c001f91b5e | ||
|
|
74bd39fcd3 | ||
|
|
f481260d7e | ||
|
|
bb980c9263 | ||
|
|
7bfe90d49b | ||
|
|
c3aa42a612 | ||
|
|
81a34e348c | ||
|
|
1440e4796c | ||
|
|
a8c9119010 | ||
|
|
1053d1271c | ||
|
|
0c21f9b27b |
@@ -1,114 +0,0 @@
|
||||
name: Docker Build, Scan & Publish
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
images:
|
||||
description: >
|
||||
JSON array of images to build.
|
||||
Each item: { name, context, dockerfile, target, cache_ref }
|
||||
required: true
|
||||
type: string
|
||||
|
||||
registry_host:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
default_branch:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
build_args:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
trivy_severity:
|
||||
required: false
|
||||
type: string
|
||||
default: "CRITICAL"
|
||||
|
||||
secrets:
|
||||
registry_user:
|
||||
required: true
|
||||
registry_password:
|
||||
required: true
|
||||
ci_token:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Determine branch context
|
||||
id: branch
|
||||
run: |
|
||||
if [ "${{ github.ref_name }}" = "${{ inputs.default_branch }}" ]; then
|
||||
echo "is_default=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "is_default=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ inputs.registry_host }}
|
||||
username: ${{ secrets.registry_user }}
|
||||
password: ${{ secrets.registry_password }}
|
||||
|
||||
- name: Build, scan and push images
|
||||
env:
|
||||
IMAGES: ${{ inputs.images }}
|
||||
BUILD_ARGS: ${{ inputs.build_args }}
|
||||
CI_TOKEN: ${{ secrets.ci_token }}
|
||||
TRIVY_SEVERITY: ${{ inputs.trivy_severity }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
echo "$IMAGES" | jq -c '.[]' | while read -r img; do
|
||||
NAME=$(echo "$img" | jq -r '.name')
|
||||
CONTEXT=$(echo "$img" | jq -r '.context')
|
||||
DOCKERFILE=$(echo "$img" | jq -r '.dockerfile')
|
||||
TARGET=$(echo "$img" | jq -r '.target')
|
||||
CACHE_REF=$(echo "$img" | jq -r '.cache_ref')
|
||||
|
||||
echo "==== Building $NAME ===="
|
||||
|
||||
TAGS=()
|
||||
TAGS+=("$NAME:${{ github.ref_name }}")
|
||||
TAGS+=("$NAME:${{ github.sha }}")
|
||||
|
||||
if [ "${{ steps.branch.outputs.is_default }}" = "true" ]; then
|
||||
TAGS+=("$NAME:latest")
|
||||
fi
|
||||
|
||||
TAG_ARGS=$(printf -- "--tag %s " "${TAGS[@]}")
|
||||
|
||||
docker buildx build \
|
||||
--file "$DOCKERFILE" \
|
||||
--target "$TARGET" \
|
||||
--cache-from "type=registry,ref=$CACHE_REF" \
|
||||
--cache-to "type=registry,ref=$CACHE_REF,mode=max" \
|
||||
--load \
|
||||
$TAG_ARGS \
|
||||
$(printf -- "--build-arg %s " $BUILD_ARGS) \
|
||||
"$CONTEXT"
|
||||
|
||||
echo "==== Trivy scan for $NAME ===="
|
||||
trivy image \
|
||||
--severity "$TRIVY_SEVERITY" \
|
||||
--exit-code 1 \
|
||||
"${TAGS[0]}"
|
||||
|
||||
echo "==== Pushing $NAME ===="
|
||||
for tag in "${TAGS[@]}"; do
|
||||
docker push "$tag"
|
||||
done
|
||||
done
|
||||
428
.github/workflows/docker-build-publish.yml
vendored
Normal file
428
.github/workflows/docker-build-publish.yml
vendored
Normal file
@@ -0,0 +1,428 @@
|
||||
name: Docker Build, Scan & Publish
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
images:
|
||||
description: >
|
||||
JSON array of images to build.
|
||||
Each item: { name, context, dockerfile, target, cache_ref }
|
||||
required: true
|
||||
type: string
|
||||
|
||||
checkout_repo:
|
||||
description: >
|
||||
Optional external repo to checkout (owner/name). If set, repo is checked out
|
||||
into checkout_path.
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
checkout_ref:
|
||||
description: >
|
||||
Optional git ref (branch/tag/SHA) for checkout_repo. Defaults to repo default branch.
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
checkout_path:
|
||||
description: >
|
||||
Path to checkout checkout_repo into.
|
||||
required: false
|
||||
type: string
|
||||
default: "external-src"
|
||||
|
||||
checkout_fetch_depth:
|
||||
description: >
|
||||
Fetch depth for checkout_repo (0 = full history).
|
||||
required: false
|
||||
type: string
|
||||
default: "0"
|
||||
|
||||
registry_host:
|
||||
required: true
|
||||
type: string
|
||||
|
||||
build_args:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
description: >
|
||||
Multiline build args, one per line: KEY=VALUE (values may include spaces)
|
||||
|
||||
extra_tags:
|
||||
description: >
|
||||
Optional extra tags to apply to each image.
|
||||
Accepts JSON array (e.g. ["latest", "main"]) or newline-separated list.
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
skip_if_tag_exists:
|
||||
description: >
|
||||
If true, skip build when all images already have sha-<commit> tags.
|
||||
required: false
|
||||
type: string
|
||||
default: "false"
|
||||
|
||||
env:
|
||||
description: >
|
||||
Multiline env vars, one per line: KEY=VALUE
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
trivy_severity:
|
||||
required: false
|
||||
type: string
|
||||
default: "CRITICAL"
|
||||
|
||||
secrets:
|
||||
registry_user:
|
||||
required: true
|
||||
registry_password:
|
||||
required: true
|
||||
ci_token:
|
||||
required: true
|
||||
ssh_private_key:
|
||||
required: false
|
||||
ssh_known_hosts:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Checkout external repository
|
||||
if: ${{ inputs.checkout_repo != '' }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ inputs.checkout_repo }}
|
||||
ref: ${{ inputs.checkout_ref }}
|
||||
server-url: ${{ github.server_url }}
|
||||
token: ${{ secrets.ci_token }}
|
||||
path: ${{ inputs.checkout_path }}
|
||||
fetch-depth: ${{ inputs.checkout_fetch_depth }}
|
||||
|
||||
- name: Load env vars
|
||||
if: ${{ inputs.env != '' }}
|
||||
run: |
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in \#*) continue;; esac
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Invalid env line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done <<< "${{ inputs.env }}"
|
||||
|
||||
- name: Start ssh-agent
|
||||
if: ${{ secrets.ssh_private_key != '' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ssh_private_key }}
|
||||
|
||||
- name: Add SSH known hosts
|
||||
if: ${{ secrets.ssh_known_hosts != '' }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Install Trivy
|
||||
run: |
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh -s -- -b /usr/local/bin
|
||||
trivy --version
|
||||
|
||||
- name: Login to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ inputs.registry_host }}
|
||||
username: ${{ secrets.registry_user }}
|
||||
password: ${{ secrets.registry_password }}
|
||||
|
||||
- name: Build, scan and push images
|
||||
env:
|
||||
IMAGES: ${{ inputs.images }}
|
||||
BUILD_ARGS: ${{ inputs.build_args }}
|
||||
EXTRA_TAGS_INPUT: ${{ inputs.extra_tags }}
|
||||
CHECKOUT_REPO: ${{ inputs.checkout_repo }}
|
||||
CHECKOUT_PATH: ${{ inputs.checkout_path }}
|
||||
SKIP_IF_TAG_EXISTS: ${{ inputs.skip_if_tag_exists }}
|
||||
CI_TOKEN: ${{ secrets.ci_token }}
|
||||
TRIVY_SEVERITY: ${{ inputs.trivy_severity }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if ! echo "$IMAGES" | jq -e . >/dev/null; then
|
||||
echo "inputs.images must be valid JSON" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! echo "$IMAGES" | jq -e 'type == "array"' >/dev/null; then
|
||||
echo "inputs.images must be a JSON array" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SSH_FLAGS=()
|
||||
if [ -n "${SSH_AUTH_SOCK:-}" ]; then
|
||||
SSH_FLAGS+=(--ssh default)
|
||||
fi
|
||||
SSH_BAKE_JSON="null"
|
||||
if [ -n "${SSH_AUTH_SOCK:-}" ]; then
|
||||
SSH_BAKE_JSON='["default"]'
|
||||
fi
|
||||
BAKE_ALLOW_FLAGS=()
|
||||
if [ -n "${SSH_AUTH_SOCK:-}" ]; then
|
||||
BAKE_ALLOW_FLAGS+=(--allow=ssh)
|
||||
fi
|
||||
|
||||
RAW_REF="${{ github.ref }}"
|
||||
SHA_FULL="${{ github.sha }}"
|
||||
SOURCE_SHA_FULL="$SHA_FULL"
|
||||
if [ -n "$CHECKOUT_REPO" ] && [ -d "$CHECKOUT_PATH/.git" ]; then
|
||||
SOURCE_SHA_FULL=$(git -C "$CHECKOUT_PATH" rev-parse HEAD)
|
||||
fi
|
||||
SHA_FULL="$SOURCE_SHA_FULL"
|
||||
SHA_SHORT="${SHA_FULL:0:12}"
|
||||
|
||||
truthy() {
|
||||
case "${1,,}" in
|
||||
1|true|yes|y|on) return 0;;
|
||||
*) return 1;;
|
||||
esac
|
||||
}
|
||||
|
||||
if truthy "$SKIP_IF_TAG_EXISTS"; then
|
||||
missing=0
|
||||
while read -r img; do
|
||||
IMAGE_NAME=$(echo "$img" | jq -r '.name')
|
||||
FULL_IMAGE="${{ inputs.registry_host }}/$IMAGE_NAME"
|
||||
if docker manifest inspect "$FULL_IMAGE:sha-$SHA_SHORT" >/dev/null 2>&1; then
|
||||
echo "==== Found existing tag $FULL_IMAGE:sha-$SHA_SHORT ===="
|
||||
else
|
||||
echo "==== Missing tag $FULL_IMAGE:sha-$SHA_SHORT (will build) ===="
|
||||
missing=1
|
||||
fi
|
||||
done < <(echo "$IMAGES" | jq -c '.[]')
|
||||
if [ "$missing" -eq 0 ]; then
|
||||
echo "All images already have sha-$SHA_SHORT tag. Skipping build."
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
VERSION_TAGS=()
|
||||
if [[ "$RAW_REF" =~ ^refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
VERSION_TAGS+=("v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}")
|
||||
VERSION_TAGS+=("v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}")
|
||||
VERSION_TAGS+=("v${BASH_REMATCH[1]}")
|
||||
VERSION_TAGS+=("latest")
|
||||
fi
|
||||
|
||||
EXTRA_TAGS=()
|
||||
if [ -n "$EXTRA_TAGS_INPUT" ]; then
|
||||
if echo "$EXTRA_TAGS_INPUT" | jq -e . >/dev/null 2>&1; then
|
||||
if ! echo "$EXTRA_TAGS_INPUT" | jq -e 'type == "array"' >/dev/null 2>&1; then
|
||||
echo "inputs.extra_tags must be a JSON array" >&2
|
||||
exit 1
|
||||
fi
|
||||
mapfile -t EXTRA_TAGS < <(echo "$EXTRA_TAGS_INPUT" | jq -r '.[]')
|
||||
else
|
||||
while IFS= read -r line; do
|
||||
trimmed="${line#"${line%%[![:space:]]*}"}"
|
||||
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
|
||||
[ -z "$trimmed" ] && continue
|
||||
case "$trimmed" in \#*) continue;; esac
|
||||
EXTRA_TAGS+=("$trimmed")
|
||||
done <<< "$EXTRA_TAGS_INPUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
BUILD_ARG_FLAGS=()
|
||||
BUILD_ARGS_JSON="{}"
|
||||
if [ -n "$BUILD_ARGS" ]; then
|
||||
while IFS= read -r line; do
|
||||
trimmed="${line#"${line%%[![:space:]]*}"}"
|
||||
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
|
||||
[ -z "$trimmed" ] && continue
|
||||
case "$trimmed" in \#*) continue;; esac
|
||||
if [[ "$trimmed" != *=* ]]; then
|
||||
echo "Invalid build arg: $trimmed" >&2
|
||||
exit 1
|
||||
fi
|
||||
BUILD_ARG_FLAGS+=(--build-arg "$trimmed")
|
||||
|
||||
key="${trimmed%%=*}"
|
||||
val="${trimmed#*=}"
|
||||
BUILD_ARGS_JSON=$(jq --arg k "$key" --arg v "$val" '. + {($k): $v}' <<<"$BUILD_ARGS_JSON")
|
||||
done <<< "$BUILD_ARGS"
|
||||
fi
|
||||
|
||||
normalize_path() {
|
||||
local p="$1"
|
||||
while [[ "$p" == ./* ]]; do
|
||||
p="${p#./}"
|
||||
done
|
||||
if [ "$p" != "/" ]; then
|
||||
p="${p%/}"
|
||||
fi
|
||||
printf '%s' "$p"
|
||||
}
|
||||
|
||||
while read -r group; do
|
||||
GROUP_COUNT=$(echo "$group" | jq 'length')
|
||||
CONTEXT=$(echo "$group" | jq -r '.[0].context')
|
||||
DOCKERFILE=$(echo "$group" | jq -r '.[0].dockerfile')
|
||||
context_norm="$(normalize_path "$CONTEXT")"
|
||||
dockerfile_norm="$(normalize_path "$DOCKERFILE")"
|
||||
DOCKERFILE_FOR_BAKE="$DOCKERFILE"
|
||||
DOCKERFILE_FOR_BUILD="$DOCKERFILE"
|
||||
if [ -n "$context_norm" ] && [ "$context_norm" != "." ]; then
|
||||
if [[ "$dockerfile_norm" == "$context_norm/"* ]]; then
|
||||
DOCKERFILE_FOR_BAKE="${dockerfile_norm#$context_norm/}"
|
||||
DOCKERFILE_FOR_BUILD="$dockerfile_norm"
|
||||
elif [[ "$dockerfile_norm" != /* ]]; then
|
||||
DOCKERFILE_FOR_BUILD="${context_norm}/${dockerfile_norm}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$GROUP_COUNT" -gt 1 ]; then
|
||||
echo "==== Building $GROUP_COUNT images from $DOCKERFILE (bake) ===="
|
||||
|
||||
CACHE_REF_SUFFIXES=$(echo "$group" | jq -c 'map(.cache_ref // empty) | map(select(length > 0)) | unique')
|
||||
CACHE_REF_SUFFIX=$(echo "$CACHE_REF_SUFFIXES" | jq -r '.[0] // empty')
|
||||
UNIQUE_CACHE_COUNT=$(echo "$CACHE_REF_SUFFIXES" | jq -r 'length')
|
||||
if [ "$UNIQUE_CACHE_COUNT" -gt 1 ]; then
|
||||
echo "Warning: multiple cache_ref values for the same dockerfile. Using $CACHE_REF_SUFFIX." >&2
|
||||
fi
|
||||
|
||||
CACHE_REF=""
|
||||
if [ -n "$CACHE_REF_SUFFIX" ]; then
|
||||
CACHE_REF="${{ inputs.registry_host }}/$CACHE_REF_SUFFIX"
|
||||
fi
|
||||
|
||||
TARGETS_JSON="{}"
|
||||
TARGET_NAMES=()
|
||||
|
||||
while read -r entry; do
|
||||
IDX=$(echo "$entry" | jq -r '.key')
|
||||
IMG=$(echo "$entry" | jq -c '.value')
|
||||
|
||||
IMAGE_NAME=$(echo "$IMG" | jq -r '.name')
|
||||
TARGET=$(echo "$IMG" | jq -r '.target // empty')
|
||||
FULL_IMAGE="${{ inputs.registry_host }}/${IMAGE_NAME}"
|
||||
|
||||
TAGS=()
|
||||
TAGS+=("$FULL_IMAGE:sha-$SHA_SHORT")
|
||||
for ver in "${VERSION_TAGS[@]}"; do
|
||||
TAGS+=("$FULL_IMAGE:$ver")
|
||||
done
|
||||
for extra in "${EXTRA_TAGS[@]}"; do
|
||||
TAGS+=("$FULL_IMAGE:$extra")
|
||||
done
|
||||
TAGS_JSON=$(printf '%s\n' "${TAGS[@]}" | jq -R . | jq -s .)
|
||||
|
||||
TARGET_NAME="img_${IDX}"
|
||||
TARGET_NAMES+=("$TARGET_NAME")
|
||||
|
||||
TARGET_OBJ=$(jq -n \
|
||||
--arg context "$CONTEXT" \
|
||||
--arg dockerfile "$DOCKERFILE_FOR_BAKE" \
|
||||
--arg target "$TARGET" \
|
||||
--argjson tags "$TAGS_JSON" \
|
||||
--argjson args "$BUILD_ARGS_JSON" \
|
||||
--argjson ssh "$SSH_BAKE_JSON" \
|
||||
--arg cache_ref "$CACHE_REF" \
|
||||
'{
|
||||
context: $context,
|
||||
dockerfile: $dockerfile,
|
||||
tags: $tags,
|
||||
args: $args
|
||||
}
|
||||
+ (if ($ssh != null) then {ssh: $ssh} else {} end)
|
||||
+ (if ($target != "" and $target != "null") then {target: $target} else {} end)
|
||||
+ (if ($cache_ref != "") then {"cache-from": ["type=registry,ref=" + $cache_ref], "cache-to": ["type=registry,ref=" + $cache_ref + ",mode=max"]} else {} end)')
|
||||
|
||||
TARGETS_JSON=$(jq -n --arg name "$TARGET_NAME" --argjson target "$TARGET_OBJ" --argjson targets "$TARGETS_JSON" '$targets + {($name): $target}')
|
||||
done < <(echo "$group" | jq -c 'to_entries[]')
|
||||
|
||||
GROUP_TARGETS_JSON=$(printf '%s\n' "${TARGET_NAMES[@]}" | jq -R . | jq -s .)
|
||||
BAKE_JSON=$(jq -n --argjson targets "$TARGETS_JSON" --argjson group_targets "$GROUP_TARGETS_JSON" '{target:$targets, group:{default:{targets:$group_targets}}}')
|
||||
BAKE_FILE=$(mktemp)
|
||||
echo "$BAKE_JSON" > "$BAKE_FILE"
|
||||
|
||||
docker buildx bake --file "$BAKE_FILE" --push "${BAKE_ALLOW_FLAGS[@]}"
|
||||
rm -f "$BAKE_FILE"
|
||||
|
||||
while read -r img; do
|
||||
IMAGE_NAME=$(echo "$img" | jq -r '.name')
|
||||
FULL_IMAGE="${{ inputs.registry_host }}/${IMAGE_NAME}"
|
||||
|
||||
echo "==== Trivy scan for $FULL_IMAGE ===="
|
||||
trivy image \
|
||||
--severity "$TRIVY_SEVERITY" \
|
||||
--exit-code 1 \
|
||||
"$FULL_IMAGE:sha-$SHA_SHORT"
|
||||
done < <(echo "$group" | jq -c '.[]')
|
||||
else
|
||||
img=$(echo "$group" | jq -c '.[0]')
|
||||
|
||||
IMAGE_NAME=$(echo "$img" | jq -r '.name')
|
||||
FULL_IMAGE="${{ inputs.registry_host }}/${IMAGE_NAME}"
|
||||
CACHE_REF_SUFFIX=$(echo "$img" | jq -r '.cache_ref // empty')
|
||||
CONTEXT=$(echo "$img" | jq -r '.context')
|
||||
DOCKERFILE=$(echo "$img" | jq -r '.dockerfile')
|
||||
TARGET=$(echo "$img" | jq -r '.target // empty')
|
||||
|
||||
TAGS=()
|
||||
TAGS+=("$FULL_IMAGE:sha-$SHA_SHORT")
|
||||
for ver in "${VERSION_TAGS[@]}"; do
|
||||
TAGS+=("$FULL_IMAGE:$ver")
|
||||
done
|
||||
for extra in "${EXTRA_TAGS[@]}"; do
|
||||
TAGS+=("$FULL_IMAGE:$extra")
|
||||
done
|
||||
|
||||
TAG_ARGS=()
|
||||
for tag in "${TAGS[@]}"; do
|
||||
TAG_ARGS+=(--tag "$tag")
|
||||
done
|
||||
|
||||
TARGET_FLAGS=()
|
||||
if [ -n "$TARGET" ] && [ "$TARGET" != "null" ]; then
|
||||
TARGET_FLAGS+=(--target "$TARGET")
|
||||
fi
|
||||
|
||||
CACHE_FLAGS=()
|
||||
if [ -n "$CACHE_REF_SUFFIX" ]; then
|
||||
CACHE_REF="${{ inputs.registry_host }}/${CACHE_REF_SUFFIX}"
|
||||
CACHE_FLAGS+=(--cache-from "type=registry,ref=$CACHE_REF")
|
||||
CACHE_FLAGS+=(--cache-to "type=registry,ref=$CACHE_REF,mode=max")
|
||||
fi
|
||||
|
||||
echo "==== Building $FULL_IMAGE ===="
|
||||
|
||||
docker buildx build \
|
||||
--file "$DOCKERFILE_FOR_BUILD" \
|
||||
"${TARGET_FLAGS[@]}" \
|
||||
"${CACHE_FLAGS[@]}" \
|
||||
"${SSH_FLAGS[@]}" \
|
||||
--push \
|
||||
"${TAG_ARGS[@]}" \
|
||||
"${BUILD_ARG_FLAGS[@]}" \
|
||||
"$CONTEXT"
|
||||
|
||||
echo "==== Trivy scan for $FULL_IMAGE ===="
|
||||
trivy image \
|
||||
--severity "$TRIVY_SEVERITY" \
|
||||
--exit-code 1 \
|
||||
"${TAGS[0]}"
|
||||
fi
|
||||
done < <(echo "$IMAGES" | jq -c 'sort_by(.context, .dockerfile) | group_by([.context, .dockerfile])[]')
|
||||
135
.github/workflows/node-ci.yml
vendored
Normal file
135
.github/workflows/node-ci.yml
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
name: Node CI
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
node_version:
|
||||
type: string
|
||||
default: "20"
|
||||
pnpm_version:
|
||||
type: string
|
||||
default: "10.23.0"
|
||||
working_directory:
|
||||
type: string
|
||||
default: "."
|
||||
cache_dependency_path:
|
||||
type: string
|
||||
default: "pnpm-lock.yaml"
|
||||
env:
|
||||
description: >
|
||||
Multiline env vars, one per line: KEY=VALUE
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
install_command:
|
||||
type: string
|
||||
default: "pnpm install --frozen-lockfile"
|
||||
format_command:
|
||||
type: string
|
||||
default: "pnpm format:check"
|
||||
lint_command:
|
||||
type: string
|
||||
default: "pnpm lint"
|
||||
typecheck_command:
|
||||
type: string
|
||||
default: "pnpm typecheck"
|
||||
test_command:
|
||||
type: string
|
||||
default: "pnpm test"
|
||||
build_command:
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
ssh_private_key:
|
||||
required: false
|
||||
ssh_known_hosts:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load env vars
|
||||
if: ${{ inputs.env != '' }}
|
||||
run: |
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in \#*) continue;; esac
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Invalid env line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done <<< "${{ inputs.env }}"
|
||||
|
||||
- name: Start ssh-agent
|
||||
if: ${{ secrets.ssh_private_key != '' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ssh_private_key }}
|
||||
|
||||
- name: Add SSH known hosts
|
||||
if: ${{ secrets.ssh_known_hosts != '' }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Set up pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: ${{ inputs.pnpm_version }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ inputs.node_version }}
|
||||
|
||||
- name: Get pnpm store path
|
||||
id: pnpm-store
|
||||
run: echo "store_path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
|
||||
- name: Cache pnpm store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.pnpm-store.outputs.store_path }}
|
||||
key: ${{ runner.os }}-pnpm-${{ inputs.pnpm_version }}-${{ hashFiles(inputs.cache_dependency_path) }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-${{ inputs.pnpm_version }}-
|
||||
${{ runner.os }}-pnpm-
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.install_command != '' }}
|
||||
run: ${{ inputs.install_command }}
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
|
||||
- name: Run format check
|
||||
if: ${{ inputs.format_command != '' }}
|
||||
run: ${{ inputs.format_command }}
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
|
||||
- name: Run lint
|
||||
if: ${{ inputs.lint_command != '' }}
|
||||
run: ${{ inputs.lint_command }}
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
|
||||
- name: Run typecheck
|
||||
if: ${{ inputs.typecheck_command != '' }}
|
||||
run: ${{ inputs.typecheck_command }}
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ inputs.test_command != '' }}
|
||||
run: ${{ inputs.test_command }}
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
|
||||
- name: Run build
|
||||
if: ${{ inputs.build_command != '' }}
|
||||
run: ${{ inputs.build_command }}
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
97
.github/workflows/pre-commit.yml
vendored
Normal file
97
.github/workflows/pre-commit.yml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
name: Pre-commit
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
type: string
|
||||
default: "3.13"
|
||||
pre_commit_version:
|
||||
type: string
|
||||
default: "latest"
|
||||
working_directory:
|
||||
type: string
|
||||
default: "."
|
||||
cache_dependency_path:
|
||||
type: string
|
||||
default: ".pre-commit-config.yaml"
|
||||
env:
|
||||
description: >
|
||||
Multiline env vars, one per line: KEY=VALUE
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
pre_commit_command:
|
||||
type: string
|
||||
default: "pre-commit run --all-files --show-diff-on-failure"
|
||||
secrets:
|
||||
ssh_private_key:
|
||||
required: false
|
||||
ssh_known_hosts:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load env vars
|
||||
if: ${{ inputs.env != '' }}
|
||||
run: |
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in \#*) continue;; esac
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Invalid env line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done <<< "${{ inputs.env }}"
|
||||
|
||||
- name: Start ssh-agent
|
||||
if: ${{ secrets.ssh_private_key != '' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ssh_private_key }}
|
||||
|
||||
- name: Add SSH known hosts
|
||||
if: ${{ secrets.ssh_known_hosts != '' }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: Install pre-commit
|
||||
env:
|
||||
PRE_COMMIT_VERSION: ${{ inputs.pre_commit_version }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
if [ -z "$PRE_COMMIT_VERSION" ] || [ "$PRE_COMMIT_VERSION" = "latest" ]; then
|
||||
python -m pip install pre-commit
|
||||
else
|
||||
python -m pip install "pre-commit==$PRE_COMMIT_VERSION"
|
||||
fi
|
||||
|
||||
- name: Cache pre-commit
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit-${{ runner.os }}-${{ hashFiles(inputs.cache_dependency_path) }}
|
||||
restore-keys: |
|
||||
pre-commit-${{ runner.os }}-
|
||||
|
||||
- name: Run pre-commit
|
||||
if: ${{ inputs.pre_commit_command != '' }}
|
||||
run: ${{ inputs.pre_commit_command }}
|
||||
248
.github/workflows/python-uv-ci-with-db.yml
vendored
Normal file
248
.github/workflows/python-uv-ci-with-db.yml
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
name: Python UV Quality (with DB)
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
type: string
|
||||
default: "3.13"
|
||||
uv_version:
|
||||
type: string
|
||||
default: "latest"
|
||||
working_directory:
|
||||
type: string
|
||||
default: "."
|
||||
cache_dependency_path:
|
||||
type: string
|
||||
default: "uv.lock"
|
||||
env:
|
||||
description: >
|
||||
Multiline env vars, one per line: KEY=VALUE
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
test_env:
|
||||
description: >
|
||||
Multiline env vars for tests, one per line: KEY=VALUE
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
uv_sync_args:
|
||||
type: string
|
||||
default: "--frozen --dev"
|
||||
format_command:
|
||||
type: string
|
||||
default: "uv run ruff format --check ."
|
||||
lint_command:
|
||||
type: string
|
||||
default: "uv run ruff check ."
|
||||
typecheck_command:
|
||||
type: string
|
||||
default: "uv run mypy ."
|
||||
test_command:
|
||||
type: string
|
||||
default: "uv run pytest"
|
||||
alembic_command:
|
||||
type: string
|
||||
default: ""
|
||||
postgres_image:
|
||||
type: string
|
||||
default: "postgres:16"
|
||||
postgres_user:
|
||||
type: string
|
||||
default: "postgres"
|
||||
postgres_password:
|
||||
type: string
|
||||
default: "postgres"
|
||||
postgres_db:
|
||||
type: string
|
||||
default: "test_db"
|
||||
postgres_health_cmd:
|
||||
type: string
|
||||
default: "pg_isready -U postgres"
|
||||
postgres_health_interval:
|
||||
type: string
|
||||
default: "10s"
|
||||
postgres_health_timeout:
|
||||
type: string
|
||||
default: "5s"
|
||||
postgres_health_retries:
|
||||
type: string
|
||||
default: "5"
|
||||
redis_image:
|
||||
type: string
|
||||
default: "redis:7-alpine"
|
||||
redis_health_cmd:
|
||||
type: string
|
||||
default: "redis-cli ping"
|
||||
redis_health_interval:
|
||||
type: string
|
||||
default: "5s"
|
||||
redis_health_timeout:
|
||||
type: string
|
||||
default: "5s"
|
||||
redis_health_retries:
|
||||
type: string
|
||||
default: "20"
|
||||
secrets:
|
||||
ssh_private_key:
|
||||
required: false
|
||||
ssh_known_hosts:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: ${{ inputs.postgres_image }}
|
||||
env:
|
||||
POSTGRES_USER: ${{ inputs.postgres_user }}
|
||||
POSTGRES_PASSWORD: ${{ inputs.postgres_password }}
|
||||
POSTGRES_DB: ${{ inputs.postgres_db }}
|
||||
ports:
|
||||
- 5432
|
||||
options: >-
|
||||
--health-cmd="${{ inputs.postgres_health_cmd }}"
|
||||
--health-interval=${{ inputs.postgres_health_interval }}
|
||||
--health-timeout=${{ inputs.postgres_health_timeout }}
|
||||
--health-retries=${{ inputs.postgres_health_retries }}
|
||||
redis:
|
||||
image: ${{ inputs.redis_image }}
|
||||
options: >-
|
||||
--health-cmd="${{ inputs.redis_health_cmd }}"
|
||||
--health-interval=${{ inputs.redis_health_interval }}
|
||||
--health-timeout=${{ inputs.redis_health_timeout }}
|
||||
--health-retries=${{ inputs.redis_health_retries }}
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Configure DB env
|
||||
run: |
|
||||
if [ "${ACT:-}" = "true" ] || [ "${GITEA_ACTIONS:-}" = "true" ]; then
|
||||
host="postgres"
|
||||
port="5432"
|
||||
else
|
||||
host="127.0.0.1"
|
||||
port="${{ job.services.postgres.ports['5432'] }}"
|
||||
fi
|
||||
echo "POSTGRES_HOST=$host" >> "$GITHUB_ENV"
|
||||
echo "POSTGRES_PORT=$port" >> "$GITHUB_ENV"
|
||||
echo "POSTGRES_USER=${{ inputs.postgres_user }}" >> "$GITHUB_ENV"
|
||||
echo "POSTGRES_PASSWORD=${{ inputs.postgres_password }}" >> "$GITHUB_ENV"
|
||||
echo "POSTGRES_DB=${{ inputs.postgres_db }}" >> "$GITHUB_ENV"
|
||||
echo "DATABASE_URL=postgres://${{ inputs.postgres_user }}:${{ inputs.postgres_password }}@$host:$port/${{ inputs.postgres_db }}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Load env vars
|
||||
if: ${{ inputs.env != '' }}
|
||||
run: |
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in \#*) continue;; esac
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Invalid env line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done <<< "${{ inputs.env }}"
|
||||
|
||||
- name: Start ssh-agent
|
||||
if: ${{ secrets.ssh_private_key != '' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ssh_private_key }}
|
||||
|
||||
- name: Add SSH known hosts
|
||||
if: ${{ secrets.ssh_known_hosts != '' }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: Install uv
|
||||
env:
|
||||
UV_VERSION: ${{ inputs.uv_version }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
if [ -z "$UV_VERSION" ] || [ "$UV_VERSION" = "latest" ]; then
|
||||
python -m pip install uv
|
||||
else
|
||||
python -m pip install "uv==$UV_VERSION"
|
||||
fi
|
||||
|
||||
- name: Cache uv downloads
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/uv
|
||||
key: uv-${{ runner.os }}-${{ hashFiles(inputs.cache_dependency_path) }}
|
||||
restore-keys: |
|
||||
uv-${{ runner.os }}-
|
||||
|
||||
- name: Sync dependencies
|
||||
run: uv sync ${{ inputs.uv_sync_args }}
|
||||
|
||||
- name: Load test env vars
|
||||
if: ${{ inputs.test_env != '' }}
|
||||
run: |
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in \#*) continue;; esac
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Invalid env line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done <<< "${{ inputs.test_env }}"
|
||||
|
||||
- name: Wait for postgres
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
|
||||
host = os.getenv("POSTGRES_HOST", "127.0.0.1")
|
||||
port = int(os.getenv("POSTGRES_PORT", "5432"))
|
||||
deadline = time.time() + 60
|
||||
|
||||
while True:
|
||||
try:
|
||||
with socket.create_connection((host, port), timeout=2):
|
||||
print(f"Postgres reachable at {host}:{port}")
|
||||
break
|
||||
except OSError:
|
||||
if time.time() > deadline:
|
||||
raise
|
||||
time.sleep(1)
|
||||
PY
|
||||
|
||||
- name: Run format check
|
||||
if: ${{ inputs.format_command != '' }}
|
||||
run: ${{ inputs.format_command }}
|
||||
|
||||
- name: Run lint
|
||||
if: ${{ inputs.lint_command != '' }}
|
||||
run: ${{ inputs.lint_command }}
|
||||
|
||||
- name: Run typecheck
|
||||
if: ${{ inputs.typecheck_command != '' }}
|
||||
run: ${{ inputs.typecheck_command }}
|
||||
|
||||
- name: Alembic upgrade head (smoke test)
|
||||
if: ${{ inputs.alembic_command != '' }}
|
||||
run: ${{ inputs.alembic_command }}
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ inputs.test_command != '' }}
|
||||
run: ${{ inputs.test_command }}
|
||||
124
.github/workflows/python-uv-ci.yml
vendored
Normal file
124
.github/workflows/python-uv-ci.yml
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
name: Python UV Quality
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
python_version:
|
||||
type: string
|
||||
default: "3.13"
|
||||
uv_version:
|
||||
type: string
|
||||
default: "latest"
|
||||
working_directory:
|
||||
type: string
|
||||
default: "."
|
||||
cache_dependency_path:
|
||||
type: string
|
||||
default: "uv.lock"
|
||||
env:
|
||||
description: >
|
||||
Multiline env vars, one per line: KEY=VALUE
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
uv_sync_args:
|
||||
type: string
|
||||
default: "--frozen --dev"
|
||||
format_command:
|
||||
type: string
|
||||
default: "uv run ruff format --check ."
|
||||
lint_command:
|
||||
type: string
|
||||
default: "uv run ruff check ."
|
||||
typecheck_command:
|
||||
type: string
|
||||
default: "uv run mypy ."
|
||||
test_command:
|
||||
type: string
|
||||
default: "uv run pytest"
|
||||
secrets:
|
||||
ssh_private_key:
|
||||
required: false
|
||||
ssh_known_hosts:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ inputs.working_directory }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Load env vars
|
||||
if: ${{ inputs.env != '' }}
|
||||
run: |
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
case "$line" in \#*) continue;; esac
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Invalid env line: $line" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done <<< "${{ inputs.env }}"
|
||||
|
||||
- name: Start ssh-agent
|
||||
if: ${{ secrets.ssh_private_key != '' }}
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.ssh_private_key }}
|
||||
|
||||
- name: Add SSH known hosts
|
||||
if: ${{ secrets.ssh_known_hosts != '' }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
printf '%s\n' "${{ secrets.ssh_known_hosts }}" >> ~/.ssh/known_hosts
|
||||
chmod 644 ~/.ssh/known_hosts
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ inputs.python_version }}
|
||||
|
||||
- name: Install uv
|
||||
env:
|
||||
UV_VERSION: ${{ inputs.uv_version }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
if [ -z "$UV_VERSION" ] || [ "$UV_VERSION" = "latest" ]; then
|
||||
python -m pip install uv
|
||||
else
|
||||
python -m pip install "uv==$UV_VERSION"
|
||||
fi
|
||||
|
||||
- name: Cache uv downloads
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/uv
|
||||
key: uv-${{ runner.os }}-${{ hashFiles(inputs.cache_dependency_path) }}
|
||||
restore-keys: |
|
||||
uv-${{ runner.os }}-
|
||||
|
||||
- name: Sync dependencies
|
||||
run: uv sync ${{ inputs.uv_sync_args }}
|
||||
|
||||
- name: Run format check
|
||||
if: ${{ inputs.format_command != '' }}
|
||||
run: ${{ inputs.format_command }}
|
||||
|
||||
- name: Run lint
|
||||
if: ${{ inputs.lint_command != '' }}
|
||||
run: ${{ inputs.lint_command }}
|
||||
|
||||
- name: Run typecheck
|
||||
if: ${{ inputs.typecheck_command != '' }}
|
||||
run: ${{ inputs.typecheck_command }}
|
||||
|
||||
- name: Run tests
|
||||
if: ${{ inputs.test_command != '' }}
|
||||
run: ${{ inputs.test_command }}
|
||||
Reference in New Issue
Block a user