This commit is contained in:
+11
-8
@@ -1,15 +1,18 @@
|
||||
FROM golang:1.22-alpine AS builder
|
||||
WORKDIR /app
|
||||
WORKDIR /src
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY . .
|
||||
RUN go build -o kforge .
|
||||
RUN go build -o /usr/local/bin/kforge .
|
||||
|
||||
FROM alpine:3.19
|
||||
COPY --from=builder /app/kforge /usr/local/bin/kforge
|
||||
RUN apk add --no-cache curl docker-cli && \
|
||||
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
|
||||
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && \
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||
FROM alpine:3.20
|
||||
RUN apk add --no-cache ca-certificates curl git
|
||||
|
||||
ARG KUBECTL_VERSION=v1.31.0
|
||||
RUN curl -fsSL "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" \
|
||||
-o /usr/local/bin/kubectl && chmod +x /usr/local/bin/kubectl
|
||||
|
||||
COPY --from=builder /usr/local/bin/kforge /usr/local/bin/kforge
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
|
||||
+19
-52
@@ -1,62 +1,29 @@
|
||||
name: "K8s YAML Generator"
|
||||
description: "Builds a Docker image, pushes it to a private registry, generates Kubernetes YAML from a simplified YML file, and deploys it."
|
||||
author: "Claude Code made this"
|
||||
name: 'kforge'
|
||||
description: 'Generate and apply Kubernetes manifests from kforge.yml'
|
||||
|
||||
inputs:
|
||||
image_name:
|
||||
description: "Docker image name to build and push (e.g. my-app)"
|
||||
required: true
|
||||
image_tag:
|
||||
description: "Docker image tag. If omitted, defaults to both 'latest' and the short commit SHA."
|
||||
command:
|
||||
description: 'deploy | preview-up | preview-down | validate | secrets'
|
||||
required: false
|
||||
dockerfile:
|
||||
description: "Path to Dockerfile"
|
||||
default: 'deploy'
|
||||
env:
|
||||
description: 'Environment to target (e.g. production, staging). Omit to target all.'
|
||||
required: false
|
||||
default: "Dockerfile"
|
||||
max_tags:
|
||||
description: "Maximum number of SHA image tags to keep in the registry"
|
||||
config:
|
||||
description: 'Path to kforge.yml relative to the workspace root'
|
||||
required: false
|
||||
default: "5"
|
||||
|
||||
registry:
|
||||
description: "Docker registry URL"
|
||||
default: 'kforge.yml'
|
||||
namespace:
|
||||
description: 'Kubernetes namespace for rollout restart (defaults to env name)'
|
||||
required: false
|
||||
default: "registry.natelubitz.com"
|
||||
registry_username:
|
||||
description: "Registry username"
|
||||
required: true
|
||||
registry_password:
|
||||
description: "Registry password"
|
||||
required: true
|
||||
|
||||
kube_host:
|
||||
description: "Kubernetes API server URL"
|
||||
pr_number:
|
||||
description: 'PR number — required for preview-up and preview-down'
|
||||
required: false
|
||||
default: "192.168.1.20:16443"
|
||||
kube_certificate:
|
||||
description: "Base64 encoded Kubernetes CA certificate"
|
||||
required: true
|
||||
kube_token:
|
||||
description: "Kubernetes service account token"
|
||||
required: true
|
||||
|
||||
scan_image:
|
||||
description: "Scan image for vulnerabilities before pushing"
|
||||
namespace_prefix:
|
||||
description: 'Namespace prefix for preview environments'
|
||||
required: false
|
||||
default: "true"
|
||||
scan_severity:
|
||||
description: "Fail on these severity levels (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL)"
|
||||
required: false
|
||||
default: "HIGH,CRITICAL"
|
||||
|
||||
# outputs:
|
||||
# output_file:
|
||||
# description: "Path to the generated Kubernetes YAML file"
|
||||
default: 'preview-pr'
|
||||
|
||||
runs:
|
||||
using: "docker"
|
||||
image: "docker://registry.natelubitz.com/infra/kforge:latest"
|
||||
# args:
|
||||
# - ${{ inputs.input_file }}
|
||||
# - ${{ inputs.output_file }}
|
||||
# - ${{ inputs.auto_deploy }}
|
||||
using: docker
|
||||
image: Dockerfile
|
||||
|
||||
+78
-134
@@ -1,146 +1,90 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# INPUT_FILE="$1"
|
||||
# OUTPUT_FILE="$2"
|
||||
# AUTO_DEPLOY="$3"
|
||||
COMMAND="${INPUT_COMMAND:-deploy}"
|
||||
CONFIG="${INPUT_CONFIG:-kforge.yml}"
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Registry login
|
||||
# ----------------------------------------------------------------
|
||||
if [ -n "$INPUT_REGISTRY_USERNAME" ] && [ -n "$INPUT_REGISTRY_PASSWORD" ]; then
|
||||
echo "Logging in to $INPUT_REGISTRY..."
|
||||
echo "$INPUT_REGISTRY_PASSWORD" | docker login "$INPUT_REGISTRY" \
|
||||
-u "$INPUT_REGISTRY_USERNAME" --password-stdin
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Build and push image
|
||||
# ----------------------------------------------------------------
|
||||
cleanup_old_tags() {
|
||||
IMAGE="$1"
|
||||
KEEP="$2"
|
||||
|
||||
echo "Fetching tags for $IMAGE..."
|
||||
|
||||
TAGS=$(curl -s -u "$INPUT_REGISTRY_USERNAME:$INPUT_REGISTRY_PASSWORD" \
|
||||
"https://$INPUT_REGISTRY/v2/$IMAGE/tags/list" \
|
||||
| tr ',' '\n' \
|
||||
| grep -o '"[a-f0-9]\{7\}"' \
|
||||
| tr -d '"')
|
||||
|
||||
COUNT=$(echo "$TAGS" | grep -c .)
|
||||
DELETE_COUNT=$((COUNT - KEEP))
|
||||
|
||||
if [ "$DELETE_COUNT" -le 0 ]; then
|
||||
echo "Only $COUNT hash tags found, no cleanup needed."
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Found $COUNT hash tags, deleting oldest $DELETE_COUNT..."
|
||||
|
||||
echo "$TAGS" | head -n "$DELETE_COUNT" | while read -r TAG; do
|
||||
echo "Deleting tag: $TAG..."
|
||||
|
||||
DIGEST=$(curl -s -I \
|
||||
-u "$INPUT_REGISTRY_USERNAME:$INPUT_REGISTRY_PASSWORD" \
|
||||
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
|
||||
"https://$INPUT_REGISTRY/v2/$IMAGE/manifests/$TAG" \
|
||||
| grep -i "docker-content-digest" \
|
||||
| tr -d '\r' \
|
||||
| awk '{print $2}')
|
||||
|
||||
if [ -n "$DIGEST" ]; then
|
||||
curl -s -X DELETE \
|
||||
-u "$INPUT_REGISTRY_USERNAME:$INPUT_REGISTRY_PASSWORD" \
|
||||
"https://$INPUT_REGISTRY/v2/$IMAGE/manifests/$DIGEST"
|
||||
echo "Deleted $TAG ($DIGEST)"
|
||||
else
|
||||
echo "Could not find digest for $TAG, skipping."
|
||||
fi
|
||||
done
|
||||
# Build a kubeconfig from the standard KUBE_* CI secrets.
|
||||
setup_kube() {
|
||||
[ -z "$KUBE_HOST" ] && return
|
||||
mkdir -p ~/.kube
|
||||
cat > ~/.kube/config <<KUBEEOF
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- cluster:
|
||||
certificate-authority-data: ${KUBE_CERTIFICATE}
|
||||
server: ${KUBE_HOST}
|
||||
name: kforge
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kforge
|
||||
user: kforge
|
||||
name: kforge
|
||||
current-context: kforge
|
||||
users:
|
||||
- name: kforge
|
||||
user:
|
||||
token: ${KUBE_TOKEN}
|
||||
KUBEEOF
|
||||
chmod 600 ~/.kube/config
|
||||
}
|
||||
|
||||
if [ -n "$INPUT_IMAGE_NAME" ]; then
|
||||
FULL_IMAGE="$INPUT_REGISTRY/$INPUT_IMAGE_NAME"
|
||||
cd "${GITHUB_WORKSPACE:-/github/workspace}"
|
||||
|
||||
if [ -n "$INPUT_IMAGE_TAG" ]; then
|
||||
echo "Building image $FULL_IMAGE:$INPUT_IMAGE_TAG..."
|
||||
docker build -t "$FULL_IMAGE:$INPUT_IMAGE_TAG" -f "$INPUT_DOCKERFILE" .
|
||||
case "$COMMAND" in
|
||||
deploy)
|
||||
export KFORGE_IMAGE_TAG="$(git rev-parse --short HEAD)"
|
||||
setup_kube
|
||||
kforge validate -c "$CONFIG"
|
||||
if [ -n "$INPUT_ENV" ]; then
|
||||
kforge secrets apply --env "$INPUT_ENV" -c "$CONFIG"
|
||||
kforge generate --env "$INPUT_ENV" --output .kforge-out -c "$CONFIG"
|
||||
else
|
||||
kforge generate --output .kforge-out -c "$CONFIG"
|
||||
fi
|
||||
kubectl apply -f .kforge-out/ --insecure-skip-tls-verify
|
||||
NAMESPACE="${INPUT_NAMESPACE:-${INPUT_ENV}}"
|
||||
if [ -n "$NAMESPACE" ]; then
|
||||
kubectl rollout restart deployment -n "$NAMESPACE" --insecure-skip-tls-verify || true
|
||||
fi
|
||||
;;
|
||||
|
||||
echo "Scanning image for vulnerabilities..."
|
||||
trivy image \
|
||||
--exit-code 1 \
|
||||
--severity "$INPUT_SCAN_SEVERITY" \
|
||||
--no-progress \
|
||||
"$FULL_IMAGE:$INPUT_IMAGE_TAG"
|
||||
preview-up)
|
||||
[ -z "$INPUT_PR_NUMBER" ] && echo "::error::pr_number input is required for preview-up" && exit 1
|
||||
setup_kube
|
||||
kforge secrets apply --pr-number "$INPUT_PR_NUMBER" -c "$CONFIG"
|
||||
kforge generate --pr-number "$INPUT_PR_NUMBER" --output .kforge-out -c "$CONFIG"
|
||||
kubectl apply -f .kforge-out/ --insecure-skip-tls-verify
|
||||
NS="${INPUT_NAMESPACE_PREFIX:-preview-pr}-${INPUT_PR_NUMBER}"
|
||||
kubectl rollout status deployment -n "$NS" --timeout=120s --insecure-skip-tls-verify || true
|
||||
;;
|
||||
|
||||
echo "Scan passed, pushing image..."
|
||||
docker push "$FULL_IMAGE:$INPUT_IMAGE_TAG"
|
||||
else
|
||||
SHA=$(echo "$GITHUB_SHA" | cut -c1-7)
|
||||
echo "Building image $FULL_IMAGE:latest and $FULL_IMAGE:$SHA..."
|
||||
docker build \
|
||||
-t "$FULL_IMAGE:latest" \
|
||||
-t "$FULL_IMAGE:$SHA" \
|
||||
-f "$INPUT_DOCKERFILE" .
|
||||
preview-down)
|
||||
[ -z "$INPUT_PR_NUMBER" ] && echo "::error::pr_number input is required for preview-down" && exit 1
|
||||
setup_kube
|
||||
NS="${INPUT_NAMESPACE_PREFIX:-preview-pr}-${INPUT_PR_NUMBER}"
|
||||
kubectl delete namespace "$NS" --ignore-not-found --insecure-skip-tls-verify
|
||||
;;
|
||||
|
||||
echo "Scanning image for vulnerabilities..."
|
||||
trivy image \
|
||||
--exit-code 1 \
|
||||
--severity "$INPUT_SCAN_SEVERITY" \
|
||||
--no-progress \
|
||||
"$FULL_IMAGE:latest"
|
||||
validate)
|
||||
kforge validate -c "$CONFIG"
|
||||
;;
|
||||
|
||||
echo "Scan passed, pushing image..."
|
||||
docker push "$FULL_IMAGE:latest"
|
||||
docker push "$FULL_IMAGE:$SHA"
|
||||
secrets)
|
||||
setup_kube
|
||||
if [ -n "$INPUT_PR_NUMBER" ]; then
|
||||
kforge secrets apply --pr-number "$INPUT_PR_NUMBER" -c "$CONFIG"
|
||||
elif [ -n "$INPUT_ENV" ]; then
|
||||
kforge secrets apply --env "$INPUT_ENV" -c "$CONFIG"
|
||||
else
|
||||
echo "::error::env or pr_number input is required for the secrets command"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
cleanup_old_tags "$INPUT_IMAGE_NAME" "${INPUT_MAX_TAGS:-5}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Generate Kubernetes YAML
|
||||
# ----------------------------------------------------------------
|
||||
echo "Generating Kubernetes YAML from .kforge.yml"
|
||||
/usr/local/bin/kforge generate
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# Deploy to Kubernetes
|
||||
# ----------------------------------------------------------------
|
||||
# Build kubeconfig from token-based credentials
|
||||
echo "Configuring kubectl..."
|
||||
|
||||
# Try writing the cert and check if it worked
|
||||
echo "$INPUT_KUBE_CERTIFICATE" | base64 -d > /tmp/kube-ca.crt 2>&1
|
||||
echo "Cert file size: $(wc -c < /tmp/kube-ca.crt)"
|
||||
echo "Cert file contents: $(cat /tmp/kube-ca.crt | head -1)"
|
||||
|
||||
kubectl config set-cluster default \
|
||||
--server="$INPUT_KUBE_HOST" \
|
||||
--certificate-authority=/tmp/kube-ca.crt
|
||||
|
||||
kubectl config set-credentials default \
|
||||
--token="$INPUT_KUBE_TOKEN"
|
||||
|
||||
kubectl config set-context default \
|
||||
--cluster=default \
|
||||
--user=default
|
||||
|
||||
kubectl config use-context default
|
||||
|
||||
|
||||
# Create/update regcred secret idempotently
|
||||
# echo "Creating regcred secret..."
|
||||
# kubectl create secret docker-registry regcred \
|
||||
# --docker-server="$INPUT_REGISTRY" \
|
||||
# --docker-username="$INPUT_REGISTRY_USERNAME" \
|
||||
# --docker-password="$INPUT_REGISTRY_PASSWORD" \
|
||||
# --dry-run=client -o yaml | kubectl apply -f - --insecure-skip-tls-verify --validate=false
|
||||
|
||||
echo "Deploying to Kubernetes..."
|
||||
kubectl apply --insecure-skip-tls-verify --validate=false -f ./.kforge-out/
|
||||
echo "Deploy complete."
|
||||
echo "Cleanup"
|
||||
rm -f /tmp/kube-ca.crt
|
||||
*)
|
||||
echo "::error::Unknown command '$COMMAND'. Valid: deploy, preview-up, preview-down, validate, secrets"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"kforge/pkg/interpolate"
|
||||
@@ -306,6 +307,11 @@ func ResolveEnvironment(cfg *KforgeConfig, envKey string) (ResolvedEnvironment,
|
||||
if imageTag == "" {
|
||||
imageTag = DefaultImageTag
|
||||
}
|
||||
// Allow the action entrypoint (or CI) to override the image tag at generate
|
||||
// time without modifying kforge.yml (e.g. KFORGE_IMAGE_TAG=abc1234).
|
||||
if override := os.Getenv("KFORGE_IMAGE_TAG"); override != "" {
|
||||
imageTag = override
|
||||
}
|
||||
image := registry.URL + "/" + registry.Repository + ":" + imageTag
|
||||
|
||||
replicas := *cfg.Defaults.Replicas
|
||||
|
||||
@@ -14,6 +14,12 @@ type KforgeConfig struct {
|
||||
Infrastructure InfrastructureConfig `yaml:"infrastructure"`
|
||||
Preview PreviewConfig `yaml:"preview,omitempty"`
|
||||
Environments map[string]EnvironmentConfig `yaml:"environments"`
|
||||
|
||||
// ActionRef is the Gitea Actions reference to this kforge installation
|
||||
// (e.g. "gitea.example.com/infra/kforge@main"). When set, kforge gitea-actions
|
||||
// and kforge gitea-preview generate workflows that call kforge as a reusable
|
||||
// action step instead of installing and running kforge inline.
|
||||
ActionRef string `yaml:"action_ref,omitempty"`
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
@@ -74,10 +74,6 @@ func writeDeployJobs(b *strings.Builder, cfg *config.KforgeConfig, opts GiteaAct
|
||||
"with": map[string]any{"fetch-depth": 0},
|
||||
})
|
||||
|
||||
writeStep(b, "Create short commit hash", map[string]any{
|
||||
"run": `echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV`,
|
||||
})
|
||||
|
||||
writeStep(b, "Login to registry", map[string]any{
|
||||
"uses": "docker/login-action@v2",
|
||||
"with": map[string]any{
|
||||
@@ -101,6 +97,39 @@ func writeDeployJobs(b *strings.Builder, cfg *config.KforgeConfig, opts GiteaAct
|
||||
},
|
||||
})
|
||||
|
||||
if cfg.ActionRef != "" {
|
||||
writeActionDeploySteps(b, cfg, opts)
|
||||
} else {
|
||||
writeInlineDeploySteps(b, cfg, opts)
|
||||
}
|
||||
}
|
||||
|
||||
// writeActionDeploySteps emits one `uses: action_ref` step per environment.
|
||||
func writeActionDeploySteps(b *strings.Builder, cfg *config.KforgeConfig, opts GiteaActionsOptions) {
|
||||
for _, envKey := range opts.Environments {
|
||||
env, err := config.ResolveEnvironment(cfg, envKey)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
label := strings.Title(envKey) //nolint:staticcheck
|
||||
writeStep(b, fmt.Sprintf("Deploy (%s)", label), map[string]any{
|
||||
"uses": cfg.ActionRef,
|
||||
"with": map[string]any{
|
||||
"command": "deploy",
|
||||
"env": envKey,
|
||||
"namespace": env.Namespace,
|
||||
},
|
||||
"env": actionEnv(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// writeInlineDeploySteps emits the classic multi-step inline approach.
|
||||
func writeInlineDeploySteps(b *strings.Builder, cfg *config.KforgeConfig, opts GiteaActionsOptions) {
|
||||
writeStep(b, "Create short commit hash", map[string]any{
|
||||
"run": `echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_ENV`,
|
||||
})
|
||||
|
||||
writeStep(b, "Install kforge", map[string]any{
|
||||
"run": "KFORGE_VERSION=\"latest\"\ncurl -fsSL \"https://kforge/releases/download/${KFORGE_VERSION}/kforge-linux-amd64\" -o /usr/local/bin/kforge\nchmod +x /usr/local/bin/kforge",
|
||||
})
|
||||
@@ -244,60 +273,82 @@ func GeneratePreviewActions(cfg *config.KforgeConfig) (string, error) {
|
||||
},
|
||||
})
|
||||
|
||||
writeStep(&b, "Install kforge", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"run": "KFORGE_VERSION=\"latest\"\ncurl -fsSL \"https://kforge/releases/download/${KFORGE_VERSION}/kforge-linux-amd64\" -o /usr/local/bin/kforge\nchmod +x /usr/local/bin/kforge",
|
||||
})
|
||||
if cfg.ActionRef != "" {
|
||||
writeStep(&b, "Deploy preview", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"uses": cfg.ActionRef,
|
||||
"with": map[string]any{
|
||||
"command": "preview-up",
|
||||
"pr_number": "${{ github.event.number }}",
|
||||
"namespace_prefix": nsPrefix,
|
||||
},
|
||||
"env": actionEnv(),
|
||||
})
|
||||
|
||||
writeStep(&b, "Apply preview secrets", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"run": "kforge secrets apply --pr-number ${{ github.event.number }}",
|
||||
"env": giteaKubeEnv(),
|
||||
})
|
||||
writeStep(&b, "Destroy preview", map[string]any{
|
||||
"if": "${{ github.event.action == 'closed' }}",
|
||||
"uses": cfg.ActionRef,
|
||||
"with": map[string]any{
|
||||
"command": "preview-down",
|
||||
"pr_number": "${{ github.event.number }}",
|
||||
"namespace_prefix": nsPrefix,
|
||||
},
|
||||
"env": actionEnv(),
|
||||
})
|
||||
} else {
|
||||
writeStep(&b, "Install kforge", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"run": "KFORGE_VERSION=\"latest\"\ncurl -fsSL \"https://kforge/releases/download/${KFORGE_VERSION}/kforge-linux-amd64\" -o /usr/local/bin/kforge\nchmod +x /usr/local/bin/kforge",
|
||||
})
|
||||
|
||||
writeStep(&b, "Generate preview manifests", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"run": "kforge generate --pr-number ${{ github.event.number }} --output .kforge-out",
|
||||
"env": map[string]any{
|
||||
"KFORGE_NODE_IP": "${{ secrets.KFORGE_NODE_IP }}",
|
||||
"PR_NUMBER": "${{ github.event.number }}",
|
||||
},
|
||||
})
|
||||
writeStep(&b, "Apply preview secrets", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"run": "kforge secrets apply --pr-number ${{ github.event.number }}",
|
||||
"env": giteaKubeEnv(),
|
||||
})
|
||||
|
||||
writeStep(&b, "Apply preview manifests", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"uses": "actions-hub/kubectl@master",
|
||||
"env": giteaKubeEnv(),
|
||||
"with": map[string]any{
|
||||
"args": fmt.Sprintf(
|
||||
"apply -f .kforge-out/ --insecure-skip-tls-verify",
|
||||
),
|
||||
},
|
||||
})
|
||||
writeStep(&b, "Generate preview manifests", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"run": "kforge generate --pr-number ${{ github.event.number }} --output .kforge-out",
|
||||
"env": map[string]any{
|
||||
"KFORGE_NODE_IP": "${{ secrets.KFORGE_NODE_IP }}",
|
||||
"PR_NUMBER": "${{ github.event.number }}",
|
||||
},
|
||||
})
|
||||
|
||||
writeStep(&b, "Wait for preview rollout", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"uses": "actions-hub/kubectl@master",
|
||||
"env": giteaKubeEnv(),
|
||||
"with": map[string]any{
|
||||
"args": fmt.Sprintf(
|
||||
"rollout status deployment -n %s-${{ github.event.number }} --timeout=120s --insecure-skip-tls-verify",
|
||||
nsPrefix,
|
||||
),
|
||||
},
|
||||
})
|
||||
writeStep(&b, "Apply preview manifests", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"uses": "actions-hub/kubectl@master",
|
||||
"env": giteaKubeEnv(),
|
||||
"with": map[string]any{
|
||||
"args": "apply -f .kforge-out/ --insecure-skip-tls-verify",
|
||||
},
|
||||
})
|
||||
|
||||
writeStep(&b, "Destroy preview namespace", map[string]any{
|
||||
"if": "${{ github.event.action == 'closed' }}",
|
||||
"uses": "actions-hub/kubectl@master",
|
||||
"env": giteaKubeEnv(),
|
||||
"with": map[string]any{
|
||||
"args": fmt.Sprintf(
|
||||
"delete namespace %s-${{ github.event.number }} --ignore-not-found --insecure-skip-tls-verify",
|
||||
nsPrefix,
|
||||
),
|
||||
},
|
||||
})
|
||||
writeStep(&b, "Wait for preview rollout", map[string]any{
|
||||
"if": "${{ github.event.action != 'closed' }}",
|
||||
"uses": "actions-hub/kubectl@master",
|
||||
"env": giteaKubeEnv(),
|
||||
"with": map[string]any{
|
||||
"args": fmt.Sprintf(
|
||||
"rollout status deployment -n %s-${{ github.event.number }} --timeout=120s --insecure-skip-tls-verify",
|
||||
nsPrefix,
|
||||
),
|
||||
},
|
||||
})
|
||||
|
||||
writeStep(&b, "Destroy preview namespace", map[string]any{
|
||||
"if": "${{ github.event.action == 'closed' }}",
|
||||
"uses": "actions-hub/kubectl@master",
|
||||
"env": giteaKubeEnv(),
|
||||
"with": map[string]any{
|
||||
"args": fmt.Sprintf(
|
||||
"delete namespace %s-${{ github.event.number }} --ignore-not-found --insecure-skip-tls-verify",
|
||||
nsPrefix,
|
||||
),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return b.String(), nil
|
||||
}
|
||||
@@ -383,6 +434,17 @@ func giteaKubeEnv() map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
// actionEnv returns the combined env block for a kforge action step —
|
||||
// kubectl auth plus the node IP for external-dns annotation resolution.
|
||||
func actionEnv() map[string]any {
|
||||
return map[string]any{
|
||||
"KUBE_CERTIFICATE": "${{ secrets.KUBE_CERTIFICATE }}",
|
||||
"KUBE_HOST": "${{ secrets.KUBE_HOST }}",
|
||||
"KUBE_TOKEN": "${{ secrets.KUBE_TOKEN }}",
|
||||
"KFORGE_NODE_IP": "${{ secrets.KFORGE_NODE_IP }}",
|
||||
}
|
||||
}
|
||||
|
||||
func mergeMaps(maps ...map[string]any) map[string]any {
|
||||
result := map[string]any{}
|
||||
for _, m := range maps {
|
||||
|
||||
Reference in New Issue
Block a user