Compare commits

..

No commits in common. "main" and "wahx6f-codex/fix-package-not-found-error" have entirely different histories.

169 changed files with 65 additions and 11253 deletions

View File

@ -1,25 +0,0 @@
name: Run Ansible Lint in Container
on:
pull_request:
branches: ["main", "stable", "release/v*"]
paths:
- '**/*.yaml'
- '**/*.yml'
workflow_dispatch:
branches:
- main
jobs:
ansible-lint:
runs-on: ubuntu-latest
container:
image: images.onwalk.net/public/base/alpine-ansible-ci-runner:0c09618
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run ansible-lint
run: echo ${{ secrets.VAULT_PASSWORD }} > ~/.vault_password && ansible-lint

View File

@ -1,111 +0,0 @@
name: Create and Test Offline Ansible Installer Release
on:
pull_request:
paths:
- '.github/workflows/ansible-offline-installer.yml'
workflow_dispatch:
branches:
- main
jobs:
prepare-offline-package:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
# Install dependencies and prepare packages for Ubuntu/Debian
- name: Install dependencies for Ubuntu/Debian
run: |
sudo apt update
sudo apt install -y python3 python3-pip
pip3 install --download /tmp/offline_packages ansible
# Install dependencies for CentOS (7.x and 8.x)
- name: Install dependencies for CentOS (7.x and 8.x)
run: |
sudo yum install -y python3 python3-pip
pip3 install --download /tmp/offline_packages ansible
if: runner.os == 'Linux' && (startsWith(runner.os, 'rhel') || startsWith(runner.os, 'centos'))
# Create the installer package
- name: Create ansible-offline-installer.tar.gz
run: |
mkdir -p installer
tar -czvf installer/ansible-offline-package.tar.gz -C /tmp offline_packages
echo '#!/bin/bash' > installer/install-ansible.sh
echo 'if [ -f /etc/os-release ]; then' >> installer/install-ansible.sh
echo ' . /etc/os-release' >> installer/install-ansible.sh
echo ' if [[ "$ID" == "ubuntu" || "$ID_LIKE" == "debian" ]]; then' >> installer/install-ansible.sh
echo ' pip3 install --no-index --find-links=/tmp/offline_packages ansible' >> installer/install-ansible.sh
echo ' elif [[ "$ID" == "centos" || "$ID" == "rhel" ]]; then' >> installer/install-ansible.sh
echo ' pip3 install --no-index --find-links=/tmp/offline_packages ansible' >> installer/install-ansible.sh
echo ' fi' >> installer/install-ansible.sh
echo 'fi' >> installer/install-ansible.sh
chmod +x installer/install-ansible.sh
tar -czvf ansible-offline-installer.tar.gz installer
# Upload the installer package as an artifact
- name: Upload ansible-offline-installer.tar.gz as artifact
uses: actions/upload-artifact@v2
with:
name: ansible-offline-installer
path: ansible-offline-installer.tar.gz
test-installer:
runs-on: ubuntu-latest
needs: prepare-offline-package
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, centos-7, centos-8]
steps:
- name: Checkout the repository
uses: actions/checkout@v2
# Download the installer package from the artifact
- name: Download the installer package from artifact
uses: actions/download-artifact@v2
with:
name: ansible-offline-installer
# Extract the installer package
- name: Extract the installer package
run: |
tar -xzvf ansible-offline-installer.tar.gz
# Run the installer script
- name: Run the installer script
run: |
./installer/install-ansible.sh
# Verify Ansible installation
- name: Verify Ansible installation
run: |
ansible --version
create-release:
runs-on: ubuntu-latest
needs: test-installer
if: success() # Only run if the test-installer job succeeds
steps:
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: v${{ github.run_number }}-${{ github.run_id }} # Generate version number
release_name: Release v${{ github.run_number }}-${{ github.run_id }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload ansible-offline-installer.tar.gz to Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ github.run_number }}-${{ github.run_id }}
files: |
ansible-offline-installer.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,36 +0,0 @@
name: build chart multi-model llm
on:
pull_request:
branches:
- main
paths:
- 'oci/multi-model-LLM/**'
- '.github/workflows/build-chart-multi-model-LLM.yaml'
workflow_dispatch:
branches:
- main
env:
CHART_DIR: oci/multi-model-LLM/charts/model-serving
jobs:
lint-and-package:
name: Lint and package Helm chart
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Helm
uses: azure/setup-helm@v4
with:
version: v3.14.4
- name: Helm lint
run: helm lint $CHART_DIR
- name: Helm package
run: helm package $CHART_DIR --version 0.1.0 --app-version 0.1.0 -d oci/multi-model-LLM/charts
- name: Upload chart artifact
uses: actions/upload-artifact@v4
with:
name: model-serving-chart
path: oci/multi-model-LLM/charts/model-serving-0.1.0.tgz

View File

@ -1,29 +0,0 @@
name: build image ollama
on:
pull_request:
branches:
- main
paths:
- 'oci/base/cuda/Ollama/Dockerfile'
- '.github/workflows/build-ci-image-Ollama.yaml'
workflow_dispatch:
branches:
- main
env:
IMAGE_REPO: "artifact.svc.plus"
jobs:
build-ollama:
name: Build Ollama image
uses: svc-design/actions/.github/workflows/build-images.yaml@main
with:
method: 'docker'
registry_addr: "harbor.onwalk.net"
dockerfile_path: 'oci/base/cuda/Ollama'
image_name: 'public/base/cuda/ollama'
image_tag: 'latest'
secrets:
artifactory_sa: ${{ secrets.REPO_USER }}
artifactory_pw: ${{ secrets.HELM_REPO_PASSWORD }}

View File

@ -1,29 +0,0 @@
name: build image cuda sglang
on:
pull_request:
branches:
- main
paths:
- 'oci/base/cuda/SGLang/Dockerfile'
- '.github/workflows/build-ci-image-SGLang.yaml'
workflow_dispatch:
branches:
- main
env:
IMAGE_REPO: "artifact.svc.plus"
jobs:
build-sglang:
name: Build CUDA SGLang image
uses: svc-design/actions/.github/workflows/build-images.yaml@main
with:
method: 'docker'
registry_addr: "harbor.onwalk.net"
dockerfile_path: 'oci/base/cuda/SGLang'
image_name: 'public/base/cuda/sglang'
image_tag: 'cuda12'
secrets:
artifactory_sa: ${{ secrets.REPO_USER }}
artifactory_pw: ${{ secrets.HELM_REPO_PASSWORD }}

View File

@ -1,29 +0,0 @@
name: build image proxysql
on:
pull_request:
branches:
- main
paths:
- 'oci/proxysql/Dockerfile'
- '.github/workflows/build-ci-image-proxysql.yml'
workflow_dispatch:
branches:
- main
env:
IMAGE_REPO: "artifact.svc.plus"
jobs:
proxysql:
name: Build ProxySQL image
uses: svc-design/actions/.github/workflows/build-images.yaml@main
with:
method: 'docker'
registry_addr: "ghcr.io" # 推送到 GitHub Container Registry
dockerfile_path: 'oci/proxysql' # 你放 Dockerfile 的目录
image_name: 'public/base/proxysql' # 镜像仓库路径 (可按你实际改)
image_tag: '3.0.2-nojemalloc' # 标签,可以改成 latest 或 matrix
secrets:
artifactory_sa: ${{ secrets.REPO_USER }}
artifactory_pw: ${{ secrets.HELM_REPO_PASSWORD }}

View File

@ -1,29 +0,0 @@
name: build image cuda vllm
on:
pull_request:
branches:
- main
paths:
- 'oci/base/cuda/vLLM/Dockerfile'
- '.github/workflows/build-ci-image-vLLM.yaml'
workflow_dispatch:
branches:
- main
env:
IMAGE_REPO: "artifact.svc.plus"
jobs:
build-vllm:
name: Build CUDA vLLM image
uses: svc-design/actions/.github/workflows/build-images.yaml@main
with:
method: 'docker'
registry_addr: "harbor.onwalk.net"
dockerfile_path: 'oci/base/cuda/vLLM'
image_name: 'public/base/cuda/vllm'
image_tag: 'cuda12'
secrets:
artifactory_sa: ${{ secrets.REPO_USER }}
artifactory_pw: ${{ secrets.HELM_REPO_PASSWORD }}

View File

@ -1,140 +0,0 @@
name: Build Offline Pulp Installer
on:
push:
paths:
- 'scripts/pulp-installer.sh'
- '.github/workflows/build-pulp-offline-installer.yml'
workflow_dispatch:
jobs:
build-pulp-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Prepare offline structure
run: mkdir -p offline-pulp/{charts,scripts,images,manifests}
- name: Download Helm Chart
run: |
helm repo add pulp-operator https://github.com/pulp/pulp-k8s-resources/raw/main/helm-charts/ --force-update
helm repo update
helm pull pulp-operator/pulp-operator --untar --untardir offline-pulp/charts
- name: Pull & Save Image
run: |
docker pull quay.io/pulp/pulp-operator:v1.0.0-beta.5
docker save quay.io/pulp/pulp-operator:v1.0.0-beta.5 -o offline-pulp/images/pulp-operator.tar
docker pull gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0
docker save gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 -o offline-pulp/images/kube-rbac-proxy.tar
- name: Copy installer script
run: |
cp scripts/pulp-installer.sh offline-pulp/scripts/
chmod +x offline-pulp/scripts/pulp-installer.sh
- name: Package offline installer
run: |
cd offline-pulp
tar czvf ../offline-setup-pulp-${{ matrix.arch }}.tar.gz ./
cd ..
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-setup-pulp-${{ matrix.arch }}
path: offline-setup-pulp-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-pulp-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download offline installer artifact for ${{ matrix.arch }}
uses: actions/download-artifact@v4
with:
name: offline-setup-pulp-${{ matrix.arch }}
path: offline-test
- name: Setup K3s and KUBECONFIG for user
run: |
curl -sfL https://get.k3s.io | sudo sh -
mkdir -p $HOME/.kube
sudo cp /etc/rancher/k3s/k3s.yaml $HOME/.kube/config
sudo chown $USER:$USER $HOME/.kube/config
kubectl get nodes
kubectl version
- name: Install Helm
run: |
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | sudo bash
- name: Load offline installer package
run: |
cd offline-test
tar -xzvf offline-setup-pulp-${{ matrix.arch }}.tar.gz
if [ -f nerdctl.tar.gz ]; then
sudo tar xzvf nerdctl.tar.gz -C /usr/local/bin/
fi
docker load -i images/pulp-operator.tar
cd ..
- name: Run offline Pulp installer in K3S
run: |
cd offline-test
bash scripts/pulp-installer.sh
sleep 15
helm list -A
kubectl -n pulp get pods
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
tag_name: offline-pulp-${{ github.run_number }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.tag_name }}
release_name: Daily Build ${{ env.tag_name }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-pulp-amd64
path: release-artifacts
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-pulp-arm64
path: release-artifacts
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.tag_name }}
files: |
release-artifacts/offline-setup-pulp-amd64.tar.gz
release-artifacts/offline-setup-pulp-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,216 +0,0 @@
name: Cloud-Neutra Golden Image Pipeline
on:
workflow_dispatch:
inputs:
edition:
description: "Golden Image Edition"
type: choice
options: ["base", "container", "k3s", "sealos", "sealos-gpu"]
default: "container"
ubuntu_version:
description: "Ubuntu LTS version"
type: choice
options: ["2204", "2404"]
default: "2404"
cpu_arch:
description: "CPU Architecture"
type: choice
options: ["amd64", "arm64"]
default: "amd64"
schedule:
- cron: "0 18 1 * *"
env:
BASE_REGION: ap-northeast-1
TARGET_REGIONS: "ap-northeast-1 ap-east-1 us-west-1"
PROJECT_TAG: Cloud-Neutra
PACKER_TEMPLATE_ROOT: packer/Cloud-Neutra-VMs/templates
jobs:
##########################################################################
# Stage 1 — Lint / Validate / Security
##########################################################################
lint:
name: Lint & Validate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: actionlint
uses: raven-actions/actionlint@v2
with:
files: ".github/workflows/cloud-neutra-golden-image.yaml"
matcher: false
cache: false
fail-on-error: true
flags: "-ignore SC2086"
- name: Install tools
run: |
sudo apt-get update
sudo apt-get install -y shellcheck jq
- name: Packer FMT
run: packer fmt -recursive .
- name: Packer Validate
run: packer validate "${PACKER_TEMPLATE_ROOT}"
- name: gitleaks Scan
uses: gitleaks/gitleaks-action@v2
with:
args: detect --no-git -v
##########################################################################
# Stage 2 — Build Golden Image
##########################################################################
build:
name: Build Golden AMI
runs-on: ubuntu-latest
needs: lint
outputs:
ami_id: ${{ steps.packer_build.outputs.ami_id }}
strategy:
fail-fast: false
matrix:
include:
- edition: base
ubuntu_version: "2204"
cpu_arch: amd64
- edition: base
ubuntu_version: "2204"
cpu_arch: arm64
steps:
- uses: actions/checkout@v4
# must be step-level to allow matrix.*
- name: Skip matrix items not requested
if: >
github.event_name == 'schedule' ||
(
github.event_name == 'workflow_dispatch' &&
github.event.inputs.edition == matrix.edition &&
github.event.inputs.ubuntu_version == matrix.ubuntu_version &&
github.event.inputs.cpu_arch == matrix.cpu_arch
)
run: echo "Matrix item selected."
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ env.BASE_REGION }}
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-access-key-id: ${{ secrets.AWS_ROOT_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_ROOT_SECRET_ACCESS_KEY }}
mask-aws-account-id: true
- name: Setup Packer
uses: hashicorp/setup-packer@v3
- name: Build AMI
id: packer_build
env:
EDITION: ${{ matrix.edition }}
UBUNTU_VERSION: ${{ matrix.ubuntu_version }}
CPU_ARCH: ${{ matrix.cpu_arch }}
run: |
TEMPLATE="${PACKER_TEMPLATE_ROOT}/${EDITION}/ubuntu-${UBUNTU_VERSION}-${EDITION}.pkr.hcl"
echo "Using template: ${TEMPLATE}"
packer build \
-color=false \
-var "cpu_arch=${CPU_ARCH}" \
-var "edition=${EDITION}" \
-var "ubuntu_version=${UBUNTU_VERSION}" \
"${TEMPLATE}" | tee packer.log
AMI_ID=$(grep 'AMI:' packer.log | awk '{print $2}' | tail -n1 || true)
if [ -z "${AMI_ID}" ]; then
echo "ERROR: Cannot parse AMI ID"
exit 1
fi
echo "ami_id=${AMI_ID}" >> "${GITHUB_OUTPUT}"
- name: Upload Logs
uses: actions/upload-artifact@v4
with:
name: packer-build-log
path: packer.log
##########################################################################
# Stage 3 — QA Test
##########################################################################
test:
name: Test Built AMI
runs-on: ubuntu-latest
needs: build
# must re-expose build's output for downstream needs.*
outputs:
ami_id: ${{ needs.build.outputs.ami_id }}
steps:
- name: Placeholder test
run: |
echo "TODO: Future QA test"
##########################################################################
# Stage 4 — AMI Replication + Retention
##########################################################################
distribute:
name: Replicate & Retain AMI
runs-on: ubuntu-latest
needs: test
strategy:
matrix:
include:
- edition: base
ubuntu_version: "2204"
cpu_arch: amd64
- edition: base
ubuntu_version: "2204"
cpu_arch: arm64
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ env.BASE_REGION }}
role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }}
aws-access-key-id: ${{ secrets.AWS_ROOT_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_ROOT_SECRET_ACCESS_KEY }}
- name: Distribute AMI
env:
BASE_REGION: ${{ env.BASE_REGION }}
TARGET_REGIONS: ${{ env.TARGET_REGIONS }}
PROJECT_TAG: ${{ env.PROJECT_TAG }}
EDITION: ${{ matrix.edition }}
UBUNTU_VERSION: ${{ matrix.ubuntu_version }}
CPU_ARCH: ${{ matrix.cpu_arch }}
AMI_ID: ${{ needs.test.outputs.ami_id }}
run: |
bash packer/scripts/common/ami-replicate.sh \
"${AMI_ID}" "${EDITION}" "${UBUNTU_VERSION}" "${CPU_ARCH}" \
"${BASE_REGION}" "${TARGET_REGIONS}" "${PROJECT_TAG}"
- name: Retention
env:
TARGET_REGIONS: ${{ env.TARGET_REGIONS }}
PROJECT_TAG: ${{ env.PROJECT_TAG }}
EDITION: ${{ matrix.edition }}
UBUNTU_VERSION: ${{ matrix.ubuntu_version }}
CPU_ARCH: ${{ matrix.cpu_arch }}
run: |
bash packer/scripts/common/ami-retention.sh \
"${EDITION}" "${UBUNTU_VERSION}" "${CPU_ARCH}" "${PROJECT_TAG}" "${TARGET_REGIONS}"

View File

@ -1,151 +0,0 @@
name: Build Offline APISIX Gateway Installer
on:
push:
paths:
- 'gitops/scripts/apisix-gateway/deploy-apisix-gateway.sh'
- '.github/workflows/offline-package-apisix-gateway.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-apisix-gateway-<run_number>"
required: false
type: string
chart_version:
description: "Override helm chart version for apisix (e.g., 1.2.3). Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write # 需要创建/上传 Release 资产
concurrency:
group: build-offline-apisix-gateway
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
chart_version: ${{ steps.resolve.outputs.chart_version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, helm)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
- name: Add helm repo
run: |
set -euo pipefail
helm repo add apisix https://charts.apiseven.com
helm repo update
- name: Resolve latest chart version
id: resolve
env:
OVERRIDE_CHART_VERSION: ${{ github.event.inputs.chart_version }}
run: |
set -euo pipefail
if [ -n "${OVERRIDE_CHART_VERSION}" ]; then
CHART_VERSION="${OVERRIDE_CHART_VERSION}"
else
CHART_VERSION=$(helm search repo apisix/apisix --versions | awk 'NR==2{print $2}')
fi
echo "chart_version=${CHART_VERSION}" >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
mkdir -p offline-installer/{images,charts,scripts,manifests}
- name: Download Gateway API manifests
run: |
set -euo pipefail
curl -L https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml -o offline-installer/manifests/gateway-api-standard-install.yaml
- name: Pull & export images from chart
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm template apisix apisix/apisix --version "${CHART_VERSION}" > manifest.yaml
images=$(grep -oP 'image:\s*\K[^\s]+' manifest.yaml | tr -d '"' | sort -u)
for img in $images; do
[ -z "$img" ] && continue
docker pull "$img"
safe=$(echo "$img" | tr '/:' '-_')
docker save "$img" -o offline-installer/images/${safe}.tar
done
- name: Download Helm chart (apisix/apisix ${{ steps.resolve.outputs.chart_version }})
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm pull apisix/apisix --version="${CHART_VERSION}" --untar --untardir offline-installer/charts
- name: Stage deployment script
run: |
set -euo pipefail
mkdir -p offline-installer/scripts
cp gitops/scripts/apisix-gateway/deploy-apisix-gateway.sh offline-installer/scripts/
- name: Pack offline installer
run: |
set -euo pipefail
tar -czvf offline-setup-apisix-gateway-${{ matrix.arch }}.tar.gz -C offline-installer .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-setup-apisix-gateway-${{ matrix.arch }}
path: offline-setup-apisix-gateway-${{ matrix.arch }}.tar.gz
publish-release:
needs: build-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-apisix-gateway-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-apisix-gateway-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-apisix-gateway-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-setup-apisix-gateway-amd64.tar.gz
release-artifacts/arm64/offline-setup-apisix-gateway-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,200 +0,0 @@
name: Build Offline Argo CD Installer
on:
push:
paths:
- '.github/workflows/offline-package-argocd-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v2.11.0). Leave empty to use offline-argocd-<run_number>"
required: false
type: string
chart_version:
description: "Override Helm chart version for argo/argo-cd. Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-argocd
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
env:
NERDCTL_VERSION: "2.0.3"
outputs:
chart_version: ${{ steps.resolve.outputs.chart_version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, helm)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
- name: Add helm repo
run: |
set -euo pipefail
helm repo add argo https://argoproj.github.io/argo-helm --force-update
helm repo update
- name: Resolve chart version
id: resolve
env:
OVERRIDE_CHART_VERSION: ${{ github.event.inputs.chart_version }}
run: bash scripts/offline-argocd/resolve_chart_version.sh
- name: Prepare directories
run: |
set -euo pipefail
rm -rf argocd-offline-package
mkdir -p argocd-offline-package/{images,charts,scripts,metadata}
- name: Stage installer script
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: bash scripts/offline-argocd/stage_installer.sh
- name: Download nerdctl binary for ${{ matrix.arch }}
run: |
set -euo pipefail
wget https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-${{ matrix.arch }}.tar.gz \
-O argocd-offline-package/nerdctl.tar.gz
- name: Pull & export required images
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
MATRIX_ARCH: ${{ matrix.arch }}
run: bash scripts/offline-argocd/pull_and_export_images.sh
- name: Download Helm chart
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm pull argo/argo-cd --version "${CHART_VERSION}" --untar --untardir argocd-offline-package/charts
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-argocd-${{ matrix.arch }}.tar.gz -C . argocd-offline-package
ls -lh offline-package-argocd-${{ matrix.arch }}.tar.gz
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-argocd-${{ matrix.arch }}
path: offline-package-argocd-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-argocd-${{ matrix.arch }}
path: offline-test
- name: Verify offline package integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-argocd-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-argocd-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/offline-package/argocd/
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-argocd-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-argocd-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-argocd-amd64.tar.gz
release-artifacts/arm64/offline-package-argocd-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: bash scripts/offline-argocd/rsync_release_assets.sh
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/argocd
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: bash scripts/offline-argocd/prune_remote_versions.sh

View File

@ -1,159 +0,0 @@
name: Build Offline AutoGen Studio Installer
on:
push:
paths:
- 'gitops/scripts/autogen-studio/**'
- '.github/workflows/offline-package-autogen-studio.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-autogen-studio-<run_number>"
required: false
type: string
autogen_tag:
description: "AutoGen Studio container tag (default: latest)"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-autogen-studio
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
autogen_tag: ${{ steps.resolve.outputs.autogen_tag }}
steps:
- uses: actions/checkout@v4
- name: Resolve image tag
id: resolve
env:
INPUT_AUTOGEN_TAG: ${{ github.event.inputs.autogen_tag }}
run: |
set -euo pipefail
AUTOGEN_TAG=${INPUT_AUTOGEN_TAG:-latest}
echo "autogen_tag=${AUTOGEN_TAG}" >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,scripts}
- name: Stage compose file and scripts
env:
AUTOGEN_TAG: ${{ steps.resolve.outputs.autogen_tag }}
run: |
set -euo pipefail
cp gitops/scripts/autogen-studio/docker-compose.yaml offline-installer/docker-compose.yaml
sed -i "s/__AUTOGEN_TAG__/${AUTOGEN_TAG}/g" offline-installer/docker-compose.yaml
cp gitops/scripts/autogen-studio/deploy-autogen-studio.sh offline-installer/scripts/
chmod +x offline-installer/scripts/deploy-autogen-studio.sh
cat <<'README' > offline-installer/README.md
# Offline AutoGen Studio Installer
This archive contains container images and helper assets for deploying AutoGen Studio with Docker Compose.
## Contents
- `docker-compose.yaml`: Reference deployment manifest configured for the packaged images.
- `images/`: Pre-pulled container images saved as tar archives.
- `scripts/deploy-autogen-studio.sh`: Helper script to load the images and manage the compose stack.
## Usage
1. Extract the archive on a host with Docker/nerdctl available.
2. (Optional) Run `IMAGE_LOAD_TOOL=nerdctl ./scripts/deploy-autogen-studio.sh load-images` to import the images with nerdctl.
3. Start the stack: `./scripts/deploy-autogen-studio.sh up`.
4. Access AutoGen Studio at http://localhost:9090.
Adjust the compose file as needed before running the helper script.
README
- name: Pull & export container images
env:
AUTOGEN_TAG: ${{ steps.resolve.outputs.autogen_tag }}
run: |
set -euo pipefail
image="autogenstudio/autogen-studio:${AUTOGEN_TAG}"
docker pull "$image"
safe=$(echo "$image" | tr '/:' '-_')
docker save "$image" -o "offline-installer/images/${safe}.tar"
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-autogen-studio-${{ matrix.arch }}.tar.gz -C offline-installer .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-autogen-studio-${{ matrix.arch }}
path: offline-package-autogen-studio-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-autogen-studio-${{ matrix.arch }}
path: offline-test
- name: Verify archive integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-autogen-studio-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-autogen-studio-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-autogen-studio-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-autogen-studio-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-autogen-studio-amd64.tar.gz
release-artifacts/arm64/offline-package-autogen-studio-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,192 +0,0 @@
name: Build Offline Dify Installer
on:
push:
paths:
- 'gitops/scripts/dify/**'
- '.github/workflows/offline-package-dify.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-dify-<run_number>"
required: false
type: string
dify_tag:
description: "Dify container tag (default: latest)"
required: false
type: string
postgres_tag:
description: "Postgres image tag (default: 15-alpine)"
required: false
type: string
redis_tag:
description: "Redis image tag (default: 7-alpine)"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-dify
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
dify_tag: ${{ steps.resolve.outputs.dify_tag }}
postgres_tag: ${{ steps.resolve.outputs.postgres_tag }}
redis_tag: ${{ steps.resolve.outputs.redis_tag }}
steps:
- uses: actions/checkout@v4
- name: Resolve image tags
id: resolve
env:
INPUT_DIFY_TAG: ${{ github.event.inputs.dify_tag }}
INPUT_POSTGRES_TAG: ${{ github.event.inputs.postgres_tag }}
INPUT_REDIS_TAG: ${{ github.event.inputs.redis_tag }}
run: |
set -euo pipefail
DIFY_TAG=${INPUT_DIFY_TAG:-latest}
POSTGRES_TAG=${INPUT_POSTGRES_TAG:-15-alpine}
REDIS_TAG=${INPUT_REDIS_TAG:-7-alpine}
{
echo "dify_tag=${DIFY_TAG}"
echo "postgres_tag=${POSTGRES_TAG}"
echo "redis_tag=${REDIS_TAG}"
} >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,scripts}
- name: Stage compose file and scripts
env:
DIFY_TAG: ${{ steps.resolve.outputs.dify_tag }}
POSTGRES_TAG: ${{ steps.resolve.outputs.postgres_tag }}
REDIS_TAG: ${{ steps.resolve.outputs.redis_tag }}
run: |
set -euo pipefail
cp gitops/scripts/dify/docker-compose.yaml offline-installer/docker-compose.yaml
sed -i "s/__DIFY_TAG__/${DIFY_TAG}/g" offline-installer/docker-compose.yaml
sed -i "s/__POSTGRES_TAG__/${POSTGRES_TAG}/g" offline-installer/docker-compose.yaml
sed -i "s/__REDIS_TAG__/${REDIS_TAG}/g" offline-installer/docker-compose.yaml
cp gitops/scripts/dify/deploy-dify.sh offline-installer/scripts/
chmod +x offline-installer/scripts/deploy-dify.sh
cat <<'README' > offline-installer/README.md
# Offline Dify Installer
This archive contains container images and helper assets for deploying Dify with Docker Compose.
## Contents
- `docker-compose.yaml`: Reference deployment manifest configured for the packaged images.
- `images/`: Pre-pulled container images saved as tar archives.
- `scripts/deploy-dify.sh`: Helper script to load the images and manage the compose stack.
## Usage
1. Extract the archive on a host with Docker/nerdctl available.
2. (Optional) Run `IMAGE_LOAD_TOOL=nerdctl ./scripts/deploy-dify.sh load-images` to import the images with nerdctl.
3. Start the stack: `./scripts/deploy-dify.sh up`.
4. Access the Dify web UI at http://localhost:8080.
Adjust the compose file as needed before running the helper script.
README
- name: Pull & export container images
env:
DIFY_TAG: ${{ steps.resolve.outputs.dify_tag }}
POSTGRES_TAG: ${{ steps.resolve.outputs.postgres_tag }}
REDIS_TAG: ${{ steps.resolve.outputs.redis_tag }}
run: |
set -euo pipefail
images=(
"langgenius/dify-api:${DIFY_TAG}"
"langgenius/dify-worker:${DIFY_TAG}"
"langgenius/dify-web:${DIFY_TAG}"
"langgenius/dify-nginx:${DIFY_TAG}"
"postgres:${POSTGRES_TAG}"
"redis:${REDIS_TAG}"
)
for image in "${images[@]}"; do
docker pull "$image"
safe=$(echo "$image" | tr '/:' '-_')
docker save "$image" -o "offline-installer/images/${safe}.tar"
done
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-dify-${{ matrix.arch }}.tar.gz -C offline-installer .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-dify-${{ matrix.arch }}
path: offline-package-dify-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-dify-${{ matrix.arch }}
path: offline-test
- name: Verify archive integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-dify-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-dify-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-dify-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-dify-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-dify-amd64.tar.gz
release-artifacts/arm64/offline-package-dify-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,159 +0,0 @@
name: Build Offline Flowise Installer
on:
push:
paths:
- 'gitops/scripts/flowise/**'
- '.github/workflows/offline-package-flowise.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-flowise-<run_number>"
required: false
type: string
flowise_tag:
description: "Flowise container tag (default: latest)"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-flowise
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
flowise_tag: ${{ steps.resolve.outputs.flowise_tag }}
steps:
- uses: actions/checkout@v4
- name: Resolve image tag
id: resolve
env:
INPUT_FLOWISE_TAG: ${{ github.event.inputs.flowise_tag }}
run: |
set -euo pipefail
FLOWISE_TAG=${INPUT_FLOWISE_TAG:-latest}
echo "flowise_tag=${FLOWISE_TAG}" >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,scripts}
- name: Stage compose file and scripts
env:
FLOWISE_TAG: ${{ steps.resolve.outputs.flowise_tag }}
run: |
set -euo pipefail
cp gitops/scripts/flowise/docker-compose.yaml offline-installer/docker-compose.yaml
sed -i "s/__FLOWISE_TAG__/${FLOWISE_TAG}/g" offline-installer/docker-compose.yaml
cp gitops/scripts/flowise/deploy-flowise.sh offline-installer/scripts/
chmod +x offline-installer/scripts/deploy-flowise.sh
cat <<'README' > offline-installer/README.md
# Offline Flowise Installer
This archive contains container images and helper assets for deploying Flowise with Docker Compose.
## Contents
- `docker-compose.yaml`: Reference deployment manifest configured for the packaged images.
- `images/`: Pre-pulled container images saved as tar archives.
- `scripts/deploy-flowise.sh`: Helper script to load the images and manage the compose stack.
## Usage
1. Extract the archive on a host with Docker/nerdctl available.
2. (Optional) Run `IMAGE_LOAD_TOOL=nerdctl ./scripts/deploy-flowise.sh load-images` to import the images with nerdctl.
3. Start the stack: `./scripts/deploy-flowise.sh up`.
4. Access the Flowise UI at http://localhost:3000.
Adjust the compose file as needed before running the helper script.
README
- name: Pull & export container images
env:
FLOWISE_TAG: ${{ steps.resolve.outputs.flowise_tag }}
run: |
set -euo pipefail
image="flowiseai/flowise:${FLOWISE_TAG}"
docker pull "$image"
safe=$(echo "$image" | tr '/:' '-_')
docker save "$image" -o "offline-installer/images/${safe}.tar"
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-flowise-${{ matrix.arch }}.tar.gz -C offline-installer .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-flowise-${{ matrix.arch }}
path: offline-package-flowise-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-flowise-${{ matrix.arch }}
path: offline-test
- name: Verify archive integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-flowise-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-flowise-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-flowise-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-flowise-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-flowise-amd64.tar.gz
release-artifacts/arm64/offline-package-flowise-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,98 +0,0 @@
name: Build Offline FluxCD Installer
on:
push:
paths:
- '.github/workflows/offline-package-fluxcd-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v2.2.0). Leave empty to use offline-fluxcd-<run_number>"
required: false
type: string
chart_version:
description: "Override Helm chart version for fluxcd-community/flux2. Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-fluxcd
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
env:
NERDCTL_VERSION: "2.0.3"
outputs:
chart_version: ${{ steps.resolve.outputs.chart_version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, helm)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
- name: Add helm repo
run: |
set -euo pipefail
helm repo add fluxcd-community https://fluxcd-community.github.io/helm-charts --force-update
helm repo update
- name: Resolve chart version
id: resolve
env:
OVERRIDE_CHART_VERSION: ${{ github.event.inputs.chart_version }}
run: script/offline-fluxcd/resolve-chart-version.sh
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,charts,scripts,metadata}
- name: Stage installer script
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: script/offline-fluxcd/stage-installer.sh
- name: Download nerdctl binary for ${{ matrix.arch }}
run: |
set -euo pipefail
wget https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-${{ matrix.arch }}.tar.gz \
-O offline-installer/nerdctl.tar.gz
- name: Pull & export required images
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
MATRIX_ARCH: ${{ matrix.arch }}
run: script/offline-fluxcd/pull-and-export-images.sh
- name: Download Helm chart
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm pull fluxcd-community/flux2 --version "${CHART_VERSION}" --untar --untardir offline-installer/charts
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-setup-fluxcd-${{ matrix.arch }}.tar.gz -C offline-installer .
ls -lh offline-setup-fluxcd-${{ matrix.arch }}.tar.gz
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-setup-fluxcd-${{ matrix.arch }}
path: offline-setup-fluxcd-${{ matrix.arch }}.tar.gz

View File

@ -1,302 +0,0 @@
name: Build Offline Gitea Installer
on:
push:
paths:
- '.github/workflows/offline-package-gitea-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v1.21.10). Leave empty to use offline-gitea-<run_number>"
required: false
type: string
chart_version:
description: "Override Helm chart version for gitea-charts/gitea. Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-gitea
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
env:
NERDCTL_VERSION: "2.0.3"
OFFLINE_DIR: gitea-offline-package
outputs:
chart_version: ${{ steps.resolve.outputs.chart_version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, helm)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
- name: Add helm repo
run: |
set -euo pipefail
helm repo add gitea-charts https://dl.gitea.io/charts/ --force-update
helm repo update
- name: Resolve chart version
id: resolve
env:
OVERRIDE_CHART_VERSION: ${{ github.event.inputs.chart_version }}
run: |
set -euo pipefail
if [ -n "${OVERRIDE_CHART_VERSION}" ]; then
CHART_VERSION="${OVERRIDE_CHART_VERSION}"
else
CHART_VERSION=$(helm search repo gitea-charts/gitea --versions | awk 'NR==2{print $2}')
fi
echo "chart_version=${CHART_VERSION}" >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
rm -rf "${OFFLINE_DIR}"
mkdir -p "${OFFLINE_DIR}"/{images,charts,metadata}
- name: Stage installer script
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
cat <<'SCRIPT' > "${OFFLINE_DIR}/install-gitea.sh"
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CHART_DIR="${ROOT_DIR}/charts/gitea"
IMAGES_DIR="${ROOT_DIR}/images"
RELEASE_NAME="${RELEASE_NAME:-gitea}"
NAMESPACE="${NAMESPACE:-gitea}"
if command -v nerdctl >/dev/null 2>&1; then
LOADER="nerdctl"
elif command -v docker >/dev/null 2>&1; then
LOADER="docker"
else
echo "Either docker or nerdctl is required to load images." >&2
exit 1
fi
for tar in "${IMAGES_DIR}"/*.tar; do
[ -f "$tar" ] || continue
echo "Loading image: $tar"
"$LOADER" load -i "$tar"
done
echo "Installing/Upgrading Gitea release ${RELEASE_NAME} in namespace ${NAMESPACE}"
helm upgrade --install "${RELEASE_NAME}" "${CHART_DIR}" \
--namespace "${NAMESPACE}" \
--create-namespace \
"$@"
SCRIPT
chmod +x "${OFFLINE_DIR}/scripts/install-gitea.sh"
cat <<'ROOTSCRIPT' > "${OFFLINE_DIR}/install-gitea.sh"
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec "${SCRIPT_DIR}/scripts/install-gitea.sh" "$@"
ROOTSCRIPT
chmod +x "${OFFLINE_DIR}/install-gitea.sh"
cat <<EOFMETA > "${OFFLINE_DIR}/metadata/INFO"
chart: gitea-charts/gitea
chart_version: ${CHART_VERSION}
created_at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
EOFMETA
- name: Download nerdctl binary for ${{ matrix.arch }}
run: |
set -euo pipefail
wget https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-${{ matrix.arch }}.tar.gz \
-O "${OFFLINE_DIR}/nerdctl.tar.gz"
- name: Pull & export required images
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
PLATFORM="linux/${{ matrix.arch }}"
helm template gitea gitea-charts/gitea --version "${CHART_VERSION}" > manifest.yaml
mapfile -t images < <(grep -oP 'image:\s*"?\K([^"\s]+)' manifest.yaml | sort -u || true)
rm -f manifest.yaml
for img in "${images[@]}"; do
[ -n "$img" ] || continue
if [[ "$img" == *"{{"* ]]; then
continue
fi
echo "Pulling $img for ${PLATFORM}"
if ! docker pull --platform "${PLATFORM}" "$img"; then
echo "::warning::Failed to pull $img for ${PLATFORM}, skipping" >&2
continue
fi
safe=$(echo "$img" | tr '/:' '-_')
docker save "$img" -o "${OFFLINE_DIR}/images/${safe}.tar"
done
- name: Download Helm chart
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm pull gitea-charts/gitea --version "${CHART_VERSION}" --untar --untardir "${OFFLINE_DIR}/charts"
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-gitea-${{ matrix.arch }}.tar.gz "${OFFLINE_DIR}"
ls -lh offline-package-gitea-${{ matrix.arch }}.tar.gz
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-gitea-${{ matrix.arch }}
path: offline-package-gitea-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-gitea-${{ matrix.arch }}
path: offline-test
- name: Verify offline package integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-gitea-${{ matrix.arch }}.tar.gz > /dev/null
- name: Verify install script layout
run: |
set -euo pipefail
cd offline-test
tar -xvpf offline-package-gitea-${{ matrix.arch }}.tar.gz
test -f gitea-offline-package/install-gitea.sh
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-gitea-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/gitea
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-gitea-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-gitea-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-gitea-amd64.tar.gz
release-artifacts/arm64/offline-package-gitea-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG_NAME}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
release-artifacts/amd64/offline-package-gitea-amd64.tar.gz \
release-artifacts/arm64/offline-package-gitea-arm64.tar.gz \
"${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/gitea
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=3
mapfile -t all < <(ls -1 | grep -E "^(offline-gitea-|v[0-9]+\.)" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,219 +0,0 @@
name: Build Offline GitLab Installer
on:
push:
paths:
- '.github/workflows/offline-package-gitlab-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v16.11.0). Leave empty to use offline-gitlab-<run_number>"
required: false
type: string
chart_version:
description: "Override Helm chart version for gitlab/gitlab. Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-gitlab
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
env:
NERDCTL_VERSION: "2.0.3"
outputs:
chart_version: ${{ steps.resolve.outputs.chart_version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, helm)
run: script/install-offline-gitlab-deps.sh
- name: Add helm repo
run: |
set -euo pipefail
helm repo add gitlab https://charts.gitlab.io --force-update
helm repo update
- name: Resolve chart version
id: resolve
env:
OVERRIDE_CHART_VERSION: ${{ github.event.inputs.chart_version }}
run: script/resolve-gitlab-chart-version.sh
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,charts,scripts,metadata}
- name: Stage installer script
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: script/stage-gitlab-offline-installer.sh
- name: Download nerdctl binary for ${{ matrix.arch }}
run: |
set -euo pipefail
wget https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-${{ matrix.arch }}.tar.gz \
-O offline-installer/nerdctl.tar.gz
- name: Pull & export required images
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
ARCH: ${{ matrix.arch }}
run: script/pull-and-export-gitlab-images.sh
- name: Download Helm chart
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm pull gitlab/gitlab --version "${CHART_VERSION}" --untar --untardir offline-installer/charts
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-setup-gitlab-${{ matrix.arch }}.tar.gz -C offline-installer .
ls -lh offline-setup-gitlab-${{ matrix.arch }}.tar.gz
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-setup-gitlab-${{ matrix.arch }}
path: offline-setup-gitlab-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-gitlab-${{ matrix.arch }}
path: offline-test
- name: Verify offline package integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-setup-gitlab-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-gitlab-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/gitlab
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-gitlab-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-gitlab-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-setup-gitlab-amd64.tar.gz
release-artifacts/arm64/offline-setup-gitlab-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG_NAME}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
release-artifacts/amd64/offline-setup-gitlab-amd64.tar.gz \
release-artifacts/arm64/offline-setup-gitlab-arm64.tar.gz \
"${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/gitlab
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=3
mapfile -t all < <(ls -1 | grep -E "^(offline-gitlab-|v[0-9]+\.)" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,207 +0,0 @@
name: Build Offline K3s Installer
on:
push:
paths:
- 'scripts/resolve_k3s_versions.sh'
- 'scripts/make_k3s_offline_package.sh'
- '.github/workflows/offline-package-k3s-installer.yaml'
workflow_dispatch:
env:
NERDCTL_VERSION: "2.1.4"
jobs:
build-k3s-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
k3s_version: ${{ steps.resolve.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Prepare toolchain (curl/jq/tar/tree + nerdctl)
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y curl jq tar tree
NURL="https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz"
TGZ="/tmp/nerdctl-${NERDCTL_VERSION}.tgz"
echo "Downloading: ${NURL}"
curl -fSL --retry 3 --retry-connrefused --connect-timeout 15 "${NURL}" -o "${TGZ}"
# 可选校验:如需严格校验,取消下面两行的注释
# curl -fSL "${NURL}.sha256" -o "${TGZ}.sha256"
# (cd /tmp && sha256sum -c "$(basename ${TGZ}).sha256") || { echo "SHA256 mismatch"; exit 1; }
sudo tar -C /usr/local/bin -xzf "${TGZ}"
sudo chmod +x /usr/local/bin/nerdctl
sudo nerdctl --version
- name: Resolve latest k3s version
id: resolve
run: |
set -euo pipefail
bash scripts/resolve_k3s_versions.sh
- name: Build offline package
env:
K3S_VERSION: ${{ steps.resolve.outputs.version }}
ARCH: ${{ matrix.arch }}
run: |
set -euo pipefail
chmod +x scripts/make_k3s_offline_package.sh
./scripts/make_k3s_offline_package.sh
- name: Validate airgap image tar
run: |
set -euo pipefail
TAR="k3s-offline-package/images/k3s-airgap-images-${{ matrix.arch }}.tar"
tmp=$(mktemp -d)
tar -xf "$TAR" -C "$tmp" manifest.json
configs=$(jq -r '.[].Config' "$tmp/manifest.json")
for cfg in $configs; do
tar -xf "$TAR" -C "$tmp" "$cfg"
arch=$(jq -r '.architecture' "$tmp/$cfg")
if [ "$arch" != "${{ matrix.arch }}" ]; then
echo "unexpected arch $arch in $cfg" >&2
exit 1
fi
done
rm -rf "$tmp"
- name: Pack final installer
run: |
set -euo pipefail
# 假设脚本产出目录为 k3s-offline-package
test -d k3s-offline-package
tar czf offline-package-k3s-installer-${{ matrix.arch }}.tar.gz k3s-offline-package
ls -lh offline-package-k3s-installer-${{ matrix.arch }}.tar.gz
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-k3s-installer-${{ matrix.arch }}
path: offline-package-k3s-installer-${{ matrix.arch }}.tar.gz
if-no-files-found: error
test-k3s-installer:
needs: build-k3s-installer
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
steps:
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: offline-package-k3s-installer-${{ matrix.arch }}
path: ./test-dir
- name: Extract Package
run: |
cd test-dir
tar -xzvf offline-package-k3s-installer-${{ matrix.arch }}.tar.gz
- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq
- name: Verify k3s binary arch
run: |
set -euo pipefail
BIN="test-dir/k3s-offline-package/bin/k3s-${{ matrix.arch }}"
file "$BIN"
if [ "${{ matrix.arch }}" = "amd64" ]; then
file "$BIN" | grep -q 'x86-64'
else
file "$BIN" | grep -q 'aarch64'
fi
- name: Verify image platforms
run: |
set -euo pipefail
TAR="test-dir/k3s-offline-package/images/k3s-airgap-images-${{ matrix.arch }}.tar"
tmp=$(mktemp -d)
tar -xf "$TAR" -C "$tmp" manifest.json
configs=$(jq -r '.[].Config' "$tmp/manifest.json")
for cfg in $configs; do
tar -xf "$TAR" -C "$tmp" "$cfg"
arch=$(jq -r '.architecture' "$tmp/$cfg")
if [ "$arch" != "${{ matrix.arch }}" ]; then
echo "unexpected arch $arch in $cfg" >&2
exit 1
fi
done
- name: Setup K3s and Test
if: matrix.arch == 'amd64'
run: |
cd test-dir/k3s-offline-package
bash install-server.sh
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl get nodes
KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl get pods -A
publish-release:
needs: test-k3s-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-k3s-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/nginx-ingress
steps:
- uses: actions/checkout@v4
- name: Create GitHub Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Daily Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 Artifact
uses: actions/download-artifact@v4
with:
name: offline-package-k3s-installer-amd64
path: release-artifacts
- name: Download arm64 Artifact
uses: actions/download-artifact@v4
with:
name: offline-package-k3s-installer-arm64
path: release-artifacts
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/offline-package-k3s-installer-amd64.tar.gz
release-artifacts/offline-package-k3s-installer-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Prune old releases (keep last 3)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
releases=$(gh release list --limit 100 --json tagName,createdAt --jq 'sort_by(.createdAt) | reverse | .[3:] | .[].tagName')
if [[ -n "$releases" ]]; then
for tag in $releases; do
gh release delete "$tag" -y
done
fi

View File

@ -1,169 +0,0 @@
name: Build Offline Kong Gateway Installer
on:
push:
paths:
- 'gitops/scripts/kong-gateway/deploy-kong-gateway.sh'
- 'gitops/scripts/kong-gateway/configure-example-app.sh'
- '.github/workflows/offline-package-kong-gateway.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-kong-gateway-<run_number>"
required: false
type: string
gateway_tag:
description: "Kong Gateway image tag. Default: 3.7"
required: false
type: string
kic_tag:
description: "Kubernetes Ingress Controller image tag. Default: 3.2"
required: false
type: string
chart_version:
description: "Override helm chart version for kong/ingress"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-kong-gateway
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
env:
GATEWAY_TAG: ${{ github.event.inputs.gateway_tag || '3.7' }}
KIC_TAG: ${{ github.event.inputs.kic_tag || '3.2' }}
CHART_VERSION: ${{ github.event.inputs.chart_version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, helm)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
- name: Add helm repo
run: |
set -euo pipefail
helm repo add kong https://charts.konghq.com
helm repo update
- name: Prepare directories
run: |
set -euo pipefail
mkdir -p offline-installer/{images,charts,scripts,bin}
- name: Stage scripts
run: |
set -euo pipefail
cp gitops/scripts/kong-gateway/deploy-kong-gateway.sh offline-installer/scripts/
cp gitops/scripts/kong-gateway/configure-example-app.sh offline-installer/scripts/
chmod +x offline-installer/scripts/deploy-kong-gateway.sh offline-installer/scripts/configure-example-app.sh
- name: Download nerdctl binary for ${{ matrix.arch }}
run: |
set -euo pipefail
wget https://github.com/containerd/nerdctl/releases/download/v2.0.3/nerdctl-2.0.3-linux-${{ matrix.arch }}.tar.gz -O offline-installer/nerdctl.tar.gz
- name: Pull & export required images
run: |
set -euo pipefail
docker pull "kong/kong-gateway:${GATEWAY_TAG}"
docker pull "kong/kubernetes-ingress-controller:${KIC_TAG}"
docker save "kong/kong-gateway:${GATEWAY_TAG}" -o offline-installer/images/kong-gateway.tar
docker save "kong/kubernetes-ingress-controller:${KIC_TAG}" -o offline-installer/images/kic.tar
- name: Download Helm Chart (kong/ingress)
run: |
set -euo pipefail
if [ -n "${CHART_VERSION}" ]; then
helm pull kong/ingress --version="${CHART_VERSION}" --untar --untardir offline-installer/charts
else
helm pull kong/ingress --untar --untardir offline-installer/charts
fi
- name: Create offline package
run: |
set -euo pipefail
tar -C offline-installer -czf offline-setup-kong-gateway-${{ matrix.arch }}.tar.gz .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-setup-kong-gateway-${{ matrix.arch }}
path: offline-setup-kong-gateway-${{ matrix.arch }}.tar.gz
test-release:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download offline installer artifact for ${{ matrix.arch }}
uses: actions/download-artifact@v4
with:
name: offline-setup-kong-gateway-${{ matrix.arch }}
path: offline-test
- name: Verify offline package
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-setup-kong-gateway-${{ matrix.arch }}.tar.gz > /dev/null
cd ..
publish-release:
needs: test-release
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-kong-gateway-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-kong-gateway-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-kong-gateway-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-setup-kong-gateway-amd64.tar.gz
release-artifacts/arm64/offline-setup-kong-gateway-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,177 +0,0 @@
name: Build Offline n8n Installer
on:
push:
paths:
- 'gitops/scripts/n8n/**'
- '.github/workflows/offline-package-n8n.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-n8n-<run_number>"
required: false
type: string
n8n_tag:
description: "n8n container tag (default: latest)"
required: false
type: string
postgres_tag:
description: "Postgres image tag (default: 15-alpine)"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-n8n
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
n8n_tag: ${{ steps.resolve.outputs.n8n_tag }}
postgres_tag: ${{ steps.resolve.outputs.postgres_tag }}
steps:
- uses: actions/checkout@v4
- name: Resolve image tags
id: resolve
env:
INPUT_N8N_TAG: ${{ github.event.inputs.n8n_tag }}
INPUT_POSTGRES_TAG: ${{ github.event.inputs.postgres_tag }}
run: |
set -euo pipefail
N8N_TAG=${INPUT_N8N_TAG:-latest}
POSTGRES_TAG=${INPUT_POSTGRES_TAG:-15-alpine}
{
echo "n8n_tag=${N8N_TAG}"
echo "postgres_tag=${POSTGRES_TAG}"
} >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,scripts}
- name: Stage compose file and scripts
env:
N8N_TAG: ${{ steps.resolve.outputs.n8n_tag }}
POSTGRES_TAG: ${{ steps.resolve.outputs.postgres_tag }}
run: |
set -euo pipefail
cp gitops/scripts/n8n/docker-compose.yaml offline-installer/docker-compose.yaml
sed -i "s/__N8N_TAG__/${N8N_TAG}/g" offline-installer/docker-compose.yaml
sed -i "s/__POSTGRES_TAG__/${POSTGRES_TAG}/g" offline-installer/docker-compose.yaml
cp gitops/scripts/n8n/deploy-n8n.sh offline-installer/scripts/
chmod +x offline-installer/scripts/deploy-n8n.sh
cat <<'README' > offline-installer/README.md
# Offline n8n Installer
This archive contains container images and helper assets for deploying n8n with Docker Compose.
## Contents
- `docker-compose.yaml`: Reference deployment manifest configured for the packaged images.
- `images/`: Pre-pulled container images saved as tar archives.
- `scripts/deploy-n8n.sh`: Helper script to load the images and manage the compose stack.
## Usage
1. Extract the archive on a host with Docker/nerdctl available.
2. (Optional) Run `IMAGE_LOAD_TOOL=nerdctl ./scripts/deploy-n8n.sh load-images` to import the images with nerdctl.
3. Start the stack: `./scripts/deploy-n8n.sh up`.
4. Access the n8n UI at http://localhost:5678.
Adjust the compose file as needed before running the helper script.
README
- name: Pull & export container images
env:
N8N_TAG: ${{ steps.resolve.outputs.n8n_tag }}
POSTGRES_TAG: ${{ steps.resolve.outputs.postgres_tag }}
run: |
set -euo pipefail
images=(
"n8nio/n8n:${N8N_TAG}"
"postgres:${POSTGRES_TAG}"
)
for image in "${images[@]}"; do
docker pull "$image"
safe=$(echo "$image" | tr '/:' '-_')
docker save "$image" -o "offline-installer/images/${safe}.tar"
done
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-n8n-${{ matrix.arch }}.tar.gz -C offline-installer .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-n8n-${{ matrix.arch }}
path: offline-package-n8n-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-n8n-${{ matrix.arch }}
path: offline-test
- name: Verify archive integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-n8n-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-n8n-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-n8n-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-n8n-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-n8n-amd64.tar.gz
release-artifacts/arm64/offline-package-n8n-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,289 +0,0 @@
name: Build Offline Nginx Ingress Installer
on:
push:
paths:
- 'scripts/ingress-installer.sh'
- 'scripts/resolve_nginx_ingress_versions.sh'
- '.github/workflows/offline-package-nginx-ingress.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.133.0). Leave empty to use offline-nginx-ingress-<run_number>"
required: false
type: string
image_tag:
description: "Override image tag for nginx/nginx-ingress (e.g., 5.1.1). Leave empty to auto-resolve"
required: false
type: string
chart_version:
description: "Override helm chart version for nginx-stable/nginx-ingress (e.g., 1.2.3). Leave empty to auto-resolve"
required: false
type: string
major_whitelist:
description: "Allowed major versions for image tag (regex alternation). Default: 5 (i.e., ^5\\.)"
required: false
type: string
default: "5"
permissions:
contents: write # 需要创建/上传 Release 资产
concurrency:
group: build-offline-nginx-ingress
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
image_tag: ${{ steps.resolve.outputs.image_tag }}
chart_version: ${{ steps.resolve.outputs.chart_version }}
artifact-name: ${{ steps.upload-artifact.outputs.artifact-name }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, helm)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
- name: Add helm repo
run: |
set -euo pipefail
helm repo add nginx-stable https://helm.nginx.com/stable
helm repo update
- name: Resolve latest versions
id: resolve
env:
OVERRIDE_IMAGE_TAG: ${{ github.event.inputs.image_tag }}
OVERRIDE_CHART_VERSION: ${{ github.event.inputs.chart_version }}
MAJOR_WHITELIST: ${{ github.event.inputs.major_whitelist }}
run: |
set -euo pipefail
bash scripts/resolve_nginx_ingress_versions.sh
- name: Prepare directories
run: |
set -euo pipefail
mkdir -p offline-installer/{images,charts,scripts,bin}
# 复制并“盖章”版本号到安装脚本
- name: Stage installer script and stamp resolved image tag
env:
IMG_TAG: ${{ steps.resolve.outputs.image_tag }}
run: |
set -euo pipefail
cp scripts/ingress-installer.sh offline-installer/scripts/
# 替换默认的 NGINX_IC_IMAGE 与 OCI_NGINX_REF 中的 2.4.0 为解析到的 ${IMG_TAG}
sed -i -E "s|(NGINX_IC_IMAGE:=nginx/nginx-ingress:)[0-9]+\.[0-9]+\.[0-9]+|\1${IMG_TAG}|" offline-installer/scripts/ingress-installer.sh
sed -i -E "s|(OCI_NGINX_REF:=nginx-ingress-)[0-9]+\.[0-9]+\.[0-9]+|\1${IMG_TAG}|" offline-installer/scripts/ingress-installer.sh
chmod +x offline-installer/scripts/ingress-installer.sh
- name: Download nerdctl binary for ${{ matrix.arch }}
run: |
set -euo pipefail
wget https://github.com/containerd/nerdctl/releases/download/v2.0.3/nerdctl-2.0.3-linux-${{ matrix.arch }}.tar.gz \
-O offline-installer/nerdctl.tar.gz
- name: Pull & export required images (nginx/nginx-ingress:${{ steps.resolve.outputs.image_tag }})
env:
IMG_TAG: ${{ steps.resolve.outputs.image_tag }}
run: |
set -euo pipefail
docker pull "nginx/nginx-ingress:${IMG_TAG}"
docker pull "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407"
docker save "nginx/nginx-ingress:${IMG_TAG}" \
-o offline-installer/images/nginx-ingress.tar
docker save "registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20230407" \
-o offline-installer/images/kube-webhook-certgen.tar
- name: Download Helm Chart (nginx-stable/nginx-ingress ${{ steps.resolve.outputs.chart_version }})
env:
CHART_VERSION: ${{ steps.resolve.outputs.chart_version }}
run: |
set -euo pipefail
helm pull nginx-stable/nginx-ingress --version="${CHART_VERSION}" --untar --untardir offline-installer/charts
- name: Package offline installer
run: |
set -euo pipefail
cd offline-installer
tar czvf ../offline-setup-nginx-ingress-${{ matrix.arch }}.tar.gz ./
cd ..
- name: Upload artifact
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: offline-setup-nginx-ingress-${{ matrix.arch }}
path: offline-setup-nginx-ingress-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download offline installer artifact for ${{ matrix.arch }}
uses: actions/download-artifact@v4
with:
name: offline-setup-nginx-ingress-${{ matrix.arch }}
path: offline-test
- name: Setup K3s and KUBECONFIG for user
run: |
set -euo pipefail
curl -sfL https://get.k3s.io | sudo sh -
mkdir -p $HOME/.kube
sudo cp /etc/rancher/k3s/k3s.yaml $HOME/.kube/config
sudo chown $USER:$USER $HOME/.kube/config
sudo mkdir -p /root/.kube
sudo cp /etc/rancher/k3s/k3s.yaml /root/.kube/config
kubectl get nodes
kubectl version --client=true
- name: Install Helm
run: |
set -euo pipefail
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Load offline installer package
run: |
set -euo pipefail
cd offline-test
tar -xzvf offline-setup-nginx-ingress-${{ matrix.arch }}.tar.gz
sudo tar xzvf nerdctl.tar.gz -C /usr/local/bin/
docker load -i images/nginx-ingress.tar
docker load -i images/kube-webhook-certgen.tar
cd ..
- name: Run offline installer in K3S cluster
run: |
set -euo pipefail
cd offline-test
sudo bash scripts/ingress-installer.sh
sleep 10
sudo helm list -A
sudo kubectl -n ingress get pods
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-nginx-ingress-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/nginx-ingress
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-nginx-ingress-amd64
path: release-artifacts
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-setup-nginx-ingress-arm64
path: release-artifacts
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/offline-setup-nginx-ingress-amd64.tar.gz
release-artifacts/offline-setup-nginx-ingress-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# === Rsync 到远端 ===
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG_NAME}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
release-artifacts/offline-setup-nginx-ingress-amd64.tar.gz \
release-artifacts/offline-setup-nginx-ingress-arm64.tar.gz \
"${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/nginx-ingress
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=3
# 同时兼容两类目录命名offline-nginx-ingress-* 和 v<semver>
mapfile -t all < <(ls -1 | grep -E "^(offline-nginx-ingress-|v[0-9]+\.)" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,175 +0,0 @@
name: Build Offline Pulumi Installer
on:
push:
paths:
- '.github/workflows/offline-package-pulumi-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v3.127.0). Leave empty to use offline-pulumi-<run_number>"
required: false
type: string
pulumi_version:
description: "Override Pulumi version (e.g., 3.127.0). Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-pulumi
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, tar)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq tar
- name: Resolve Pulumi version
id: resolve
env:
OVERRIDE_VERSION: ${{ github.event.inputs.pulumi_version }}
run: script/resolve-pulumi-version.sh
- name: Build offline Pulumi package
env:
ARCH: ${{ matrix.arch }}
PULUMI_VERSION: ${{ steps.resolve.outputs.version }}
MATRIX_ARCH: ${{ matrix.arch }}
run: script/build-offline-pulumi-package.sh
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: pulumi-offline-package-${{ matrix.arch }}
path: offline-package-pulumi-${{ matrix.arch }}.tar.gz
if-no-files-found: error
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: pulumi-offline-package-${{ matrix.arch }}
path: ./test-dir
- name: Extract package
run: |
set -euo pipefail
cd test-dir
tar -xzvf offline-package-pulumi-${{ matrix.arch }}.tar.gz
- name: Verify Pulumi bundle
env:
ARCH: ${{ matrix.arch }}
PULUMI_VERSION: ${{ needs.build-offline-installer.outputs.version }}
MATRIX_ARCH: ${{ matrix.arch }}
run: script/verify-pulumi-bundle.sh
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-pulumi-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/pulumi
steps:
- uses: actions/checkout@v4
- name: Create GitHub Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: pulumi-offline-package-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: pulumi-offline-package-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-pulumi-amd64.tar.gz
release-artifacts/arm64/offline-package-pulumi-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: script/rsync-release-assets.sh
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/pulumi
steps:
- uses: actions/checkout@v4
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: script/prune-remote-versions.sh

View File

@ -1,189 +0,0 @@
name: Build Offline RAGFlow Installer
on:
push:
paths:
- 'gitops/scripts/ragflow/**'
- '.github/workflows/offline-package-ragflow.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v0.1.0). Leave empty to use offline-ragflow-<run_number>"
required: false
type: string
ragflow_tag:
description: "RAGFlow container tag (default: latest)"
required: false
type: string
postgres_tag:
description: "Postgres image tag (default: 15-alpine)"
required: false
type: string
redis_tag:
description: "Redis image tag (default: 7-alpine)"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-ragflow
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
ragflow_tag: ${{ steps.resolve.outputs.ragflow_tag }}
postgres_tag: ${{ steps.resolve.outputs.postgres_tag }}
redis_tag: ${{ steps.resolve.outputs.redis_tag }}
steps:
- uses: actions/checkout@v4
- name: Resolve image tags
id: resolve
env:
INPUT_RAGFLOW_TAG: ${{ github.event.inputs.ragflow_tag }}
INPUT_POSTGRES_TAG: ${{ github.event.inputs.postgres_tag }}
INPUT_REDIS_TAG: ${{ github.event.inputs.redis_tag }}
run: |
set -euo pipefail
RAGFLOW_TAG=${INPUT_RAGFLOW_TAG:-latest}
POSTGRES_TAG=${INPUT_POSTGRES_TAG:-15-alpine}
REDIS_TAG=${INPUT_REDIS_TAG:-7-alpine}
{
echo "ragflow_tag=${RAGFLOW_TAG}"
echo "postgres_tag=${POSTGRES_TAG}"
echo "redis_tag=${REDIS_TAG}"
} >> "$GITHUB_OUTPUT"
- name: Prepare directories
run: |
set -euo pipefail
rm -rf offline-installer
mkdir -p offline-installer/{images,scripts}
- name: Stage compose file and scripts
env:
RAGFLOW_TAG: ${{ steps.resolve.outputs.ragflow_tag }}
POSTGRES_TAG: ${{ steps.resolve.outputs.postgres_tag }}
REDIS_TAG: ${{ steps.resolve.outputs.redis_tag }}
run: |
set -euo pipefail
cp gitops/scripts/ragflow/docker-compose.yaml offline-installer/docker-compose.yaml
sed -i "s/__RAGFLOW_TAG__/${RAGFLOW_TAG}/g" offline-installer/docker-compose.yaml
sed -i "s/__POSTGRES_TAG__/${POSTGRES_TAG}/g" offline-installer/docker-compose.yaml
sed -i "s/__REDIS_TAG__/${REDIS_TAG}/g" offline-installer/docker-compose.yaml
cp gitops/scripts/ragflow/deploy-ragflow.sh offline-installer/scripts/
chmod +x offline-installer/scripts/deploy-ragflow.sh
cat <<'README' > offline-installer/README.md
# Offline RAGFlow Installer
This archive contains container images and helper assets for deploying RAGFlow with Docker Compose.
## Contents
- `docker-compose.yaml`: Reference deployment manifest configured for the packaged images.
- `images/`: Pre-pulled container images saved as tar archives.
- `scripts/deploy-ragflow.sh`: Helper script to load the images and manage the compose stack.
## Usage
1. Extract the archive on a host with Docker/nerdctl available.
2. (Optional) Run `IMAGE_LOAD_TOOL=nerdctl ./scripts/deploy-ragflow.sh load-images` to import the images with nerdctl.
3. Start the stack: `./scripts/deploy-ragflow.sh up`.
4. Access the RAGFlow UI at http://localhost:3001.
Adjust the compose file as needed before running the helper script. Configure external vector stores or storage services as required by your deployment.
README
- name: Pull & export container images
env:
RAGFLOW_TAG: ${{ steps.resolve.outputs.ragflow_tag }}
POSTGRES_TAG: ${{ steps.resolve.outputs.postgres_tag }}
REDIS_TAG: ${{ steps.resolve.outputs.redis_tag }}
run: |
set -euo pipefail
images=(
"ragflow/ragflow:${RAGFLOW_TAG}"
"postgres:${POSTGRES_TAG}"
"redis:${REDIS_TAG}"
)
for image in "${images[@]}"; do
docker pull "$image"
safe=$(echo "$image" | tr '/:' '-_')
docker save "$image" -o "offline-installer/images/${safe}.tar"
done
- name: Package offline installer
run: |
set -euo pipefail
tar -czf offline-package-ragflow-${{ matrix.arch }}.tar.gz -C offline-installer .
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: offline-package-ragflow-${{ matrix.arch }}
path: offline-package-ragflow-${{ matrix.arch }}.tar.gz
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: offline-package-ragflow-${{ matrix.arch }}
path: offline-test
- name: Verify archive integrity
run: |
set -euo pipefail
cd offline-test
tar -tzf offline-package-ragflow-${{ matrix.arch }}.tar.gz > /dev/null
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-ragflow-{0}', github.run_number) }}
steps:
- uses: actions/checkout@v4
- name: Create Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-ragflow-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: offline-package-ragflow-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/offline-package-ragflow-amd64.tar.gz
release-artifacts/arm64/offline-package-ragflow-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,201 +0,0 @@
name: Build Offline Sealos Installer
on:
push:
paths:
- 'scripts/create-sealos-offline-package.sh'
- 'scripts/sealos-install.sh'
- 'scripts/cilium-values.yaml'
- 'scripts/resolve_sealos_versions.sh'
- '.github/workflows/offline-package-sealos-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v5.0.3). Leave empty to use offline-sealos-<run_number>"
required: false
type: string
sealos_version:
description: "Override Sealos version (e.g., 5.0.3). Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-sealos
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
sealos_version: ${{ steps.resolve.outputs.sealos_version }}
artifact-name: ${{ steps.upload-artifact.outputs.artifact-name }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq
- name: Resolve latest Sealos version
id: resolve
env:
OVERRIDE_SEALOS_VERSION: ${{ github.event.inputs.sealos_version }}
run: |
set -euo pipefail
bash scripts/resolve_sealos_versions.sh
- name: Run Offline Package Builder
env:
SEALOS_VERSION: ${{ steps.resolve.outputs.sealos_version }}
run: |
set -euo pipefail
chmod +x scripts/create-sealos-offline-package.sh
ARCH=${{ matrix.arch }} ./scripts/create-sealos-offline-package.sh
- name: Upload Artifact
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: sealos-offline-package-${{ matrix.arch }}
path: sealos-offline-package-${{ matrix.arch }}.tar.gz
if-no-files-found: error
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
steps:
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: sealos-offline-package-${{ matrix.arch }}
path: ./test-dir
- name: Extract Package
run: |
set -euo pipefail
cd test-dir
tar -xzvf sealos-offline-package-${{ matrix.arch }}.tar.gz
- name: Verify Package Contents
run: |
set -euo pipefail
test -f test-dir/sealos-offline-package/sealos-install.sh
test -f test-dir/sealos-offline-package/cilium-values.yaml
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-sealos-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/sealos
steps:
- uses: actions/checkout@v4
- name: Create GitHub Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: sealos-offline-package-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: sealos-offline-package-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/sealos-offline-package-amd64.tar.gz
release-artifacts/arm64/sealos-offline-package-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG_NAME}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
release-artifacts/amd64/sealos-offline-package-amd64.tar.gz \
release-artifacts/arm64/sealos-offline-package-arm64.tar.gz \
"${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/sealos
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=3
mapfile -t all < <(ls -1 | grep -E "^(offline-sealos-|v[0-9]+\.)" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,167 +0,0 @@
name: Build Offline Terraform Installer
on:
push:
paths:
- '.github/workflows/offline-package-terraform-installer.yaml'
workflow_dispatch:
inputs:
tag:
description: "Release tag to use/sync (e.g., v1.8.5). Leave empty to use offline-terraform-<run_number>"
required: false
type: string
terraform_version:
description: "Override Terraform version (e.g., 1.8.5). Leave empty to auto-resolve"
required: false
type: string
permissions:
contents: write
concurrency:
group: build-offline-terraform
cancel-in-progress: false
jobs:
build-offline-installer:
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
outputs:
version: ${{ steps.resolve.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Install deps (curl, jq, unzip)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y curl jq unzip
- name: Resolve Terraform version
id: resolve
env:
OVERRIDE_VERSION: ${{ github.event.inputs.terraform_version }}
run: script/resolve-terraform-version.sh
- name: Build offline Terraform package
env:
TERRAFORM_VERSION: ${{ steps.resolve.outputs.version }}
ARCH: ${{ matrix.arch }}
run: script/build-offline-terraform-package.sh
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: terraform-offline-package-${{ matrix.arch }}
path: terraform-offline-package-${{ matrix.arch }}.tar.gz
if-no-files-found: error
test-offline-installer:
needs: build-offline-installer
strategy:
matrix:
arch: [amd64, arm64]
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: terraform-offline-package-${{ matrix.arch }}
path: ./test-dir
- name: Extract package
run: |
set -euo pipefail
cd test-dir
tar -xzvf terraform-offline-package-${{ matrix.arch }}.tar.gz
- name: Verify Terraform bundle
env:
TERRAFORM_VERSION: ${{ needs.build-offline-installer.outputs.version }}
ARCH: ${{ matrix.arch }}
run: script/verify-terraform-bundle.sh
publish-release:
needs: test-offline-installer
runs-on: ubuntu-latest
env:
TAG_NAME: ${{ github.event.inputs.tag != '' && github.event.inputs.tag || format('offline-terraform-{0}', github.run_number) }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/terraform
steps:
- uses: actions/checkout@v4
- name: Create GitHub Release
id: create_release
uses: actions/create-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
release_name: Build ${{ env.TAG_NAME }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Download amd64 artifact
uses: actions/download-artifact@v4
with:
name: terraform-offline-package-amd64
path: release-artifacts/amd64
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: terraform-offline-package-arm64
path: release-artifacts/arm64
- name: Upload offline installers to GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ env.TAG_NAME }}
files: |
release-artifacts/amd64/terraform-offline-package-amd64.tar.gz
release-artifacts/arm64/terraform-offline-package-arm64.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Ensure deps (rsync, ssh)
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y rsync openssh-client
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync release assets to remote
run: script/rsync-release-assets.sh
retention:
name: Remote retention (keep latest 3)
needs: publish-release
runs-on: ubuntu-latest
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ secrets.VPS_HOST }}
REMOTE_ROOT: /data/update-server/terraform
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 3)
run: script/prune-remote-versions.sh

View File

@ -1,67 +0,0 @@
name: release-oci-charts
on:
push:
branches:
- main
paths:
- "oci/charts/apps/app-service/**"
- "oci/charts/postgresql/**"
- "oci/charts/observability/**"
- ".github/workflows/release-oci-charts.yml"
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
- name: Setup Helm
uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4
with:
version: v3.15.4
- name: Log In To GHCR
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Lint charts
run: |
set -euo pipefail
charts=(
"oci/charts/apps/app-service"
"oci/charts/postgresql"
"oci/charts/observability"
)
for chart in "${charts[@]}"; do
helm lint "./${chart}"
done
- name: Package charts
run: |
set -euo pipefail
mkdir -p dist
charts=(
"oci/charts/apps/app-service"
"oci/charts/postgresql"
"oci/charts/observability"
)
for chart in "${charts[@]}"; do
helm package "./${chart}" --destination dist
done
- name: Push charts to GHCR
run: |
set -euo pipefail
for pkg in dist/*.tgz; do
helm push "${pkg}" oci://ghcr.io/x-evor
done

View File

@ -1,88 +0,0 @@
name: Generate and Release Self-Signed SSL Certificates
on:
push:
tags:
- "release-*"
pull_request:
paths:
- '.github/workflows/self-signed-ssl-cert-workflow.yml'
workflow_dispatch:
inputs:
domain:
description: 'Domain to generate certificate for'
required: false
default: 'kube.registry.local'
valid_days:
description: 'Certificate validity (days)'
required: false
default: '3650'
env:
DOMAIN: ${{ github.event.inputs.domain || 'kube.registry.local' }}
VALID_DAYS: ${{ github.event.inputs.valid_days || '3650' }}
OUTPUT_DIR: ssl_certificates
TAG_NAME: ${{ github.ref_name != '' && github.ref_name || format('daily-{0}', github.run_number) }}
jobs:
generate-cert:
runs-on: ubuntu-latest
outputs:
tag_name: ${{ env.TAG_NAME }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Ensure script is executable
run: chmod +x scripts/generate_ssl.sh
- name: Generate Self-Signed SSL Certificate
run: scripts/generate_ssl.sh "$DOMAIN" "$VALID_DAYS" "$OUTPUT_DIR"
- name: Package Certificates
run: tar -czvf ssl_certificates.tar.gz -C "$OUTPUT_DIR" .
- name: Upload SSL Certificates Artifact
uses: actions/upload-artifact@v4
with:
name: self-signed-ssl-certificates
path: ssl_certificates.tar.gz
test-cert:
needs: generate-cert
runs-on: ubuntu-latest
steps:
- name: Download SSL Certificates
uses: actions/download-artifact@v4
with:
name: self-signed-ssl-certificates
- name: Unpack Certificates
run: tar -xzvf ssl_certificates.tar.gz
- name: Validate Certificate with OpenSSL
run: |
openssl x509 -in ssl_certificates/cert.pem -noout -subject -issuer -dates
echo "✅ Certificate appears valid"
release-cert:
needs: test-cert
if: startsWith(github.ref, 'refs/tags/release-') || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: Download SSL Certificates
uses: actions/download-artifact@v4
with:
name: self-signed-ssl-certificates
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name != '' && github.ref_name || format('daily-{0}', github.run_number) }}
name: >-
${{ startsWith(github.ref, 'refs/tags/')
&& format('Release {0}', github.ref_name)
|| format('Daily Build {0}', github.run_number) }}
files: ssl_certificates.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,201 +0,0 @@
name: Sync blackbox_exporter latest v0.* (matrix)
on:
workflow_dispatch:
schedule:
- cron: "0 2 * * *" # <-- 这是 UTC 02:00。若需 JST 02:00请改为 "0 17 * * *"
permissions:
contents: read
concurrency:
group: sync-blackbox-exporter-v0
cancel-in-progress: false
jobs:
prep:
name: Resolve latest tag & remote check (${{ matrix.vps_host }})
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
GH_REPO: prometheus/blackbox_exporter
GH_TOKEN: ${{ github.token }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/prometheus/blackbox_exporter
outputs:
tag: ${{ steps.latest.outputs.tag }}
version: ${{ steps.latest.outputs.version }}
exists: ${{ steps.remotecheck.outputs.exists }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
jq --version
rsync --version | head -n1
- name: Resolve latest tag (v0.*)
id: latest
run: |
set -euo pipefail
TAG=$(./scripts/resolve_github_repo_release.sh "${GH_REPO}" '^v0(\.|$)' 'v0.*')
VERSION=${TAG#v}
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Latest tag: $TAG (version: $VERSION)"
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Check remote existing version dir
id: remotecheck
env:
VERSION: ${{ steps.latest.outputs.version }}
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${VERSION}"
if ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "test -d '${REMOTE_DIR}'"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Remote already has ${REMOTE_DIR}, skip whole sync."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Remote does not have ${REMOTE_DIR}, will sync."
fi
sync-one:
name: Sync ${{ matrix.asset_suffix }} for ${{ needs.prep.outputs.version }} (${{ matrix.vps_host }})
needs: prep
if: needs.prep.outputs.exists == 'false'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
asset_suffix:
- "linux-amd64.tar.gz"
- "linux-arm64.tar.gz"
- "darwin-amd64.tar.gz"
- "darwin-arm64.tar.gz"
env:
GH_REPO: prometheus/blackbox_exporter
GH_TOKEN: ${{ github.token }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/prometheus/blackbox_exporter
TAG: ${{ needs.prep.outputs.tag }}
VERSION: ${{ needs.prep.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
- name: Check asset exists via GitHub CLI
id: has_asset
run: |
set -euo pipefail
ASSET="blackbox_exporter-${VERSION}.${{ matrix.asset_suffix }}"
echo "Checking asset $ASSET for tag ${TAG}"
if gh release view "${TAG}" --repo "${GH_REPO}" --json assets \
| jq -r '.assets[].name' | grep -Fxq "$ASSET"; then
echo "asset=$ASSET" >> "$GITHUB_OUTPUT"
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Asset $ASSET not found for ${TAG}, will skip."
fi
- name: Download asset
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p "releases/${VERSION}"
gh release download "${TAG}" \
--repo "${GH_REPO}" \
--pattern "${{ steps.has_asset.outputs.asset }}" \
--dir "releases/${VERSION}"
- name: Init SSH
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync this asset to remote
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${VERSION}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync releases/${VERSION}/${{ steps.has_asset.outputs.asset }} -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
"releases/${VERSION}/${{ steps.has_asset.outputs.asset }}" "${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 10 v0.*) (${{ matrix.vps_host }})
needs: [prep, sync-one]
if: needs.prep.outputs.exists == 'false'
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/prometheus/blackbox_exporter
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 10)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=10
mapfile -t all < <(ls -1 | grep -E "^[0-9]+\\.[0-9]+\\.[0-9]+$" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,217 +0,0 @@
name: Sync node_exporter 1.9.* / 1.8.* (matrix)
on:
workflow_dispatch:
inputs:
tag:
description: "Release version without 'v' (e.g., 1.9.1). Defaults to 1.9.1"
required: false
type: string
schedule:
- cron: "0 2 * * *" # <-- 这是 UTC 02:00。若需 JST 02:00请改为 "0 17 * * *"
permissions:
contents: read
concurrency:
group: sync-node-exporter-1x
cancel-in-progress: false
jobs:
prep:
name: Resolve version & remote check (${{ matrix.vps_host }})
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
GH_REPO: prometheus/node_exporter
GH_TOKEN: ${{ github.token }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/otel/node_exporter/
DEFAULT_TAG: 1.9.1 # <-- 无 v
ALLOWED_SERIES: "^(1\\.9|1\\.8)\\.[0-9]+\\.[0-9]+$"
outputs:
tag: ${{ steps.resolve.outputs.tag }} # v1.9.1
version: ${{ steps.resolve.outputs.version }} # 1.9.1
exists: ${{ steps.remotecheck.outputs.exists }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
jq --version
rsync --version | head -n1
- name: Resolve version (use input or default) & validate (1.9.* / 1.8.*)
id: resolve
run: |
set -euo pipefail
VERSION_INPUT='${{ github.event.inputs.tag }}'
if [ -n "$VERSION_INPUT" ]; then
VERSION="$VERSION_INPUT"
else
VERSION="$DEFAULT_TAG"
fi
if ! echo "$VERSION" | grep -Eq '${{ env.ALLOWED_SERIES }}'; then
echo "Invalid or disallowed version: $VERSION. Allowed: 1.9.* or 1.8.*" >&2
exit 1
fi
REL_TAG="v${VERSION}" # GitHub release tag 有 v 前缀
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$REL_TAG" >> "$GITHUB_OUTPUT"
echo "Use version: $VERSION (release tag: $REL_TAG)"
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Check remote existing version dir
id: remotecheck
env:
VERSION: ${{ steps.resolve.outputs.version }}
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${VERSION}"
if ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "test -d '${REMOTE_DIR}'"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Remote already has ${REMOTE_DIR}, skip whole sync."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Remote does not have ${REMOTE_DIR}, will sync."
fi
sync-one:
name: Sync ${{ matrix.asset_suffix }} for ${{ needs.prep.outputs.version }} (${{ matrix.vps_host }})
needs: prep
if: needs.prep.outputs.exists == 'false'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
asset_suffix:
- "linux-amd64.tar.gz"
- "linux-arm64.tar.gz"
env:
GH_REPO: prometheus/node_exporter
GH_TOKEN: ${{ github.token }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/prometheus/node_exporter
TAG: ${{ needs.prep.outputs.tag }} # v1.9.1
VERSION: ${{ needs.prep.outputs.version }} # 1.9.1
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
- name: Check asset exists via GitHub CLI
id: has_asset
run: |
set -euo pipefail
# 文件名node_exporter-${VERSION}.linux-amd64.tar.gz / linux-arm64.tar.gz
ASSET="node_exporter-${VERSION}.${{ matrix.asset_suffix }}"
echo "Checking asset $ASSET for tag ${TAG}"
if gh release view "${TAG}" --repo "${GH_REPO}" --json assets \
| jq -r '.assets[].name' | grep -Fxq "$ASSET"; then
echo "asset=$ASSET" >> "$GITHUB_OUTPUT"
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Asset $ASSET not found for ${TAG}, will skip."
fi
- name: Download asset
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p "releases/${VERSION}"
gh release download "${TAG}" \
--repo "${GH_REPO}" \
--pattern "${{ steps.has_asset.outputs.asset }}" \
--dir "releases/${VERSION}"
- name: Init SSH
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync this asset to remote
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${VERSION}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync releases/${VERSION}/${{ steps.has_asset.outputs.asset }} -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
"releases/${VERSION}/${{ steps.has_asset.outputs.asset }}" "${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 10) (${{ matrix.vps_host }})
needs: [prep, sync-one]
if: needs.prep.outputs.exists == 'false'
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/prometheus/node_exporter/
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 10)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=10
mapfile -t all < <(ls -1 | grep -E "^[0-9]+\.[0-9]+\.[0-9]+$" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,237 +0,0 @@
name: Sync otelcol-contrib v0.* (matrix)
on:
workflow_dispatch:
inputs:
tag:
description: "Release tag to sync (e.g., v0.133.0). Defaults to v0.133.0"
required: false
type: string
schedule:
- cron: "0 2 * * *" # <-- 这是 UTC 02:00。若需 JST 02:00请改为 "0 17 * * *"
# 让内置 GITHUB_TOKEN 具备读 release 资产的权限
permissions:
contents: read
concurrency:
group: sync-otelcol-contrib-v0
cancel-in-progress: false
jobs:
prep:
name: Resolve tag & remote check (${{ matrix.vps_host }})
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
GH_REPO: open-telemetry/opentelemetry-collector-releases
GH_TOKEN: ${{ github.token }} # 用内置 token无需自建 PAT
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/otel/OpenTelemetry/
outputs:
tag: ${{ steps.resolve.outputs.tag }}
version: ${{ steps.resolve.outputs.version }}
exists: ${{ steps.remotecheck.outputs.exists }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
jq --version
rsync --version | head -n1
- name: Resolve tag (use input or latest v0.*)
id: resolve
run: |
set -euo pipefail
TAG_INPUT='${{ github.event.inputs.tag }}'
if [ -n "$TAG_INPUT" ]; then
TAG="$TAG_INPUT"
else
TAG=$(./scripts/resolve_github_repo_release.sh "${GH_REPO}" '^v0\.[0-9]+\.[0-9]+$' 'v0.*')
fi
# 基本校验:必须形如 v0.X.Y
if ! echo "$TAG" | grep -Eq '^v0\.[0-9]+\.[0-9]+$'; then
echo "Invalid tag format: $TAG" >&2
exit 1
fi
VERSION="${TAG#v}" # 去掉前缀 v例如 v0.133.0 -> 0.133.0
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Use tag: $TAG (version=$VERSION)"
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Check remote existing tag
id: remotecheck
env:
TAG: ${{ steps.resolve.outputs.tag }}
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG}"
if ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "test -d '${REMOTE_DIR}'"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Remote already has ${REMOTE_DIR}, skip whole sync."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Remote does not have ${REMOTE_DIR}, will sync."
fi
sync-one:
name: Sync ${{ matrix.asset_prefix }}_${{ matrix.asset_suffix }} for ${{ needs.prep.outputs.tag }} (${{ matrix.vps_host }})
needs: prep
if: needs.prep.outputs.exists == 'false' # 远端已存在则整个矩阵跳过
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
asset:
- otelcol-contrib_linux_amd64
- otelcol-contrib_linux_arm64
- opampsupervisor_linux_amd64
- opampsupervisor_linux_arm64
include:
- asset: otelcol-contrib_linux_amd64
asset_prefix: "otelcol-contrib"
asset_suffix: "linux_amd64.tar.gz"
release_tag_prefix: ""
- asset: otelcol-contrib_linux_arm64
asset_prefix: "otelcol-contrib"
asset_suffix: "linux_arm64.tar.gz"
release_tag_prefix: ""
- asset: opampsupervisor_linux_amd64
asset_prefix: "opampsupervisor"
asset_suffix: "linux_amd64"
release_tag_prefix: "cmd/opampsupervisor/"
- asset: opampsupervisor_linux_arm64
asset_prefix: "opampsupervisor"
asset_suffix: "linux_arm64"
release_tag_prefix: "cmd/opampsupervisor/"
env:
GH_REPO: open-telemetry/opentelemetry-collector-releases
GH_TOKEN: ${{ github.token }} # 继续使用内置 token
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/otel/OpenTelemetry
TAG: ${{ needs.prep.outputs.tag }}
VERSION: ${{ needs.prep.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
- name: Check asset exists via GitHub CLI
id: has_asset
run: |
set -euo pipefail
ASSET="${{ matrix.asset_prefix }}_${VERSION}_${{ matrix.asset_suffix }}"
RELEASE_TAG="${{ matrix.release_tag_prefix }}${TAG}"
echo "Checking asset $ASSET for release tag ${RELEASE_TAG}"
ASSET_LIST=$(gh release view "${RELEASE_TAG}" --repo "${GH_REPO}" --json assets | jq -r '.assets[].name')
if echo "${ASSET_LIST}" | grep -Fxq "$ASSET"; then
echo "asset=$ASSET" >> "$GITHUB_OUTPUT"
echo "release_tag=${RELEASE_TAG}" >> "$GITHUB_OUTPUT"
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Asset $ASSET not found for ${RELEASE_TAG}, will skip."
fi
- name: Download asset
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p "releases/${TAG}"
gh release download "${{ steps.has_asset.outputs.release_tag }}" \
--repo "${GH_REPO}" \
--pattern "${{ steps.has_asset.outputs.asset }}" \
--dir "releases/${TAG}"
- name: Init SSH
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync this asset to remote
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync releases/${TAG}/${{ steps.has_asset.outputs.asset }} -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
"releases/${TAG}/${{ steps.has_asset.outputs.asset }}" "${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 10 v0.*) (${{ matrix.vps_host }})
needs: [prep, sync-one]
if: needs.prep.outputs.exists == 'false' # 只有新增版本时才清理
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/otel/OpenTelemetry/
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 10)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=10
mapfile -t all < <(ls -1 | grep -E "^v0\.[0-9]+\.[0-9]+$" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,199 +0,0 @@
name: Sync vultr-cli latest (matrix)
on:
workflow_dispatch:
schedule:
- cron: "0 2 * * *" # <-- 这是 UTC 02:00。若需 JST 02:00请改为 "0 17 * * *"
permissions:
contents: read
concurrency:
group: sync-vultr-cli-latest
cancel-in-progress: false
jobs:
prep:
name: Resolve latest tag & remote check (${{ matrix.vps_host }})
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
GH_REPO: vultr/vultr-cli
GH_TOKEN: ${{ github.token }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/iac/vultr-cli/
outputs:
tag: ${{ steps.latest.outputs.tag }}
version: ${{ steps.latest.outputs.version }}
exists: ${{ steps.remotecheck.outputs.exists }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
jq --version
rsync --version | head -n1
- name: Resolve latest tag (semver)
id: latest
run: |
set -euo pipefail
TAG=$(./scripts/resolve_github_repo_release.sh "${GH_REPO}" '^v[0-9]+\.[0-9]+\.[0-9]+$' 'v*.*.*')
VERSION=${TAG#v}
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Latest tag: $TAG (version: $VERSION)"
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Check remote existing version dir
id: remotecheck
env:
VERSION: ${{ steps.latest.outputs.version }}
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT%/}/${VERSION}"
if ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "test -d '${REMOTE_DIR}'"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Remote already has ${REMOTE_DIR}, skip whole sync."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Remote does not have ${REMOTE_DIR}, will sync."
fi
sync-one:
name: Sync ${{ matrix.asset_suffix }} for ${{ needs.prep.outputs.version }} (${{ matrix.vps_host }})
needs: prep
if: needs.prep.outputs.exists == 'false'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
asset_suffix:
- "linux_amd64.tar.gz"
- "macOS_arm64.tar.gz"
env:
GH_REPO: vultr/vultr-cli
GH_TOKEN: ${{ github.token }}
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/iac/vultr-cli/
TAG: ${{ needs.prep.outputs.tag }}
VERSION: ${{ needs.prep.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
- name: Check asset exists via GitHub CLI
id: has_asset
run: |
set -euo pipefail
ASSET="vultr_${TAG}_${{ matrix.asset_suffix }}"
echo "Checking asset $ASSET for tag ${TAG}"
if gh release view "${TAG}" --repo "${GH_REPO}" --json assets \
| jq -r '.assets[].name' | grep -Fxq "$ASSET"; then
echo "asset=$ASSET" >> "$GITHUB_OUTPUT"
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Asset $ASSET not found for ${TAG}, will skip."
fi
- name: Download asset
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p "releases/${VERSION}"
gh release download "${TAG}" \
--repo "${GH_REPO}" \
--pattern "${{ steps.has_asset.outputs.asset }}" \
--dir "releases/${VERSION}"
- name: Init SSH
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync this asset to remote
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT%/}/${VERSION}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync releases/${VERSION}/${{ steps.has_asset.outputs.asset }} -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
"releases/${VERSION}/${{ steps.has_asset.outputs.asset }}" "${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 10) (${{ matrix.vps_host }})
needs: [prep, sync-one]
if: needs.prep.outputs.exists == 'false'
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/iac/vultr-cli/
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 10)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=10
mapfile -t all < <(ls -1 | grep -E "^[0-9]+\\.[0-9]+\\.[0-9]+$" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

View File

@ -1,191 +0,0 @@
name: Sync Xray-core latest v25.* (matrix)
on:
workflow_dispatch:
schedule:
- cron: "0 2 * * *" # <-- 这是 UTC 02:00。若需 JST 02:00请改为 "0 17 * * *"
# 让内置 GITHUB_TOKEN 具备读 release 资产的权限(显式声明,避免组织策略导致只读/禁用)
permissions:
contents: read
concurrency:
group: sync-xray-core-v25
cancel-in-progress: false
jobs:
prep:
name: Resolve latest tag & remote check (${{ matrix.vps_host }})
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
GH_REPO: XTLS/Xray-core
GH_TOKEN: ${{ github.token }} # 用内置 token无需自建 PAT
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/xray-core
outputs:
tag: ${{ steps.latest.outputs.tag }}
exists: ${{ steps.remotecheck.outputs.exists }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
jq --version
rsync --version | head -n1
- name: Resolve latest tag (v25.*)
id: latest
run: |
set -euo pipefail
TAG=$(./scripts/resolve_github_repo_release.sh "${GH_REPO}" '^v25(\.|$)' 'v25.*')
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "Latest tag: $TAG"
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Check remote existing tag
id: remotecheck
run: |
set -euo pipefail
TAG='${{ steps.latest.outputs.tag }}'
REMOTE_DIR="${REMOTE_ROOT}/${TAG}"
if ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "test -d '${REMOTE_DIR}'"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Remote already has ${REMOTE_DIR}, skip whole sync."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Remote does not have ${REMOTE_DIR}, will sync."
fi
sync-one:
name: Sync ${{ matrix.asset }} for ${{ needs.prep.outputs.tag }} (${{ matrix.vps_host }})
needs: prep
if: needs.prep.outputs.exists == 'false' # 远端已存在则整个矩阵跳过
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
asset: [ "Xray-linux-64.zip", "Xray-macos-64.zip", "Xray-windows-64.zip" ]
env:
GH_REPO: XTLS/Xray-core
GH_TOKEN: ${{ github.token }} # 继续使用内置 token
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/xray-core
TAG: ${{ needs.prep.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Ensure GitHub CLI & deps
run: |
set -euo pipefail
sudo apt-get update -y
sudo apt-get install -y gh jq rsync
gh --version
- name: Check asset exists via GitHub CLI
id: has_asset
run: |
set -euo pipefail
ASSET='${{ matrix.asset }}'
echo "Checking asset $ASSET for tag ${TAG}"
if gh release view "${TAG}" --repo "${GH_REPO}" --json assets \
| jq -r '.assets[].name' | grep -Fxq "$ASSET"; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "Asset $ASSET not found for ${TAG}, will skip."
fi
- name: Download asset
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p "releases/${TAG}"
gh release download "${TAG}" \
--repo "${GH_REPO}" \
--pattern "${{ matrix.asset }}" \
--dir "releases/${TAG}"
- name: Init SSH
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Rsync this asset to remote
if: steps.has_asset.outputs.exists == 'true'
run: |
set -euo pipefail
REMOTE_DIR="${REMOTE_ROOT}/${TAG}"
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" "mkdir -p '${REMOTE_DIR}'"
echo "Rsync releases/${TAG}/${{ matrix.asset }} -> ${VPS_HOST}:${REMOTE_DIR}/"
rsync -av -e "ssh -i ~/.ssh/id_rsa" \
"releases/${TAG}/${{ matrix.asset }}" "${RSYNC_SSH_USER}@${VPS_HOST}:${REMOTE_DIR}/"
retention:
name: Remote retention (keep latest 10 v25.*) (${{ matrix.vps_host }})
needs: [prep, sync-one]
if: needs.prep.outputs.exists == 'false' # 只有新增版本时才清理
runs-on: ubuntu-latest
strategy:
matrix:
vps_host:
- cn-homepage.svc.plus
- global-homepage.svc.plus
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
RSYNC_SSH_USER: ${{ secrets.RSYNC_SSH_USER }}
VPS_HOST: ${{ matrix.vps_host }}
REMOTE_ROOT: /data/update-server/xray-core
steps:
- name: Init SSH
run: |
set -euo pipefail
mkdir -p ~/.ssh
echo "$RSYNC_SSH_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H "$VPS_HOST" >> ~/.ssh/known_hosts
- name: Prune old versions on remote (keep 10)
run: |
set -euo pipefail
ssh -i ~/.ssh/id_rsa "${RSYNC_SSH_USER}@${VPS_HOST}" bash -lc '
set -euo pipefail
cd "'"${REMOTE_ROOT}"'" || exit 0
keep=10
mapfile -t all < <(ls -1 | grep -E "^v25(\.|$)" | sort -V -r || true)
if [ "${#all[@]}" -le "$keep" ]; then
echo "Nothing to prune. Count=${#all[@]}"
exit 0
fi
to_delete=("${all[@]:keep}")
echo "Pruning old versions: ${to_delete[*]}"
for d in "${to_delete[@]}"; do
rm -rf -- "$d"
done
'

3
.gitignore vendored
View File

@ -22,6 +22,3 @@
.build-harness
build-harness
dist/bin/*
# Packaged Helm charts generated in-place during release work
oci/charts/apps/*/charts/*.tgz

View File

@ -3,7 +3,3 @@
1. clone repo
2. create repo Actions secrets and variables: HELM_REPO_PASSWORD
3. push your code will be tigger github ci pipeline
Additional documentation:
- [GPU Kubernetes offline package](docs/gpu-offline-package.md)

View File

@ -1,52 +0,0 @@
# 在 Ubuntu 上安装 NVIDIA 驱动和 nvidia-container-toolkit
以下步骤演示如何在主机安装 NVIDIA 驱动及 nvidia-container-toolkit并将 containerd 配置为能够使用 GPU。末尾还补充了 sealos 自带 containerd 的配置方式。
## 1. 安装 NVIDIA 驱动和 nvidia-container-toolkit
```bash
# 添加 NVIDIA 容器工具箱仓库
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
distribution=$(awk -F= '/^ID=/{print $2}' /etc/os-release)$(awk -F= '/^VERSION_ID=/{print $2}' /etc/os-release)
curl -s -L "https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list" | \
sed 's#^deb #deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] #' | \
sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update
sudo apt-get install -y nvidia-driver-535 nvidia-container-toolkit
```
安装完成后可通过 `nvidia-smi` 验证驱动是否正常:
```bash
nvidia-smi
```
## 2. 配置 containerd 使用 GPU
使用 `nvidia-ctk` 工具可以快速生成配置并设置为默认运行时:
```bash
sudo nvidia-ctk runtime configure --runtime=containerd --set-as-default
sudo systemctl restart containerd
```
以上命令会在 `/etc/containerd/config.toml` 中新增 `nvidia` 运行时,使 kubelet 或其他工具可以直接调度 GPU 容器。
## 3. sealos-containerd 支持
若主机通过 [sealos](https://github.com/labring/sealos) 部署集群,其内置的 containerd 服务名通常为 `sealos-containerd`,配置文件位于 sealos 数据目录,例如:
`/var/lib/sealos/data/default/rootfs/etc/containerd/config.toml`。可按以下方式配置:
```bash
sudo nvidia-ctk runtime configure \
--config /var/lib/sealos/data/default/rootfs/etc/containerd/config.toml \
--set-as-default
sudo systemctl restart sealos-containerd
```
完成后即可在 sealos 集群内运行需要 GPU 的容器或 Pod。

View File

@ -1,27 +0,0 @@
# 构建 GPU Kubernetes 离线包
本仓库提供 `scripts/create-gpu-k8s-offline-package.sh` 脚本,用于生成在无网络环境下部署 GPU 集群所需的离线文件。脚本默认打包 Kubernetes 以及相关组件的 `v1.29.9` 版本,你可以通过环境变量调整需要的版本。
## 使用方法
```bash
# 构建默认版本 (v1.29.9)
bash scripts/create-gpu-k8s-offline-package.sh
# 指定其它版本,例如 v1.28.7
K8S_VERSION=labring/kubernetes:v1.28.7 \
bash scripts/create-gpu-k8s-offline-package.sh
```
生成的 `gpu_k8s_offline_packages.tar.gz` 包含以下内容:
- Kubernetes 二进制镜像
- Cilium、Helm 等依赖镜像
- NVIDIA 驱动nvidia-driver-535及 nvidia-container-toolkit 离线包deb/rpm
- nerdctl CLIv${NERDCTL_VERSION:-2.1.2}
- 必要的容器镜像,包括 `registry.k8s.io/pause:3.8`
- GPU 环境检测脚本 `check-gpu-status.sh`
- 可选的集群安全脚本 `close-k8s-debug-ports.sh`
该离线包用于基于 `sealos` 部署 Kubernetes最低推荐版本为 **1.29**,也可以使用更新的 `1.30` 等稳定版本。

View File

@ -1,56 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
ingress_ip=$(hostname -I | awk '{print $1}')
cat > values.yaml <<EOF
service:
type: NodePort
externalIPs:
- ${ingress_ip}
http:
enabled: true
servicePort: 80
tls:
servicePort: 443
nodePort: 30443
# 仅部署网关数据面;不装 etcd、不装 ingress-controller
etcd:
enabled: false
ingress-controller:
enabled: false
apisix:
deployment:
# standalone = 无 etcd本地文件/ConfigMap 驱动;同时禁用 Admin API
mode: standalone
role: data_plane
role_data_plane:
# 需要时可用 yaml/json 作配置源;此处先保留 yaml
config_provider: yaml
# 基本特性可按需开启
ssl:
enabled: true
prometheus:
enabled: true
# (可选加固)即便 Helm 仍创建了 Admin Servicestandalone 下也不会有 Admin 监听;
# 这里进一步把 Admin 访问白名单收紧,避免误触。
admin:
allow:
ipList:
- 127.0.0.1/32
EOF
helm repo add apisix https://charts.apiseven.com || true
helm repo update
kubectl get ns ingress >/dev/null 2>&1 || kubectl create ns ingress
# 只安装 APISIX 网关(无 etcd / 无 admin / 无 ingress-controller
helm upgrade --install apisix apisix/apisix \
--namespace ingress \
-f values.yaml

View File

@ -1,86 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
APP_NAME="AutoGen Studio"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
OFFLINE_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
COMPOSE_FILE="${OFFLINE_ROOT}/docker-compose.yaml"
IMAGES_DIR="${OFFLINE_ROOT}/images"
IMAGE_LOAD_TOOL="${IMAGE_LOAD_TOOL:-docker}"
command_exists() {
command -v "$1" >/dev/null 2>&1
}
load_images() {
if ! command_exists "${IMAGE_LOAD_TOOL}"; then
echo "Error: image loader '${IMAGE_LOAD_TOOL}' not found in PATH" >&2
exit 1
fi
if [ ! -d "${IMAGES_DIR}" ]; then
echo "No images directory found at ${IMAGES_DIR}. Skipping image load." >&2
return
fi
shopt -s nullglob
local tarball
for tarball in "${IMAGES_DIR}"/*.tar; do
echo "Loading container images from ${tarball}"
"${IMAGE_LOAD_TOOL}" load -i "${tarball}"
done
shopt -u nullglob
}
compose() {
if command_exists docker && docker compose version >/dev/null 2>&1; then
docker compose "$@"
elif command_exists docker-compose; then
docker-compose "$@"
else
echo "Error: docker compose plugin or docker-compose binary is required" >&2
exit 1
fi
}
usage() {
cat <<USAGE
Usage: $(basename "$0") [command]
Commands:
up Load images (if available) and start ${APP_NAME}
down Stop ${APP_NAME}
load-images Only load container images from the images/ directory
status Show status of the compose application
Environment variables:
IMAGE_LOAD_TOOL Override the container image loader (default: docker)
COMPOSE_FILE Override docker compose file path (default: ${COMPOSE_FILE})
USAGE
}
cmd=${1:-up}
case "${cmd}" in
up)
load_images
compose -f "${COMPOSE_FILE}" up -d
;;
down)
compose -f "${COMPOSE_FILE}" down
;;
load-images)
load_images
;;
status)
compose -f "${COMPOSE_FILE}" ps
;;
-h|--help|help)
usage
;;
*)
echo "Unknown command: ${cmd}" >&2
usage
exit 1
;;
esac

View File

@ -1,18 +0,0 @@
version: "3.8"
services:
autogen-studio:
image: autogenstudio/autogen-studio:__AUTOGEN_TAG__
container_name: autogen-studio
restart: unless-stopped
ports:
- "9090:9090"
environment:
AUTOGEN_STUDIO_HOST: 0.0.0.0
AUTOGEN_STUDIO_PORT: 9090
AUTOGEN_STUDIO_LOG_LEVEL: info
volumes:
- autogen-data:/data
volumes:
autogen-data:

View File

@ -1,86 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
APP_NAME="Dify"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
OFFLINE_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
COMPOSE_FILE="${OFFLINE_ROOT}/docker-compose.yaml"
IMAGES_DIR="${OFFLINE_ROOT}/images"
IMAGE_LOAD_TOOL="${IMAGE_LOAD_TOOL:-docker}"
command_exists() {
command -v "$1" >/dev/null 2>&1
}
load_images() {
if ! command_exists "${IMAGE_LOAD_TOOL}"; then
echo "Error: image loader '${IMAGE_LOAD_TOOL}' not found in PATH" >&2
exit 1
fi
if [ ! -d "${IMAGES_DIR}" ]; then
echo "No images directory found at ${IMAGES_DIR}. Skipping image load." >&2
return
fi
shopt -s nullglob
local tarball
for tarball in "${IMAGES_DIR}"/*.tar; do
echo "Loading container images from ${tarball}"
"${IMAGE_LOAD_TOOL}" load -i "${tarball}"
done
shopt -u nullglob
}
compose() {
if command_exists docker && docker compose version >/dev/null 2>&1; then
docker compose "$@"
elif command_exists docker-compose; then
docker-compose "$@"
else
echo "Error: docker compose plugin or docker-compose binary is required" >&2
exit 1
fi
}
usage() {
cat <<USAGE
Usage: $(basename "$0") [command]
Commands:
up Load images (if available) and start ${APP_NAME}
down Stop ${APP_NAME}
load-images Only load container images from the images/ directory
status Show status of the compose application
Environment variables:
IMAGE_LOAD_TOOL Override the container image loader (default: docker)
COMPOSE_FILE Override docker compose file path (default: ${COMPOSE_FILE})
USAGE
}
cmd=${1:-up}
case "${cmd}" in
up)
load_images
compose -f "${COMPOSE_FILE}" up -d
;;
down)
compose -f "${COMPOSE_FILE}" down
;;
load-images)
load_images
;;
status)
compose -f "${COMPOSE_FILE}" ps
;;
-h|--help|help)
usage
;;
*)
echo "Unknown command: ${cmd}" >&2
usage
exit 1
;;
esac

View File

@ -1,72 +0,0 @@
version: "3.8"
services:
postgres:
image: postgres:__POSTGRES_TAG__
container_name: dify-postgres
restart: unless-stopped
environment:
POSTGRES_DB: dify
POSTGRES_USER: dify
POSTGRES_PASSWORD: dify
volumes:
- dify-postgres:/var/lib/postgresql/data
redis:
image: redis:__REDIS_TAG__
container_name: dify-redis
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes"]
volumes:
- dify-redis:/data
dify-api:
image: langgenius/dify-api:__DIFY_TAG__
container_name: dify-api
restart: unless-stopped
depends_on:
- postgres
- redis
environment:
DATABASE_URL: postgresql+psycopg://dify:dify@postgres:5432/dify
REDIS_URL: redis://redis:6379/0
WEB_URL: http://localhost:8080
WORKER_QUEUE_BROKER_URL: redis://redis:6379/1
WORKER_QUEUE_BACKEND_URL: redis://redis:6379/2
volumes:
- dify-storage:/app/storage
dify-worker:
image: langgenius/dify-worker:__DIFY_TAG__
container_name: dify-worker
restart: unless-stopped
depends_on:
- redis
- dify-api
environment:
REDIS_URL: redis://redis:6379/1
WORKER_QUEUE_BACKEND_URL: redis://redis:6379/2
API_URL: http://dify-api:5001
dify-web:
image: langgenius/dify-web:__DIFY_TAG__
container_name: dify-web
restart: unless-stopped
depends_on:
- dify-api
environment:
VITE_API_URL: http://dify-nginx
VITE_APP_ENV: production
dify-nginx:
image: langgenius/dify-nginx:__DIFY_TAG__
container_name: dify-nginx
restart: unless-stopped
ports:
- "8080:80"
depends_on:
- dify-api
- dify-web
environment:
API_HOST: dify-api:5001
WEB_HOST: dify-web:3000
volumes:
dify-postgres:
dify-redis:
dify-storage:

View File

@ -1,86 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
APP_NAME="Flowise"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
OFFLINE_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
COMPOSE_FILE="${OFFLINE_ROOT}/docker-compose.yaml"
IMAGES_DIR="${OFFLINE_ROOT}/images"
IMAGE_LOAD_TOOL="${IMAGE_LOAD_TOOL:-docker}"
command_exists() {
command -v "$1" >/dev/null 2>&1
}
load_images() {
if ! command_exists "${IMAGE_LOAD_TOOL}"; then
echo "Error: image loader '${IMAGE_LOAD_TOOL}' not found in PATH" >&2
exit 1
fi
if [ ! -d "${IMAGES_DIR}" ]; then
echo "No images directory found at ${IMAGES_DIR}. Skipping image load." >&2
return
fi
shopt -s nullglob
local tarball
for tarball in "${IMAGES_DIR}"/*.tar; do
echo "Loading container images from ${tarball}"
"${IMAGE_LOAD_TOOL}" load -i "${tarball}"
done
shopt -u nullglob
}
compose() {
if command_exists docker && docker compose version >/dev/null 2>&1; then
docker compose "$@"
elif command_exists docker-compose; then
docker-compose "$@"
else
echo "Error: docker compose plugin or docker-compose binary is required" >&2
exit 1
fi
}
usage() {
cat <<USAGE
Usage: $(basename "$0") [command]
Commands:
up Load images (if available) and start ${APP_NAME}
down Stop ${APP_NAME}
load-images Only load container images from the images/ directory
status Show status of the compose application
Environment variables:
IMAGE_LOAD_TOOL Override the container image loader (default: docker)
COMPOSE_FILE Override docker compose file path (default: ${COMPOSE_FILE})
USAGE
}
cmd=${1:-up}
case "${cmd}" in
up)
load_images
compose -f "${COMPOSE_FILE}" up -d
;;
down)
compose -f "${COMPOSE_FILE}" down
;;
load-images)
load_images
;;
status)
compose -f "${COMPOSE_FILE}" ps
;;
-h|--help|help)
usage
;;
*)
echo "Unknown command: ${cmd}" >&2
usage
exit 1
;;
esac

View File

@ -1,19 +0,0 @@
version: "3.8"
services:
flowise:
image: flowiseai/flowise:__FLOWISE_TAG__
container_name: flowise-app
restart: unless-stopped
ports:
- "3000:3000"
environment:
PORT: 3000
FLOWISE_USERNAME: admin
FLOWISE_PASSWORD: changeme
DATABASE_PATH: /data/flowise.sqlite
volumes:
- flowise-data:/data
volumes:
flowise-data:

View File

@ -1,98 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
NODE_NAME=$(hostname)
kubectl label nodes "${NODE_NAME}" ingress-node=true --overwrite
cat <<'YEOF' | kubectl apply -f -
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: kong
annotations:
konghq.com/gatewayclass-unmanaged: 'true'
spec:
controllerName: konghq.com/kic-gateway-controller
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: default
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: demo-gateway
namespace: default
annotations:
konghq.com/publish-service: kong/kong-gateway-proxy
spec:
gatewayClassName: kong
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: "example.com"
tls:
mode: Terminate
certificateRefs:
- name: example-tls
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: demo-route
namespace: default
spec:
parentRefs:
- name: demo-gateway
namespace: default
hostnames:
- example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: nginx-svc
port: 80
YEOF
EXTERNAL_IP=$(hostname -I | awk '{print $1}')
curl -ksv https://example.com --resolve example.com:443:${EXTERNAL_IP}

View File

@ -1,63 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml
# Setup Helm repository and values
helm repo add kong https://charts.konghq.com
helm repo update
cat > kong-values.yaml <<'VEOF'
kong:
secretVolumes:
- example-tls
env:
ssl_cert: /etc/secrets/example-tls/tls.crt
ssl_cert_key: /etc/secrets/example-tls/tls.key
VEOF
kubectl create ns kong || true
# Generate self-signed certificate for example.com
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-subj "/CN=example.com" \
-keyout example.com.key \
-out example.com.pem
kubectl create secret tls example-tls --cert=example.com.pem --key=example.com.key -n kong
helm upgrade --install kong kong/ingress -n kong --create-namespace -f kong-values.yaml
# Expose Kong proxy via NodePort and external IP
kubectl patch svc kong-gateway-proxy -n kong \
--type='merge' \
-p '{
"spec": {
"type": "NodePort",
"ports": [
{
"port": 80,
"targetPort": 8000,
"protocol": "TCP",
"name": "http",
"nodePort": 30080
},
{
"port": 443,
"targetPort": 8443,
"protocol": "TCP",
"name": "https",
"nodePort": 30443
}
]
}
}'
EXTERNAL_IP=$(hostname -I | awk '{print $1}')
kubectl patch svc kong-gateway-proxy -n kong \
--type='merge' \
-p "{\"spec\": {\"externalIPs\": [\"${EXTERNAL_IP}\"]}}"
NODE_NAME=$(hostname)
kubectl patch deployment kong-gateway -n kong \
--type='merge' \
-p "{\"spec\": {\"template\": {\"spec\": {\"nodeName\": \"${NODE_NAME}\"}}}}"

View File

@ -1,86 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
APP_NAME="n8n"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
OFFLINE_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
COMPOSE_FILE="${OFFLINE_ROOT}/docker-compose.yaml"
IMAGES_DIR="${OFFLINE_ROOT}/images"
IMAGE_LOAD_TOOL="${IMAGE_LOAD_TOOL:-docker}"
command_exists() {
command -v "$1" >/dev/null 2>&1
}
load_images() {
if ! command_exists "${IMAGE_LOAD_TOOL}"; then
echo "Error: image loader '${IMAGE_LOAD_TOOL}' not found in PATH" >&2
exit 1
fi
if [ ! -d "${IMAGES_DIR}" ]; then
echo "No images directory found at ${IMAGES_DIR}. Skipping image load." >&2
return
fi
shopt -s nullglob
local tarball
for tarball in "${IMAGES_DIR}"/*.tar; do
echo "Loading container images from ${tarball}"
"${IMAGE_LOAD_TOOL}" load -i "${tarball}"
done
shopt -u nullglob
}
compose() {
if command_exists docker && docker compose version >/dev/null 2>&1; then
docker compose "$@"
elif command_exists docker-compose; then
docker-compose "$@"
else
echo "Error: docker compose plugin or docker-compose binary is required" >&2
exit 1
fi
}
usage() {
cat <<USAGE
Usage: $(basename "$0") [command]
Commands:
up Load images (if available) and start ${APP_NAME}
down Stop ${APP_NAME}
load-images Only load container images from the images/ directory
status Show status of the compose application
Environment variables:
IMAGE_LOAD_TOOL Override the container image loader (default: docker)
COMPOSE_FILE Override docker compose file path (default: ${COMPOSE_FILE})
USAGE
}
cmd=${1:-up}
case "${cmd}" in
up)
load_images
compose -f "${COMPOSE_FILE}" up -d
;;
down)
compose -f "${COMPOSE_FILE}" down
;;
load-images)
load_images
;;
status)
compose -f "${COMPOSE_FILE}" ps
;;
-h|--help|help)
usage
;;
*)
echo "Unknown command: ${cmd}" >&2
usage
exit 1
;;
esac

View File

@ -1,37 +0,0 @@
version: "3.8"
services:
postgres:
image: postgres:__POSTGRES_TAG__
container_name: n8n-postgres
restart: unless-stopped
environment:
POSTGRES_DB: n8n
POSTGRES_USER: n8n
POSTGRES_PASSWORD: n8n
volumes:
- n8n-postgres:/var/lib/postgresql/data
n8n:
image: n8nio/n8n:__N8N_TAG__
container_name: n8n-app
restart: unless-stopped
depends_on:
- postgres
ports:
- "5678:5678"
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
DB_POSTGRESDB_PASSWORD: n8n
N8N_BASIC_AUTH_ACTIVE: "true"
N8N_BASIC_AUTH_USER: admin
N8N_BASIC_AUTH_PASSWORD: changeme
volumes:
- n8n-data:/home/node/.n8n
volumes:
n8n-postgres:
n8n-data:

View File

@ -1,86 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
APP_NAME="RAGFlow"
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
OFFLINE_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd)
COMPOSE_FILE="${OFFLINE_ROOT}/docker-compose.yaml"
IMAGES_DIR="${OFFLINE_ROOT}/images"
IMAGE_LOAD_TOOL="${IMAGE_LOAD_TOOL:-docker}"
command_exists() {
command -v "$1" >/dev/null 2>&1
}
load_images() {
if ! command_exists "${IMAGE_LOAD_TOOL}"; then
echo "Error: image loader '${IMAGE_LOAD_TOOL}' not found in PATH" >&2
exit 1
fi
if [ ! -d "${IMAGES_DIR}" ]; then
echo "No images directory found at ${IMAGES_DIR}. Skipping image load." >&2
return
fi
shopt -s nullglob
local tarball
for tarball in "${IMAGES_DIR}"/*.tar; do
echo "Loading container images from ${tarball}"
"${IMAGE_LOAD_TOOL}" load -i "${tarball}"
done
shopt -u nullglob
}
compose() {
if command_exists docker && docker compose version >/dev/null 2>&1; then
docker compose "$@"
elif command_exists docker-compose; then
docker-compose "$@"
else
echo "Error: docker compose plugin or docker-compose binary is required" >&2
exit 1
fi
}
usage() {
cat <<USAGE
Usage: $(basename "$0") [command]
Commands:
up Load images (if available) and start ${APP_NAME}
down Stop ${APP_NAME}
load-images Only load container images from the images/ directory
status Show status of the compose application
Environment variables:
IMAGE_LOAD_TOOL Override the container image loader (default: docker)
COMPOSE_FILE Override docker compose file path (default: ${COMPOSE_FILE})
USAGE
}
cmd=${1:-up}
case "${cmd}" in
up)
load_images
compose -f "${COMPOSE_FILE}" up -d
;;
down)
compose -f "${COMPOSE_FILE}" down
;;
load-images)
load_images
;;
status)
compose -f "${COMPOSE_FILE}" ps
;;
-h|--help|help)
usage
;;
*)
echo "Unknown command: ${cmd}" >&2
usage
exit 1
;;
esac

View File

@ -1,37 +0,0 @@
version: "3.8"
services:
postgres:
image: postgres:__POSTGRES_TAG__
container_name: ragflow-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ragflow
POSTGRES_USER: ragflow
POSTGRES_PASSWORD: ragflow
volumes:
- ragflow-postgres:/var/lib/postgresql/data
redis:
image: redis:__REDIS_TAG__
container_name: ragflow-redis
restart: unless-stopped
command: ["redis-server", "--appendonly", "yes"]
volumes:
- ragflow-redis:/data
ragflow:
image: ragflow/ragflow:__RAGFLOW_TAG__
container_name: ragflow-app
restart: unless-stopped
depends_on:
- postgres
- redis
ports:
- "3001:3000"
environment:
DATABASE_URL: postgresql://ragflow:ragflow@postgres:5432/ragflow
REDIS_URL: redis://redis:6379/0
SECRET_KEY: changeme
volumes:
ragflow-postgres:
ragflow-redis:

View File

@ -1,29 +0,0 @@
ORG ?= your-org
REGISTRY ?= ghcr.io/$(ORG)/model-serving
VLLM_TAG ?= cuda12
SGLANG_TAG ?= cuda12
OLLAMA_TAG ?= latest
.PHONY: docker-build docker-push docker-build-vllm docker-build-sglang docker-build-ollama docker-push-vllm docker-push-sglang docker-push-ollama
docker-build: docker-build-vllm docker-build-sglang docker-build-ollama
docker-push: docker-push-vllm docker-push-sglang docker-push-ollama
docker-build-vllm:
docker build -t $(REGISTRY)/vllm:$(VLLM_TAG) vLLM
docker-build-sglang:
docker build -t $(REGISTRY)/sglang:$(SGLANG_TAG) SGLang
docker-build-ollama:
docker build -t $(REGISTRY)/ollama:$(OLLAMA_TAG) Ollama
docker-push-vllm:
docker push $(REGISTRY)/vllm:$(VLLM_TAG)
docker-push-sglang:
docker push $(REGISTRY)/sglang:$(SGLANG_TAG)
docker-push-ollama:
docker push $(REGISTRY)/ollama:$(OLLAMA_TAG)

View File

@ -1,28 +0,0 @@
# Ollama runtime image that leans on host NVIDIA drivers via container toolkit
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates unzip gnupg \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://ollama.com/download/ollama-linux-amd64.tgz | tar -xz -C /usr/local/bin \
&& chmod +x /usr/local/bin/ollama
RUN useradd -m -u 10001 app && mkdir -p /home/app/.ollama && chown -R app:app /home/app
USER app
ENV OLLAMA_HOST=0.0.0.0:11434 \
OLLAMA_MODELS=/home/app/.ollama/models \
OLLAMA_MODEL="phi3:latest"
EXPOSE 11434
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s CMD curl -fsS http://127.0.0.1:11434/api/tags || exit 1
ENTRYPOINT ["bash","-lc","set -euo pipefail; \
ollama serve & \
for i in $(seq 1 30); do sleep 1; curl -fsS http://127.0.0.1:11434/api/tags && break || true; done; \
ollama pull \"$OLLAMA_MODEL\" || true; \
wait -n"]

View File

@ -1,30 +0,0 @@
# CUDA 12.1 runtime base for SGLang
FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-venv python3-pip git curl ca-certificates build-essential \
&& rm -rf /var/lib/apt/lists/*
ENV PIP_NO_CACHE_DIR=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
RUN pip3 install --upgrade pip \
&& pip3 install --extra-index-url https://download.pytorch.org/whl/cu121 \
torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 \
&& pip3 install sglang==0.2.3 uvicorn fastapi
EXPOSE 30000
ENV SGLANG_MODEL="Qwen/Qwen2-7B-Instruct" \
SGLANG_PORT=30000 \
SGLANG_ARGS="--tp 1 --context-length 8192"
RUN useradd -m -u 10001 app && mkdir -p /models && chown -R app:app /models
USER app
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s CMD curl -fsS http://127.0.0.1:${SGLANG_PORT}/v1/models || exit 1
ENTRYPOINT ["bash","-lc","python3 -m sglang.launch_server --model \"$SGLANG_MODEL\" --port $SGLANG_PORT --trust-remote-code --enable-openai-compatible-api $SGLANG_ARGS"]

View File

@ -1,33 +0,0 @@
# CUDA 12.1 + cuDNN8 runtime base — tested with recent PyTorch wheels
FROM nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04
ARG DEBIAN_FRONTEND=noninteractive
# System deps + Python
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-venv python3-pip git curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
ENV PIP_NO_CACHE_DIR=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Install CUDA-enabled PyTorch + vLLM
RUN pip3 install --upgrade pip \
&& pip3 install --extra-index-url https://download.pytorch.org/whl/cu121 \
torch==2.3.1 torchvision==0.18.1 torchaudio==2.3.1 \
&& pip3 install vllm==0.5.2 uvicorn fastapi
EXPOSE 8000
ENV MODEL_PATH="meta-llama/Meta-Llama-3-8B-Instruct" \
VLLM_ARGS="--max-model-len 8192 --gpu-memory-utilization 0.9" \
HF_HOME=/models/.cache \
VLLM_WORKER_USE_GRAPH_EXECUTOR=1
RUN useradd -m -u 10001 app && mkdir -p /models && chown -R app:app /models
USER app
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s CMD curl -fsS http://127.0.0.1:8000/v1/models || exit 1
ENTRYPOINT ["bash","-lc","vllm serve \"$MODEL_PATH\" --port 8000 --api-key dummy $VLLM_ARGS"]

View File

@ -1,15 +0,0 @@
# OCI Charts
This repository stores reusable Helm charts published to `ghcr.io/x-evor`.
## Layout
- `apps/app-service`: reusable runtime chart for application services
- `postgresql`: PostgreSQL service chart with optional `stunnel` server/client
- `observability`: observability composition chart for server and agent components
## Release Model
- Registry: `oci://ghcr.io/x-evor`
- Each chart is versioned independently
- Runtime image tags are managed by GitOps values rather than chart versions

View File

@ -1,6 +0,0 @@
dependencies:
- name: app-service
repository: file://../app-service
version: 0.1.0
digest: sha256:29102607dbddc890cc60258ec869b75fd9e5f995fc8c5ee1f1a31b046b80e407
generated: "2026-04-02T17:55:26.238504+08:00"

View File

@ -1,11 +0,0 @@
apiVersion: v2
name: accounts-chart
description: Accounts service chart backed by the shared app-service subchart
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: app-service
version: 0.1.0
repository: file://../app-service
alias: service

View File

@ -1,25 +0,0 @@
service:
nameOverride: accounts
containerPort: 8080
service:
port: 80
global:
existingSecretName: accounts-env
repository: ghcr.io/x-evor/accounts
tag: latest
env:
PORT: "8080"
SERVICE_NAME: accounts
HEALTHCHECK_PATH: /healthz
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 20

View File

@ -1,6 +0,0 @@
apiVersion: v2
name: app-service
description: Reusable chart for core HTTP application services
type: application
version: 0.1.0
appVersion: "1.0.0"

View File

@ -1,26 +0,0 @@
{{- define "app-service.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "app-service.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- include "app-service.name" . -}}
{{- end -}}
{{- end -}}
{{- define "app-service.labels" -}}
app.kubernetes.io/name: {{ include "app-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
{{- end -}}
{{- define "app-service.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{- default (include "app-service.fullname" .) .Values.serviceAccount.name -}}
{{- else -}}
{{- default "default" .Values.serviceAccount.name -}}
{{- end -}}
{{- end -}}

View File

@ -1,128 +0,0 @@
{{- $global := .Values.global | default dict -}}
{{- $globalRepository := $global.repository | default "" -}}
{{- $globalTag := $global.tag | default "" -}}
{{- $globalEnv := $global.env | default dict -}}
{{- $localEnv := .Values.env | default dict -}}
{{- $env := mergeOverwrite (deepCopy $globalEnv) $localEnv -}}
{{- $existingSecretName := .Values.existingSecretName | default ($global.existingSecretName | default "") -}}
{{- $imageRepository := default $globalRepository .Values.image.repository -}}
{{- $imageTag := default $globalTag .Values.image.tag -}}
{{- $globalEnvFromSecretRefs := $global.envFromSecretRefs | default list -}}
{{- $localEnvFromSecretRefs := .Values.envFromSecretRefs | default list -}}
{{- $envFromSecretRefs := concat $globalEnvFromSecretRefs $localEnvFromSecretRefs -}}
{{- $globalExternalServices := index $global "external-service" | default list -}}
{{- $localExternalServices := index .Values "external-service" | default list -}}
{{- $externalServices := concat $globalExternalServices $localExternalServices -}}
{{- if $externalServices -}}
{{- $_ := set $env "EXTERNAL_SERVICES" (join "," $externalServices) -}}
{{- end -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "app-service.fullname" . }}
labels:
{{- include "app-service.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
revisionHistoryLimit: 3
strategy:
type: {{ .Values.strategy.type }}
rollingUpdate:
maxUnavailable: {{ .Values.strategy.rollingUpdate.maxUnavailable }}
maxSurge: {{ .Values.strategy.rollingUpdate.maxSurge }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "app-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
{{- include "app-service.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
{{- if and .Values.reloader.enabled $existingSecretName }}
secret.reloader.stakater.com/reload: {{ default $existingSecretName .Values.reloader.secretMatch | quote }}
{{- end }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ include "app-service.serviceAccountName" . }}
{{- with .Values.image.pullSecrets }}
imagePullSecrets:
{{- range . }}
- name: {{ . }}
{{- end }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.initContainers }}
initContainers:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: app
image: "{{ $imageRepository }}:{{ $imageTag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- with .Values.command }}
command:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.args }}
args:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- if .Values.workingDir }}
workingDir: {{ .Values.workingDir | quote }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.containerPort }}
{{- if $env }}
env:
{{- range $key := keys $env | sortAlpha }}
- name: {{ $key }}
value: {{ index $env $key | quote }}
{{- end }}
{{- end }}
{{- if or $existingSecretName $envFromSecretRefs }}
envFrom:
{{- if $existingSecretName }}
- secretRef:
name: {{ $existingSecretName }}
{{- end }}
{{- range $envFromSecretRefs }}
- secretRef:
name: {{ . }}
{{- end }}
{{- end }}
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
{{- with .Values.extraContainers }}
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -1,35 +0,0 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "app-service.fullname" . }}
labels:
{{- include "app-service.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- with .Values.ingress.tls }}
tls:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ default "Prefix" .pathType }}
backend:
service:
name: {{ include "app-service.fullname" $ }}
port:
number: {{ default $.Values.service.port .servicePort }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -1,14 +0,0 @@
{{- if .Values.pdb.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "app-service.fullname" . }}
labels:
{{- include "app-service.labels" . | nindent 4 }}
spec:
minAvailable: {{ .Values.pdb.minAvailable }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "app-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View File

@ -1,19 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "app-service.fullname" . }}
labels:
{{- include "app-service.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
selector:
app.kubernetes.io/name: {{ include "app-service.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: http

View File

@ -1,12 +0,0 @@
{{- if .Values.serviceAccount.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "app-service.serviceAccountName" . }}
labels:
{{- include "app-service.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -1,94 +0,0 @@
nameOverride: ""
fullnameOverride: ""
replicaCount: 1
image:
repository: ""
tag: ""
pullPolicy: IfNotPresent
pullSecrets: []
command: []
args: []
workingDir: ""
containerPort: 8080
service:
port: 80
type: ClusterIP
annotations: {}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
podLabels: {}
podAnnotations: {}
serviceAccount:
create: false
name: ""
annotations: {}
global:
repository: ""
tag: ""
env: {}
existingSecretName: ""
external-service: []
envFromSecretRefs: []
# Local overrides remain available for backwards compatibility.
env: {}
existingSecretName: ""
external-service: []
envFromSecretRefs: []
initContainers: []
extraContainers: []
volumeMounts: []
volumes: []
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 20
pdb:
enabled: true
minAvailable: 1
ingress:
enabled: false
className: ""
annotations: {}
tls: []
hosts: []
nodeSelector: {}
tolerations: []
affinity: {}
reloader:
enabled: true
secretMatch: ""

View File

@ -1,6 +0,0 @@
dependencies:
- name: app-service
repository: file://../app-service
version: 0.1.0
digest: sha256:29102607dbddc890cc60258ec869b75fd9e5f995fc8c5ee1f1a31b046b80e407
generated: "2026-04-02T17:55:26.213216+08:00"

View File

@ -1,11 +0,0 @@
apiVersion: v2
name: console-chart
description: Console service chart backed by the shared app-service subchart
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: app-service
version: 0.1.0
repository: file://../app-service
alias: service

View File

@ -1,31 +0,0 @@
service:
nameOverride: console
containerPort: 3000
service:
port: 80
global:
existingSecretName: console-env
repository: ghcr.io/x-evor/console
tag: latest
env:
PORT: "3000"
SERVICE_NAME: console
HEALTHCHECK_PATH: /
DOCS_SERVICE_URL: https://docs.svc.plus
NEXT_PUBLIC_DOCS_BASE_URL: https://docs.svc.plus
external-service:
- docs.svc.plus
- xworkmate.svc.plus
- openclaw-gateway.svc.plus
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 30
periodSeconds: 20

View File

@ -1,6 +0,0 @@
dependencies:
- name: app-service
repository: file://../app-service
version: 0.1.0
digest: sha256:29102607dbddc890cc60258ec869b75fd9e5f995fc8c5ee1f1a31b046b80e407
generated: "2026-04-02T17:55:26.26398+08:00"

View File

@ -1,11 +0,0 @@
apiVersion: v2
name: rag-server-chart
description: RAG server chart backed by the shared app-service subchart
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: app-service
version: 0.1.0
repository: file://../app-service
alias: service

View File

@ -1,25 +0,0 @@
service:
nameOverride: rag-server
containerPort: 8080
service:
port: 80
global:
existingSecretName: rag-server-env
repository: ghcr.io/x-evor/rag-server
tag: latest
env:
PORT: "8080"
SERVICE_NAME: rag-server
HEALTHCHECK_PATH: /healthz
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 20

View File

@ -1,18 +0,0 @@
apiVersion: v2
name: observability
description: Observability composition chart for server and agent components
type: application
version: 0.1.0
appVersion: "1.0.0"
keywords:
- observability
- prometheus
- victoria
- grafana
- otel
home: https://github.com/cloud-neutral-toolkit/observability.svc.plus
sources:
- https://github.com/cloud-neutral-toolkit/observability.svc.plus
maintainers:
- name: Cloud-Neutral Toolkit
email: admin@svc.plus

View File

@ -1,4 +0,0 @@
{{- range .Values.extraObjects }}
---
{{ toYaml . }}
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.server.grafana.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.server.grafana.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.server.grafana.releaseName }}
chart:
spec:
chart: {{ .Values.server.grafana.chart.name }}
version: {{ .Values.server.grafana.chart.version | quote }}
sourceRef:
kind: {{ .Values.server.grafana.sourceRef.kind }}
name: {{ .Values.server.grafana.sourceRef.name }}
namespace: {{ .Values.server.grafana.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.server.grafana.values | nindent 4 }}
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.agent.nodeExporter.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.agent.nodeExporter.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.agent.nodeExporter.releaseName }}
chart:
spec:
chart: {{ .Values.agent.nodeExporter.chart.name }}
version: {{ .Values.agent.nodeExporter.chart.version | quote }}
sourceRef:
kind: {{ .Values.agent.nodeExporter.sourceRef.kind }}
name: {{ .Values.agent.nodeExporter.sourceRef.name }}
namespace: {{ .Values.agent.nodeExporter.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.agent.nodeExporter.values | nindent 4 }}
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.server.otelConnector.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.server.otelConnector.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.server.otelConnector.releaseName }}
chart:
spec:
chart: {{ .Values.server.otelConnector.chart.name }}
version: {{ .Values.server.otelConnector.chart.version | quote }}
sourceRef:
kind: {{ .Values.server.otelConnector.sourceRef.kind }}
name: {{ .Values.server.otelConnector.sourceRef.name }}
namespace: {{ .Values.server.otelConnector.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.server.otelConnector.values | nindent 4 }}
{{- end }}

View File

@ -1,65 +0,0 @@
{{- if .Values.agent.processExporter.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.agent.processExporter.serviceAccountName }}
namespace: {{ .Values.namespaces.observability }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: process-exporter-config
namespace: {{ .Values.namespaces.observability }}
data:
config.yaml: |
{{- .Values.agent.processExporter.config | nindent 4 }}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: process-exporter
namespace: {{ .Values.namespaces.observability }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: process-exporter
template:
metadata:
labels:
app.kubernetes.io/name: process-exporter
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "{{ .Values.agent.processExporter.port }}"
spec:
serviceAccountName: {{ .Values.agent.processExporter.serviceAccountName }}
hostPID: true
containers:
- name: process-exporter
image: "{{ .Values.agent.processExporter.image.repository }}:{{ .Values.agent.processExporter.image.tag }}"
imagePullPolicy: {{ .Values.agent.processExporter.image.pullPolicy }}
args:
- --procfs
- /host/proc
- --config.path
- /etc/process-exporter/config.yaml
- --web.listen-address=:{{ .Values.agent.processExporter.port }}
ports:
- name: metrics
containerPort: {{ .Values.agent.processExporter.port }}
protocol: TCP
resources:
{{- toYaml .Values.agent.processExporter.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/process-exporter
- name: proc
mountPath: /host/proc
readOnly: true
volumes:
- name: config
configMap:
name: process-exporter-config
- name: proc
hostPath:
path: /proc
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.server.prometheus.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.server.prometheus.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.server.prometheus.releaseName }}
chart:
spec:
chart: {{ .Values.server.prometheus.chart.name }}
version: {{ .Values.server.prometheus.chart.version | quote }}
sourceRef:
kind: {{ .Values.server.prometheus.sourceRef.kind }}
name: {{ .Values.server.prometheus.sourceRef.name }}
namespace: {{ .Values.server.prometheus.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.server.prometheus.values | nindent 4 }}
{{- end }}

View File

@ -1,69 +0,0 @@
{{- if .Values.agent.vector.enabled }}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ .Values.agent.vector.serviceAccountName }}
namespace: {{ .Values.namespaces.observability }}
---
apiVersion: v1
kind: ConfigMap
metadata:
name: vector-agent-config
namespace: {{ .Values.namespaces.observability }}
data:
vector.yaml: |
{{- .Values.agent.vector.config | nindent 4 }}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: vector-agent
namespace: {{ .Values.namespaces.observability }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: vector-agent
template:
metadata:
labels:
app.kubernetes.io/name: vector-agent
spec:
serviceAccountName: {{ .Values.agent.vector.serviceAccountName }}
containers:
- name: vector
image: "{{ .Values.agent.vector.image.repository }}:{{ .Values.agent.vector.image.tag }}"
imagePullPolicy: {{ .Values.agent.vector.image.pullPolicy }}
resources:
{{- toYaml .Values.agent.vector.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/vector
{{- if .Values.agent.vector.volume.enabled }}
- name: {{ .Values.agent.vector.volume.name }}
mountPath: {{ .Values.agent.vector.volume.mountPath }}
{{- end }}
- name: var-log
mountPath: /var/log
readOnly: true
- name: machine-id
mountPath: /etc/machine-id
readOnly: true
volumes:
- name: config
configMap:
name: vector-agent-config
{{- if .Values.agent.vector.volume.enabled }}
- name: {{ .Values.agent.vector.volume.name }}
emptyDir:
{{- if .Values.agent.vector.volume.sizeLimit }}
sizeLimit: {{ .Values.agent.vector.volume.sizeLimit }}
{{- end }}
{{- end }}
- name: var-log
hostPath:
path: /var/log
- name: machine-id
hostPath:
path: /etc/machine-id
type: File
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.server.victoriaLogs.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.server.victoriaLogs.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.server.victoriaLogs.releaseName }}
chart:
spec:
chart: {{ .Values.server.victoriaLogs.chart.name }}
version: {{ .Values.server.victoriaLogs.chart.version | quote }}
sourceRef:
kind: {{ .Values.server.victoriaLogs.sourceRef.kind }}
name: {{ .Values.server.victoriaLogs.sourceRef.name }}
namespace: {{ .Values.server.victoriaLogs.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.server.victoriaLogs.values | nindent 4 }}
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.server.victoriaMetrics.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.server.victoriaMetrics.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.server.victoriaMetrics.releaseName }}
chart:
spec:
chart: {{ .Values.server.victoriaMetrics.chart.name }}
version: {{ .Values.server.victoriaMetrics.chart.version | quote }}
sourceRef:
kind: {{ .Values.server.victoriaMetrics.sourceRef.kind }}
name: {{ .Values.server.victoriaMetrics.sourceRef.name }}
namespace: {{ .Values.server.victoriaMetrics.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.server.victoriaMetrics.values | nindent 4 }}
{{- end }}

View File

@ -1,26 +0,0 @@
{{- if .Values.server.victoriaTraces.enabled }}
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: {{ .Values.server.victoriaTraces.releaseName }}
namespace: {{ .Values.namespaces.observability }}
spec:
interval: 10m0s
releaseName: {{ .Values.server.victoriaTraces.releaseName }}
chart:
spec:
chart: {{ .Values.server.victoriaTraces.chart.name }}
version: {{ .Values.server.victoriaTraces.chart.version | quote }}
sourceRef:
kind: {{ .Values.server.victoriaTraces.sourceRef.kind }}
name: {{ .Values.server.victoriaTraces.sourceRef.name }}
namespace: {{ .Values.server.victoriaTraces.sourceRef.namespace }}
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
{{- toYaml .Values.server.victoriaTraces.values | nindent 4 }}
{{- end }}

View File

@ -1,199 +0,0 @@
namespaces:
observability: observability
server:
prometheus:
enabled: true
releaseName: prometheus
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
chart:
name: prometheus
version: ">=25.0.0 <26.0.0"
values:
server:
persistentVolume:
enabled: true
size: 20Gi
prometheus-node-exporter:
enabled: false
victoriaMetrics:
enabled: true
releaseName: victoria-metrics
sourceRef:
kind: HelmRepository
name: victoria-metrics
namespace: flux-system
chart:
name: victoria-metrics-single
version: ">=0.13.0 <1.0.0"
values:
server:
persistentVolume:
enabled: true
size: 50Gi
victoriaLogs:
enabled: true
releaseName: victoria-logs
sourceRef:
kind: HelmRepository
name: victoria-metrics
namespace: flux-system
chart:
name: victoria-logs-single
version: ">=0.9.0 <1.0.0"
values:
server:
persistentVolume:
enabled: true
size: 50Gi
victoriaTraces:
enabled: true
releaseName: victoria-traces
sourceRef:
kind: HelmRepository
name: victoria-metrics
namespace: flux-system
chart:
name: victoria-traces-single
version: ">=0.0.1 <1.0.0"
values: {}
grafana:
enabled: false
releaseName: grafana
sourceRef:
kind: HelmRepository
name: grafana
namespace: flux-system
chart:
name: grafana
version: ">=8.0.0 <9.0.0"
values:
initChownData:
enabled: false
podSecurityContext:
fsGroup: 472
securityContext:
runAsUser: 472
runAsGroup: 472
persistence:
enabled: true
size: 10Gi
otelConnector:
enabled: false
releaseName: otel-connector
sourceRef:
kind: HelmRepository
name: open-telemetry
namespace: flux-system
chart:
name: opentelemetry-collector
version: ">=0.104.0 <1.0.0"
values:
image:
repository: otel/opentelemetry-collector-contrib
mode: deployment
config:
receivers:
otlp:
protocols:
grpc: {}
http: {}
processors:
batch: {}
exporters:
debug: {}
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [debug]
metrics:
receivers: [otlp]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp]
processors: [batch]
exporters: [debug]
agent:
nodeExporter:
enabled: true
releaseName: node-exporter
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
chart:
name: prometheus-node-exporter
version: ">=4.30.0 <5.0.0"
values: {}
vector:
enabled: true
image:
repository: timberio/vector
tag: "0.36.0-distroless-libc"
pullPolicy: IfNotPresent
serviceAccountName: vector-agent
vlogsEndpoint: http://victoria-logs-victoria-logs-single-server.observability.svc.cluster.local:9428
config: |
data_dir: /vector-data-dir
sources:
journald:
type: journald
transforms:
normalize:
type: remap
inputs: ["journald"]
source: |
.cluster = "k3s"
.origin = "vector-agent"
sinks:
vlogs:
type: elasticsearch
inputs: ["normalize"]
endpoints:
- http://victoria-logs-victoria-logs-single-server.observability.svc.cluster.local:9428/insert/elasticsearch/
mode: bulk
compression: gzip
resources:
limits:
cpu: 300m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
volume:
enabled: true
name: vector-data-dir
mountPath: /vector-data-dir
sizeLimit: 1Gi
processExporter:
enabled: true
image:
repository: ncabatoff/process-exporter
tag: v0.8.3
pullPolicy: IfNotPresent
serviceAccountName: process-exporter
port: 9256
config: |
process_names:
- name: "{{.Comm}}"
cmdline:
- '.+'
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 25m
memory: 64Mi
extraObjects: []

View File

@ -1,19 +0,0 @@
apiVersion: v2
name: postgresql
description: PostgreSQL service chart with optional stunnel server and client for cloud-neutral deployments
type: application
version: 1.1.0
appVersion: "16.4"
keywords:
- postgresql
- database
- vector
- search
- queue
home: https://github.com/cloud-neutral-toolkit/postgresql.svc.plus
sources:
- https://github.com/cloud-neutral-toolkit/postgresql.svc.plus
maintainers:
- name: Cloud-Neutral Toolkit
email: admin@svc.plus
icon: https://www.postgresql.org/media/img/about/press/elephant.png

View File

@ -1,82 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "postgresql.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "postgresql.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "postgresql.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "postgresql.labels" -}}
helm.sh/chart: {{ include "postgresql.chart" . }}
{{ include "postgresql.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "postgresql.selectorLabels" -}}
app.kubernetes.io/name: {{ include "postgresql.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "postgresql.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "postgresql.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Get the password secret name
*/}}
{{- define "postgresql.secretName" -}}
{{- if .Values.auth.existingSecret -}}
{{- .Values.auth.existingSecret -}}
{{- else -}}
{{- include "postgresql.fullname" . -}}
{{- end -}}
{{- end -}}
{{/*
Get the password key
*/}}
{{- define "postgresql.secretKey" -}}
{{- if .Values.auth.existingSecret -}}
{{- .Values.auth.secretKey -}}
{{- else -}}
password
{{- end -}}
{{- end -}}

View File

@ -1,13 +0,0 @@
{{- if and .Values.server.enabled .Values.initScripts.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "postgresql.fullname" . }}-init-scripts
labels:
{{- include "postgresql.labels" . | nindent 4 }}
data:
{{- range $key, $value := .Values.initScripts.scripts }}
{{ $key }}: |
{{- $value | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -1,11 +0,0 @@
{{- if and .Values.server.enabled .Values.stunnel.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "postgresql.fullname" . }}-stunnel-config
labels:
{{- include "postgresql.labels" . | nindent 4 }}
data:
stunnel.conf: |
{{- .Values.stunnel.config | nindent 4 }}
{{- end }}

View File

@ -1,25 +0,0 @@
{{- if .Values.server.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "postgresql.fullname" . }}-config
labels:
{{- include "postgresql.labels" . | nindent 4 }}
data:
postgresql.conf: |
{{- .Values.postgresql.config | nindent 4 }}
{{- if .Values.postgresql.pgHba }}
pg_hba.conf: |
# TYPE DATABASE USER ADDRESS METHOD
# Default entries
local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
host all all 0.0.0.0/0 md5
host all all ::/0 md5
# Custom entries
{{- .Values.postgresql.pgHba | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -1,11 +0,0 @@
{{- if and .Values.server.enabled (not .Values.auth.existingSecret) }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "postgresql.fullname" . }}
labels:
{{- include "postgresql.labels" . | nindent 4 }}
type: Opaque
data:
password: {{ .Values.auth.password | b64enc | quote }}
{{- end }}

View File

@ -1,21 +0,0 @@
{{- if and .Values.server.enabled .Values.metrics.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "postgresql.fullname" . }}-metrics
labels:
{{- include "postgresql.labels" . | nindent 4 }}
{{- with .Values.metrics.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.metrics.service.type }}
ports:
- port: {{ .Values.metrics.service.port }}
targetPort: metrics
protocol: TCP
name: metrics
selector:
{{- include "postgresql.selectorLabels" . | nindent 4 }}
{{- end }}

View File

@ -1,27 +0,0 @@
{{- if .Values.server.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "postgresql.fullname" . }}
labels:
{{- include "postgresql.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: postgres
protocol: TCP
name: postgres
{{- if .Values.stunnel.enabled }}
- port: {{ .Values.stunnel.port }}
targetPort: stunnel
protocol: TCP
name: stunnel
{{- end }}
selector:
{{- include "postgresql.selectorLabels" . | nindent 4 }}
{{- end }}

View File

@ -1,12 +0,0 @@
{{- if and .Values.server.enabled .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "postgresql.serviceAccountName" . }}
labels:
{{- include "postgresql.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -1,203 +0,0 @@
{{- if .Values.server.enabled }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "postgresql.fullname" . }}
labels:
{{- include "postgresql.labels" . | nindent 4 }}
spec:
serviceName: {{ include "postgresql.fullname" . }}
replicas: 1
selector:
matchLabels:
{{- include "postgresql.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "postgresql.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "postgresql.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: postgresql
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: postgres
containerPort: 5432
protocol: TCP
env:
- name: POSTGRES_USER
value: {{ .Values.auth.username | quote }}
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "postgresql.secretName" . }}
key: {{ include "postgresql.secretKey" . }}
- name: POSTGRES_DB
value: {{ .Values.auth.database | quote }}
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U {{ .Values.auth.username }}
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U {{ .Values.auth.username }}
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
- name: config
mountPath: /etc/postgresql
{{- if .Values.initScripts.enabled }}
- name: init-scripts
mountPath: /docker-entrypoint-initdb.d
{{- end }}
{{- if .Values.tls.enabled }}
- name: tls-certs
mountPath: /etc/postgresql/certs
readOnly: true
{{- end }}
{{- if .Values.stunnel.enabled }}
- name: stunnel
image: "{{ .Values.stunnel.image.repository }}:{{ .Values.stunnel.image.tag }}"
imagePullPolicy: {{ .Values.stunnel.image.pullPolicy }}
ports:
- name: stunnel
containerPort: {{ .Values.stunnel.port }}
protocol: TCP
volumeMounts:
- name: stunnel-config
mountPath: /etc/stunnel/stunnel.conf
subPath: stunnel.conf
{{- if .Values.stunnel.certificatesSecret }}
- name: stunnel-certs
mountPath: /etc/stunnel/certs
readOnly: true
{{- end }}
{{- end }}
{{- if .Values.metrics.enabled }}
- name: metrics
image: "{{ .Values.metrics.image.repository }}:{{ .Values.metrics.image.tag }}"
imagePullPolicy: {{ .Values.metrics.image.pullPolicy }}
ports:
- name: metrics
containerPort: 9187
protocol: TCP
env:
- name: DATA_SOURCE_NAME
value: "postgresql://{{ .Values.auth.username }}:$(POSTGRES_PASSWORD)@localhost:5432/{{ .Values.auth.database }}?sslmode=disable"
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "postgresql.secretName" . }}
key: {{ include "postgresql.secretKey" . }}
resources:
{{- toYaml .Values.metrics.resources | nindent 12 }}
{{- end }}
volumes:
- name: config
configMap:
name: {{ include "postgresql.fullname" . }}-config
{{- if .Values.initScripts.enabled }}
- name: init-scripts
configMap:
name: {{ include "postgresql.fullname" . }}-init-scripts
{{- end }}
{{- if .Values.tls.enabled }}
- name: tls-certs
secret:
secretName: {{ .Values.tls.certificatesSecret }}
defaultMode: 0600
{{- end }}
{{- if .Values.stunnel.enabled }}
- name: stunnel-config
configMap:
name: {{ include "postgresql.fullname" . }}-stunnel-config
{{- if .Values.stunnel.certificatesSecret }}
- name: stunnel-certs
secret:
secretName: {{ .Values.stunnel.certificatesSecret }}
defaultMode: 0600
{{- end }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.persistence.enabled }}
volumeClaimTemplates:
- metadata:
name: data
{{- with .Values.persistence.annotations }}
annotations:
{{- toYaml . | nindent 10 }}
{{- end }}
spec:
accessModes:
{{- range .Values.persistence.accessModes }}
- {{ . | quote }}
{{- end }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- with .Values.persistence.selector }}
selector:
{{- toYaml . | nindent 10 }}
{{- end }}
{{- else }}
- name: data
emptyDir: {}
{{- end }}
{{- end }}

View File

@ -1,11 +0,0 @@
{{- if .Values.stunnelClient.enabled }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "postgresql.fullname" . }}-stunnel-client
labels:
{{- include "postgresql.labels" . | nindent 4 }}
data:
stunnel.conf: |
{{- .Values.stunnelClient.config | nindent 4 }}
{{- end }}

View File

@ -1,40 +0,0 @@
{{- if .Values.stunnelClient.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "postgresql.fullname" . }}-stunnel-client
labels:
{{- include "postgresql.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.stunnelClient.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "postgresql.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: stunnel-client
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "postgresql.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: stunnel-client
spec:
containers:
- name: stunnel-client
image: "{{ .Values.stunnelClient.image.repository }}:{{ .Values.stunnelClient.image.tag }}"
imagePullPolicy: {{ .Values.stunnelClient.image.pullPolicy }}
ports:
- name: postgres
containerPort: {{ .Values.stunnelClient.service.port }}
protocol: TCP
resources:
{{- toYaml .Values.stunnelClient.resources | nindent 12 }}
volumeMounts:
- name: config
mountPath: /etc/stunnel/stunnel.conf
subPath: stunnel.conf
volumes:
- name: config
configMap:
name: {{ include "postgresql.fullname" . }}-stunnel-client
{{- end }}

View File

@ -1,23 +0,0 @@
{{- if .Values.stunnelClient.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "postgresql.fullname" . }}-stunnel-client
labels:
{{- include "postgresql.labels" . | nindent 4 }}
{{- with .Values.stunnelClient.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.stunnelClient.service.type }}
ports:
- name: postgres
port: {{ .Values.stunnelClient.service.port }}
targetPort: postgres
protocol: TCP
selector:
app.kubernetes.io/name: {{ include "postgresql.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: stunnel-client
{{- end }}

View File

@ -1,245 +0,0 @@
# Default values for postgresql chart
server:
enabled: true
# Image configuration
image:
repository: postgres-extensions
tag: "16"
pullPolicy: IfNotPresent
# Image pull secrets for private registries
imagePullSecrets: []
# Override name
nameOverride: ""
fullnameOverride: ""
# Service account
serviceAccount:
create: true
annotations: {}
name: ""
# Pod annotations
podAnnotations: {}
# Pod security context
podSecurityContext:
fsGroup: 999
# Container security context
securityContext:
runAsUser: 999
runAsNonRoot: true
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# Service configuration
service:
type: ClusterIP
port: 5432
annotations: {}
# Ingress (not typically used for PostgreSQL, but available)
ingress:
enabled: false
className: ""
annotations: {}
hosts: []
tls: []
# PostgreSQL authentication
auth:
username: postgres
password: "" # Set this or use existingSecret
database: postgres
existingSecret: "" # Name of existing secret with password
secretKey: "password" # Key in the secret
# PostgreSQL configuration
postgresql:
# Custom postgresql.conf settings
config: |
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 16MB
maintenance_work_mem = 64MB
max_connections = 100
wal_buffers = 16MB
checkpoint_completion_target = 0.9
random_page_cost = 1.1
effective_io_concurrency = 200
log_min_duration_statement = 1000
# Custom pg_hba.conf entries (appended to defaults)
pgHba: |
# Custom entries
# host all all 0.0.0.0/0 md5
# Initialization scripts
initScripts:
enabled: true
# Scripts will be created from the scripts below
scripts:
01-init-extensions.sql: |
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pg_jieba;
CREATE EXTENSION IF NOT EXISTS pgmq;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
# Persistence
persistence:
enabled: true
storageClass: "" # Use default storage class
accessModes:
- ReadWriteOnce
size: 10Gi
annotations: {}
selector: {}
# Resource limits
resources:
limits:
cpu: 2000m
memory: 2Gi
requests:
cpu: 500m
memory: 1Gi
# Liveness probe
livenessProbe:
enabled: true
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
# Readiness probe
readinessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
# Node selector
nodeSelector: {}
# Tolerations
tolerations: []
# Affinity
affinity: {}
# Metrics (for Prometheus)
metrics:
enabled: false
image:
repository: prometheuscommunity/postgres-exporter
tag: v0.15.0
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 9187
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9187"
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
# Backup configuration (optional)
backup:
enabled: false
schedule: "0 2 * * *" # Daily at 2 AM
retention: 7 # Keep 7 days of backups
storageClass: ""
size: 20Gi
# TLS/SSL configuration
tls:
enabled: false
certificatesSecret: "" # Name of secret containing tls.crt and tls.key
certFilename: "tls.crt"
certKeyFilename: "tls.key"
certCAFilename: "ca.crt"
# Stunnel sidecar for TLS over TCP
stunnel:
enabled: false
image:
repository: dweomer/stunnel
tag: latest
pullPolicy: IfNotPresent
port: 5433
certificatesSecret: "" # Name of secret containing stunnel certificates
config: |
[postgres-tunnel]
client = no
accept = 0.0.0.0:5433
connect = 127.0.0.1:5432
cert = /etc/stunnel/certs/server-cert.pem
key = /etc/stunnel/certs/server-key.pem
sslVersion = TLSv1.2
options = NO_SSLv2
options = NO_SSLv3
ciphers = HIGH:!aNULL:!MD5
# NetworkPolicy
networkPolicy:
enabled: false
policyTypes:
- Ingress
ingress:
- from:
- podSelector: {}
ports:
- protocol: TCP
port: 5432
# PodDisruptionBudget
podDisruptionBudget:
enabled: false
minAvailable: 1
# maxUnavailable: 1
stunnelClient:
enabled: false
replicaCount: 1
image:
repository: dweomer/stunnel
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 5432
annotations: {}
config: |
[postgres-client]
client = yes
accept = 0.0.0.0:5432
connect = postgresql.database.svc.cluster.local:5433
verifyChain = no
sslVersion = TLSv1.2
options = NO_SSLv2
options = NO_SSLv3
ciphers = HIGH:!aNULL:!MD5
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 25m
memory: 64Mi

View File

@ -1,35 +0,0 @@
ORG ?= your-org
IMAGE_REGISTRY ?= ghcr.io/$(ORG)/model-serving
CHART_NAME ?= model-serving
CHART_DIR := charts/$(CHART_NAME)
VERSION ?= 0.1.0
.PHONY: docker-build docker-push helm-lint helm-package helm-push install uninstall template
docker-build:
$(MAKE) -C ../base/cuda docker-build REGISTRY=$(IMAGE_REGISTRY)
docker-push:
$(MAKE) -C ../base/cuda docker-push REGISTRY=$(IMAGE_REGISTRY)
helm-lint:
helm lint $(CHART_DIR)
helm-package:
helm package $(CHART_DIR) --version $(VERSION) --app-version $(VERSION) -d charts/
helm-push: helm-package
helm push charts/$(CHART_NAME)-$(VERSION).tgz oci://ghcr.io/$(ORG)/helm
RELEASE ?= ms
NAMESPACE ?= llm
install:
kubectl create ns $(NAMESPACE) --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install $(RELEASE) $(CHART_DIR) -n $(NAMESPACE)
uninstall:
helm uninstall $(RELEASE) -n $(NAMESPACE) || true
template:
helm template $(RELEASE) $(CHART_DIR)

View File

@ -1,101 +0,0 @@
# CUDA LLM Serving — vLLM / SGLang / Ollama (Kubernetes)
This package bundles three CUDA-ready images plus a single Helm chart that can serve **multiple models** behind one host with **path-based routing** such as:
- `https://api.svc.plus/v1/llama3` → vLLM (OpenAI-compatible)
- `https://api.svc.plus/v1/qwen2` → SGLang (OpenAI-compatible)
- `https://api.svc.plus/v1/phi3` → Ollama `/api/*`
The Dockerfiles live under [`oci/base/cuda`](../base/cuda/), while the Helm chart is in [`charts/model-serving`](charts/model-serving/).
## Prerequisites
- Kubernetes ≥ 1.25
- NVIDIA GPUs on worker nodes + NVIDIA Container Toolkit
- Ingress Controller (e.g. NGINX) and TLS secret if using HTTPS
- (Optional) GitHub Container Registry (GHCR) for distributing images and charts
## Build & Publish
```bash
# 1) Build and push images to GHCR (adjust ORG)
make -C oci/base/cuda ORG=svc-design docker-build docker-push
# 2) Lint & package the chart
make -C oci/multi-model-LLM helm-lint helm-package VERSION=0.1.0
# 3) Push chart as OCI to GHCR
make -C oci/multi-model-LLM ORG=svc-design VERSION=0.1.0 helm-push
```
> Authenticate GHCR first:
>
> ```bash
> echo $GHCR_TOKEN | docker login ghcr.io -u <GITHUB_USER> --password-stdin
> helm registry login ghcr.io -u <GITHUB_USER> -p $GHCR_TOKEN
> ```
## Install
```bash
# install into namespace llm with release name ms
make -C oci/multi-model-LLM install RELEASE=ms NAMESPACE=llm
```
## Configure Models
Edit [`charts/model-serving/values.yaml`](charts/model-serving/values.yaml) and extend the `models:` list. Example:
```yaml
models:
- name: llama3-8b-vllm
engine: vllm
image: "model-serving/vllm"
tag: "cuda12"
path: v1/llama3
env:
- name: MODEL_PATH
value: meta-llama/Meta-Llama-3-8B-Instruct
- name: VLLM_ARGS
value: --max-model-len 8192 --gpu-memory-utilization 0.9
resources:
limits:
nvidia.com/gpu: 1
- name: qwen2-7b-sglang
engine: sglang
image: "model-serving/sglang"
tag: "cuda12"
path: v1/qwen2
env:
- name: SGLANG_MODEL
value: Qwen/Qwen2-7B-Instruct
- name: phi3-ollama
engine: ollama
image: "model-serving/ollama"
tag: latest
path: v1/phi3
env:
- name: OLLAMA_MODEL
value: phi3:latest
```
Deployments and services are generated per model, and a single ingress exposes them under unique paths.
## Runtime Notes
* **GPU scheduling**: Templates set `runtimeClassName: nvidia` and default GPU limits. Ensure the cluster has the NVIDIA device plugin and RuntimeClass defined, or override `runtimeClassName` per model.
* **Storage**: vLLM/SGLang cache defaults to the container filesystem. Mount an external volume by extending the template if persistence is required.
* **Authentication**: vLLM launches with a dummy API key. Place an API gateway or ingress authentication in front for production.
* **Scaling**: Increase `replicas` per model and add engine-specific flags through environment variables for tensor parallelism or sharding.
## Uninstall
```bash
make -C oci/multi-model-LLM uninstall RELEASE=ms NAMESPACE=llm
```
## License
MIT

View File

@ -1,6 +0,0 @@
*.tgz
*.swp
*.swo
.DS_Store
.git/
.github/

Some files were not shown because too many files have changed in this diff Show More