mirror of
https://github.com/sredevopsorg/deepseek-kubernetes-sandbox.git
synced 2026-11-03 01:13:24 +00:00
feat(repo)!: restructure around the dsh-harness image and one example per implementation
- the root Dockerfile is the primary artifact; versions.env is the single source of truth
- merge gateway-api/dsh-demo into examples/dsh-agents-as-a-service (manifests/, exposure variants)
- move the single-tenant template into examples/minimal-single-tenant
- publish the gateway as a nested package under the repository image
- mirror the DSH ref for release identities (first release dsh-v0.2.1-rc.2-r2)
- CI: lint/python/manifests/gateway-image, reusable multi-arch build, tag-driven release, weekly e2e
- docs: README, docs/{main-image,examples,gitflow,versioning}, example READMEs, AGENTS.md, governance
- SSO is opt-in again; gateway pull policy and cookie defaults match the documented demo
BREAKING CHANGE: example manifest paths, the gateway image name, and the removal
of deploy/ and gateway-api/ affect existing deployments; see the migration table
in README.md.
This commit is contained in:
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Bug report
|
||||
description: Report a reproducible problem with the image, an example or the tooling.
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Do not include API keys, tokens or other secrets. Report security
|
||||
issues privately via SECURITY.md instead.
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened
|
||||
description: The observed behavior and the expected behavior.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: Commands, manifests and versions. Redact secrets.
|
||||
placeholder: |
|
||||
make image
|
||||
docker run ...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: image-tag
|
||||
attributes:
|
||||
label: Image or release
|
||||
description: The pinned tag or digest, for example dsh-v0.2.1-rc.2-r2.
|
||||
- type: input
|
||||
id: cluster
|
||||
attributes:
|
||||
label: Environment
|
||||
description: Kubernetes distribution/version, kind version, agent-sandbox version.
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant logs
|
||||
description: Paste the smallest useful excerpt. Remove tokens and API keys.
|
||||
render: shell
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security vulnerability
|
||||
url: https://github.com/sredevopsorg/deepseek-kubernetes-sandbox/security/advisories/new
|
||||
about: Report security issues privately; do not open a public issue.
|
||||
- name: Documentation
|
||||
url: https://github.com/sredevopsorg/deepseek-kubernetes-sandbox/tree/main/docs
|
||||
about: Read the repository docs before opening an issue.
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: Feature request
|
||||
description: Propose a new example, image capability or workflow improvement.
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem or use case
|
||||
description: What are you trying to do, and what blocks you today?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: Proposed solution
|
||||
description: Describe the shape you have in mind. For a new example, say which implementation it demonstrates.
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: area
|
||||
attributes:
|
||||
label: Area
|
||||
options:
|
||||
- Main DSH Web image
|
||||
- Existing example
|
||||
- New example
|
||||
- CI / release automation
|
||||
- Documentation
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives considered
|
||||
28
.github/dependabot.yml
vendored
Normal file
28
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
actions:
|
||||
patterns: ["*"]
|
||||
|
||||
- package-ecosystem: pip
|
||||
directory: /examples/dsh-agents-as-a-service/gateway
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
gateway-python:
|
||||
patterns: ["*"]
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: /examples/dsh-agents-as-a-service/gateway
|
||||
schedule:
|
||||
interval: weekly
|
||||
31
.github/pull_request_template.md
vendored
Normal file
31
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
## Summary
|
||||
|
||||
<!-- What changes, and why? Link the issue: Closes #123 -->
|
||||
|
||||
## Type of change
|
||||
|
||||
- [ ] feat: new capability or example
|
||||
- [ ] fix: bug fix
|
||||
- [ ] docs: documentation only
|
||||
- [ ] refactor: no behavior change
|
||||
- [ ] build / ci: tooling, images, workflows
|
||||
- [ ] chore: maintenance
|
||||
|
||||
## Breaking change
|
||||
|
||||
- [ ] Yes, and the migration is documented in the description
|
||||
- [ ] No
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] make check passes (versions, example layout, manifest YAML)
|
||||
- [ ] make test passes (gateway unit tests)
|
||||
- [ ] make lint passes (ruff, shellcheck; yamllint/actionlint where installed)
|
||||
- [ ] Docs updated for the change (root README, docs/, example README)
|
||||
- [ ] No secrets or usable credentials committed
|
||||
- [ ] CHANGELOG.md was not edited by hand (generated by git-cliff)
|
||||
- [ ] Version pins changed only through versions.env / make set-version
|
||||
|
||||
## Testing
|
||||
|
||||
<!-- Commands run, clusters used, results. Mention if kind e2e was skipped. -->
|
||||
160
.github/workflows/_build-image.yaml
vendored
Normal file
160
.github/workflows/_build-image.yaml
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
# Reusable multi-arch image build: one workflow, called by build-main,
|
||||
# build-gateway and release. Builds natively per platform (no QEMU), pushes by
|
||||
# digest, then merges the manifest list with SBOM and provenance attestations.
|
||||
name: Reusable multi-arch image build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
image:
|
||||
description: Fully qualified image name without a tag
|
||||
required: true
|
||||
type: string
|
||||
version:
|
||||
description: Version recorded in the OCI labels
|
||||
required: true
|
||||
type: string
|
||||
context:
|
||||
description: Docker build context
|
||||
required: true
|
||||
type: string
|
||||
dockerfile:
|
||||
description: Dockerfile path, relative to the context
|
||||
required: false
|
||||
default: Dockerfile
|
||||
type: string
|
||||
build-args:
|
||||
description: Newline-separated build args
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
tags:
|
||||
description: Newline-separated fully qualified tags to publish
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ matrix.platform }}
|
||||
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-latest' || 'ubuntu-24.04-arm' }}
|
||||
timeout-minutes: 120
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Prepare environment
|
||||
env:
|
||||
IMAGE: ${{ inputs.image }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
pair=$(printf '%s' "${{ matrix.platform }}" | tr '/' '-')
|
||||
echo "PLATFORM_PAIR=$pair" >> "$GITHUB_ENV"
|
||||
echo "CACHE_SCOPE=$(printf '%s' "$IMAGE" | tr '/' '-')" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push by digest
|
||||
id: build
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
with:
|
||||
context: ${{ inputs.context }}
|
||||
file: ${{ inputs.context }}/${{ inputs.dockerfile }}
|
||||
platforms: ${{ matrix.platform }}
|
||||
build-args: ${{ inputs.build-args }}
|
||||
labels: |
|
||||
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
||||
org.opencontainers.image.version=${{ inputs.version }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
outputs: type=image,name=${{ inputs.image }},push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=true
|
||||
sbom: true
|
||||
provenance: mode=max
|
||||
cache-from: type=gha,scope=${{ env.CACHE_SCOPE }}-${{ matrix.platform }}
|
||||
cache-to: type=gha,mode=max,scope=${{ env.CACHE_SCOPE }}-${{ matrix.platform }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
name: Merge manifest list
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create and push manifest list
|
||||
working-directory: /tmp/digests
|
||||
env:
|
||||
IMAGE: ${{ inputs.image }}
|
||||
TAGS: ${{ inputs.tags }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
args=()
|
||||
while IFS= read -r tag; do
|
||||
if [ -n "$tag" ]; then
|
||||
args+=("-t" "$tag")
|
||||
fi
|
||||
done <<< "$TAGS"
|
||||
refs=()
|
||||
for digest in *; do
|
||||
refs+=("$IMAGE@sha256:$digest")
|
||||
done
|
||||
docker buildx imagetools create "${args[@]}" "${refs[@]}"
|
||||
|
||||
- name: Inspect image
|
||||
env:
|
||||
IMAGE: ${{ inputs.image }}
|
||||
TAGS: ${{ inputs.tags }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
first=$(printf '%s\n' "$TAGS" | head -n1)
|
||||
docker buildx imagetools inspect "$first"
|
||||
echo "### Published ${{ inputs.image }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
printf '%s\n' "$TAGS" | sed 's/^/- /' >> "$GITHUB_STEP_SUMMARY"
|
||||
69
.github/workflows/build-gateway.yaml
vendored
Normal file
69
.github/workflows/build-gateway.yaml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# Development channel for the example gateway image: a merge that touches the
|
||||
# gateway publishes ghcr.io/<owner>/<repo>/dsh-agents-as-a-service/gateway:main.
|
||||
name: Build gateway image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- examples/dsh-agents-as-a-service/gateway/**
|
||||
- versions.env
|
||||
- .github/workflows/build-gateway.yaml
|
||||
- .github/workflows/_build-image.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
concurrency:
|
||||
group: build-gateway-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
meta:
|
||||
name: Read versions
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
identity: ${{ steps.meta.outputs.identity }}
|
||||
gateway_image: ${{ steps.meta.outputs.gateway_image }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Read versions.env
|
||||
id: meta
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
while IFS= read -r line; do
|
||||
case "$line" in ''|'#'*) continue ;; esac
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done < versions.env
|
||||
identity="$DSH_VERSION"
|
||||
[ -z "$RELEASE_REVISION" ] || identity="$DSH_VERSION-$RELEASE_REVISION"
|
||||
{
|
||||
echo "identity=$identity"
|
||||
echo "gateway_image=$GATEWAY_IMAGE"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
name: Build and push gateway image
|
||||
needs: meta
|
||||
uses: ./.github/workflows/_build-image.yaml
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
with:
|
||||
image: ${{ needs.meta.outputs.gateway_image }}
|
||||
version: ${{ needs.meta.outputs.identity }}
|
||||
context: examples/dsh-agents-as-a-service/gateway
|
||||
dockerfile: Dockerfile
|
||||
tags: |
|
||||
${{ needs.meta.outputs.gateway_image }}:main
|
||||
83
.github/workflows/build-main.yaml
vendored
Normal file
83
.github/workflows/build-main.yaml
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
# Development channel for the main DSH Web image: every merge to main that
|
||||
# touches the image definition publishes ghcr.io/<owner>/<repo>:main.
|
||||
# Releases are published by release.yaml with immutable version tags.
|
||||
name: Build main image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- Dockerfile
|
||||
- versions.env
|
||||
- .github/workflows/build-main.yaml
|
||||
- .github/workflows/_build-image.yaml
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
concurrency:
|
||||
group: build-main-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
meta:
|
||||
name: Read versions
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
outputs:
|
||||
identity: ${{ steps.meta.outputs.identity }}
|
||||
dsh_version: ${{ steps.meta.outputs.dsh_version }}
|
||||
pnpm_version: ${{ steps.meta.outputs.pnpm_version }}
|
||||
release_revision: ${{ steps.meta.outputs.release_revision }}
|
||||
ghcr_image: ${{ steps.meta.outputs.ghcr_image }}
|
||||
gateway_image: ${{ steps.meta.outputs.gateway_image }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Read versions.env
|
||||
id: meta
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
while IFS= read -r line; do
|
||||
case "$line" in ''|'#'*) continue ;; esac
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done < versions.env
|
||||
identity="$DSH_VERSION"
|
||||
[ -z "$RELEASE_REVISION" ] || identity="$DSH_VERSION-$RELEASE_REVISION"
|
||||
{
|
||||
echo "identity=$identity"
|
||||
echo "dsh_version=$DSH_VERSION"
|
||||
echo "pnpm_version=$PNPM_VERSION"
|
||||
echo "release_revision=$RELEASE_REVISION"
|
||||
echo "ghcr_image=$GHCR_IMAGE"
|
||||
echo "gateway_image=$GATEWAY_IMAGE"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
name: Build and push main image
|
||||
needs: meta
|
||||
uses: ./.github/workflows/_build-image.yaml
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
with:
|
||||
image: ${{ needs.meta.outputs.ghcr_image }}
|
||||
version: ${{ needs.meta.outputs.identity }}
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
build-args: |
|
||||
DSH_VERSION=${{ needs.meta.outputs.dsh_version }}
|
||||
RELEASE_REVISION=${{ needs.meta.outputs.release_revision }}
|
||||
PNPM_VERSION=${{ needs.meta.outputs.pnpm_version }}
|
||||
VCS_REF=${{ github.sha }}
|
||||
tags: |
|
||||
${{ needs.meta.outputs.ghcr_image }}:main
|
||||
257
.github/workflows/ci.yaml
vendored
Normal file
257
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
# Continuous integration: lint, unit tests, manifest dry-run, gateway image
|
||||
# smoke test and dependency review. Job names are the required branch-protection
|
||||
# checks documented in docs/gitflow.md.
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install Python linters
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python -m pip install --upgrade pip
|
||||
pip install "yamllint==1.38.0" "pyyaml==6.0.3"
|
||||
- name: Install actionlint
|
||||
env:
|
||||
ACTIONLINT_VERSION: v1.7.12
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="${ACTIONLINT_VERSION#v}"
|
||||
curl -fsSL -o /tmp/actionlint.tar.gz "https://github.com/rhysd/actionlint/releases/download/$ACTIONLINT_VERSION/actionlint_${version}_linux_amd64.tar.gz"
|
||||
tar -xzf /tmp/actionlint.tar.gz -C /tmp actionlint
|
||||
install -m 0755 /tmp/actionlint /usr/local/bin/actionlint
|
||||
actionlint -version
|
||||
- name: Install hadolint
|
||||
env:
|
||||
HADOLINT_VERSION: v2.12.0
|
||||
run: |
|
||||
set -euo pipefail
|
||||
curl -fsSL -o /tmp/hadolint "https://github.com/hadolint/hadolint/releases/download/$HADOLINT_VERSION/hadolint-Linux-x86_64"
|
||||
install -m 0755 /tmp/hadolint /usr/local/bin/hadolint
|
||||
hadolint --version
|
||||
- name: Repository checks
|
||||
run: |
|
||||
set -euo pipefail
|
||||
./scripts/check-versions.sh
|
||||
./scripts/check-examples.sh
|
||||
python scripts/check-manifests.py
|
||||
- name: Shellcheck
|
||||
run: shellcheck -x build.sh scripts/*.sh examples/*/scripts/*.sh
|
||||
- name: Actionlint
|
||||
run: actionlint -color
|
||||
- name: Yamllint
|
||||
run: yamllint .
|
||||
- name: Hadolint
|
||||
run: |
|
||||
set -euo pipefail
|
||||
hadolint Dockerfile
|
||||
hadolint examples/dsh-agents-as-a-service/gateway/Dockerfile
|
||||
- name: Documentation links
|
||||
uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2
|
||||
with:
|
||||
args: >-
|
||||
--offline --no-progress --verbose
|
||||
./README.md ./CONTRIBUTING.md ./SECURITY.md ./AGENTS.md ./docs ./examples
|
||||
|
||||
python:
|
||||
name: python
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
defaults:
|
||||
run:
|
||||
working-directory: examples/dsh-agents-as-a-service/gateway
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
examples/dsh-agents-as-a-service/gateway/requirements.txt
|
||||
examples/dsh-agents-as-a-service/gateway/requirements-dev.txt
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt -r requirements-dev.txt
|
||||
- name: Lint
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python -m ruff check .
|
||||
python -m ruff format --check .
|
||||
- name: Unit tests with coverage
|
||||
run: >-
|
||||
python -m pytest -q
|
||||
--cov=. --cov-report=xml:coverage.xml --cov-report=term
|
||||
--junitxml=junit.xml
|
||||
- name: Coverage summary
|
||||
run: |
|
||||
set -euo pipefail
|
||||
{
|
||||
echo "### Gateway test coverage"
|
||||
python -m coverage report
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: Upload test artefacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: gateway-test-results
|
||||
path: |
|
||||
examples/dsh-agents-as-a-service/gateway/junit.xml
|
||||
examples/dsh-agents-as-a-service/gateway/coverage.xml
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
|
||||
manifests:
|
||||
name: manifests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@1544676c07bb3570fcb70166beaa07c4528fd4aa # v1.15.1
|
||||
with:
|
||||
version: v0.33.0
|
||||
cluster_name: dsh-ci
|
||||
- name: Read versions.env
|
||||
run: |
|
||||
set -euo pipefail
|
||||
while IFS= read -r line; do
|
||||
case "$line" in ''|'#'*) continue ;; esac
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done < versions.env
|
||||
- name: Install agent-sandbox and Gateway API CRDs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
kubectl apply -f "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/$AGENT_SANDBOX_VERSION/sandbox-with-extensions.yaml"
|
||||
kubectl wait --for=condition=Established crd/sandboxtemplates.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxwarmpools.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxclaims.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxes.agents.x-k8s.io --timeout=180s
|
||||
kubectl apply -f "https://github.com/kubernetes-sigs/gateway-api/releases/download/$GATEWAY_API_VERSION/standard-install.yaml"
|
||||
kubectl wait --for=condition=Established crd/gateways.gateway.networking.k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/httproutes.gateway.networking.k8s.io --timeout=180s
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install check dependencies
|
||||
run: pip install "pyyaml==6.0.3"
|
||||
- name: Validate manifest shape
|
||||
run: python scripts/check-manifests.py
|
||||
- name: Validate manifests (server dry-run)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
kubectl create namespace dsh-demo
|
||||
shopt -s globstar nullglob
|
||||
count=0
|
||||
for file in examples/*/manifests/**/*.yaml; do
|
||||
case "$file" in *.yaml.example) continue ;; esac
|
||||
echo "== $file =="
|
||||
kubectl apply --dry-run=server -f "$file"
|
||||
count=$((count + 1))
|
||||
done
|
||||
if [ "$count" -eq 0 ]; then
|
||||
echo "no manifests were validated" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "validated $count manifests"
|
||||
|
||||
gateway-image:
|
||||
name: gateway-image
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build gateway image
|
||||
run: docker build -t dsh-aaas-gateway:ci examples/dsh-agents-as-a-service/gateway/
|
||||
- name: Smoke-test /healthz without a cluster
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cat > /tmp/kubeconfig <<'EOF'
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: dummy
|
||||
cluster:
|
||||
server: https://127.0.0.1:6443
|
||||
insecure-skip-tls-verify: true
|
||||
contexts:
|
||||
- name: dummy
|
||||
context:
|
||||
cluster: dummy
|
||||
user: dummy
|
||||
users:
|
||||
- name: dummy
|
||||
user: {}
|
||||
current-context: dummy
|
||||
EOF
|
||||
docker run -d --name gateway-smoke \
|
||||
-p 18080:8080 \
|
||||
-e NAMESPACE=dsh-demo \
|
||||
-e POOL=dsh-pool \
|
||||
-e KUBECONFIG=/tmp/kubeconfig \
|
||||
-v /tmp/kubeconfig:/tmp/kubeconfig:ro \
|
||||
dsh-aaas-gateway:ci
|
||||
trap 'docker logs gateway-smoke || true; docker rm -f gateway-smoke || true' EXIT
|
||||
code=""
|
||||
for _ in $(seq 1 30); do
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:18080/healthz || true)
|
||||
[ "$code" = "200" ] && break
|
||||
sleep 1
|
||||
done
|
||||
if [ "$code" != "200" ]; then
|
||||
echo "gateway /healthz returned '$code' instead of 200" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "gateway image healthy: GET /healthz -> 200"
|
||||
|
||||
dependency-review:
|
||||
name: dependency-review
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Dependency review
|
||||
uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4
|
||||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -96,6 +96,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
78
.github/workflows/e2e-kind.yaml
vendored
Normal file
78
.github/workflows/e2e-kind.yaml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
# Weekly end-to-end run of the agents-as-a-service example against a real kind
|
||||
# cluster with agent-sandbox installed. Scheduled (not PR-blocking) so upstream
|
||||
# controller churn does not block merges; also available on demand.
|
||||
name: E2E (kind)
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * 1"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: e2e-kind
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
name: Example walkthrough on kind
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@1544676c07bb3570fcb70166beaa07c4528fd4aa # v1.15.1
|
||||
with:
|
||||
version: v0.33.0
|
||||
cluster_name: dsh-e2e
|
||||
- name: Read versions.env
|
||||
run: |
|
||||
set -euo pipefail
|
||||
while IFS= read -r line; do
|
||||
case "$line" in ''|'#'*) continue ;; esac
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done < versions.env
|
||||
- name: Install agent-sandbox
|
||||
run: |
|
||||
set -euo pipefail
|
||||
kubectl apply -f "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/$AGENT_SANDBOX_VERSION/sandbox-with-extensions.yaml"
|
||||
kubectl -n agent-sandbox-system rollout status deployment --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxtemplates.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxwarmpools.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxclaims.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxes.agents.x-k8s.io --timeout=180s
|
||||
- name: Set up Python (WebSocket checks)
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
- name: Install WebSocket client
|
||||
run: pip install "websockets==17.3"
|
||||
- name: Run the example walkthrough
|
||||
env:
|
||||
KIND_CLUSTER: dsh-e2e
|
||||
run: examples/dsh-agents-as-a-service/scripts/e2e-kind.sh
|
||||
- name: Collect cluster state
|
||||
if: always()
|
||||
run: |
|
||||
set -euo pipefail
|
||||
{
|
||||
echo "== resources =="
|
||||
kubectl get all,pvc -n dsh-demo -o wide 2>&1 || true
|
||||
echo "== events =="
|
||||
kubectl get events -n dsh-demo --sort-by=.lastTimestamp 2>&1 || true
|
||||
echo "== gateway logs =="
|
||||
kubectl -n dsh-demo logs deploy/dsh-gateway --tail=200 2>&1 || true
|
||||
} > /tmp/e2e-cluster.txt
|
||||
- name: Upload cluster state
|
||||
if: always()
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: e2e-cluster-state
|
||||
path: /tmp/e2e-cluster.txt
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
277
.github/workflows/multi-build-gateway.yaml
vendored
277
.github/workflows/multi-build-gateway.yaml
vendored
@@ -1,277 +0,0 @@
|
||||
# This workflow builds the multi-arch gateway image (Starlette ASGI, served by
|
||||
# uvicorn on :8080) and pushes it to GHCR as
|
||||
# ghcr.io/<owner>/<repo>:gateway. The DSH Web image is built by multi-build.yaml.
|
||||
# It uses Docker Buildx with native AMD64 and ARM64 runners, without QEMU.
|
||||
name: Gateway Build multi arch Docker Image with separate Github Runners
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'examples/dsh-agents-as-a-service/gateway/**'
|
||||
- '.github/workflows/multi-build-gateway.yaml'
|
||||
env:
|
||||
# ghcr.io/<owner>/<repo>; this workflow always publishes the `:gateway` tag.
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository }}
|
||||
permissions:
|
||||
# Global permissions for the workflow, which can be overridden at the job level
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
# This concurrency group ensures that only one job in the group runs at a time.
|
||||
# If a new job is triggered, the previous one will be canceled.
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# The build job builds the Docker image for each platform specified in the matrix.
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
# The matrix includes two platforms: linux/amd64 and linux/arm64.
|
||||
# The build job will run for each platform in the matrix.
|
||||
|
||||
permissions:
|
||||
# Permissions for the build job, which can be overridden at the step level
|
||||
# The permissions are set to allow the job to write to the GitHub Container Registry (GHCR) and read from the repository.
|
||||
attestations: write
|
||||
actions: read
|
||||
checks: write
|
||||
contents: write
|
||||
deployments: none
|
||||
id-token: write
|
||||
issues: read
|
||||
discussions: read
|
||||
packages: write
|
||||
pages: none
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
|
||||
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-latest' || matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' }}
|
||||
# The job runs on different runners based on the platform.
|
||||
# For linux/amd64, it runs on the latest Ubuntu runner.
|
||||
# For linux/arm64, it runs on an Ubuntu 24.04 ARM runner.
|
||||
# The runner is selected based on the platform specified in the matrix.
|
||||
|
||||
name: Build Docker image for ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Prepare environment for current platform
|
||||
# This step sets up the environment for the current platform being built.
|
||||
# It replaces the '/' character in the platform name with '-' and sets it as an environment variable.
|
||||
# This is useful for naming artifacts and other resources that cannot contain '/'.
|
||||
# The environment variable PLATFORMS_PAIR will be used later in the workflow.
|
||||
id: prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# This step checks out the code from the repository.
|
||||
# It uses the actions/checkout action to clone the repository into the runner's workspace.
|
||||
|
||||
- name: Docker meta default
|
||||
# This step generates metadata for the Docker image.
|
||||
# It uses the docker/metadata-action to create metadata based on the repository information.
|
||||
# The metadata includes information such as the image name, tags, and labels.
|
||||
# The metadata will be used later in the workflow to build and push the Docker image.
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
with:
|
||||
images: ${{ env.GHCR_IMAGE }}
|
||||
tags: |
|
||||
type=raw,value=gateway
|
||||
|
||||
|
||||
- name: Set up Docker Context for Buildx
|
||||
# This step sets up a Docker context for Buildx.
|
||||
# It creates a new context named "builders" that will be used for building the Docker image.
|
||||
# The context allows Buildx to use the Docker daemon for building images.
|
||||
id: buildx-context
|
||||
run: |
|
||||
docker context create builders
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
# This step sets up Docker Buildx, which is a Docker CLI plugin for extended build capabilities with BuildKit.
|
||||
# It uses the docker/setup-buildx-action to configure Buildx with the specified context and platforms.
|
||||
# The platforms are specified in the matrix and will be used for building the Docker image.
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
with:
|
||||
endpoint: builders
|
||||
platforms: ${{ matrix.platform }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
# This step logs in to the GitHub Container Registry (GHCR) using the docker/login-action.
|
||||
# It uses the GitHub actor's username and the GITHUB_TOKEN secret for authentication.
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Build and push by digest
|
||||
# This step builds and pushes the Docker image using Buildx.
|
||||
# It uses the docker/build-push-action to build the image with the specified context and platforms.
|
||||
# The image is built with the labels and annotations generated in the previous steps.
|
||||
# The outputs are configured to push the image by digest, which allows for better caching and versioning.
|
||||
# The cache-from and cache-to options are used to enable caching for the build process.
|
||||
# The cache is stored in GitHub Actions cache and is scoped to the repository, branch, and platform.
|
||||
id: build
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
with:
|
||||
context: ./examples/dsh-agents-as-a-service/gateway/
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
outputs: type=image,name=${{ env.GHCR_IMAGE }},push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=true
|
||||
cache-from: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform }}
|
||||
cache-to: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform }}
|
||||
|
||||
|
||||
- name: Export digest
|
||||
# This step exports the digest of the built image to a file.
|
||||
# It creates a directory in /tmp/digests and saves the digest of the image to a file.
|
||||
# The digest is obtained from the output of the build step.
|
||||
# The digest is used to uniquely identify the built image and can be used for further processing or verification.
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
# This step uploads the digest file to the GitHub Actions artifact storage.
|
||||
# It uses the actions/upload-artifact action to upload the file created in the previous step.
|
||||
# The artifact is named digests-${{ env.PLATFORM_PAIR }}, where PLATFORM_PAIR is the platform name with '/' replaced by '-'.
|
||||
# The artifact is retained for 1 day, and if no files are found, it will throw an error.
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
|
||||
merge:
|
||||
# This job merges the Docker manifests for the different platforms built in the previous job.
|
||||
name: Merge Docker manifests
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
attestations: write
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: none
|
||||
id-token: write
|
||||
issues: read
|
||||
discussions: read
|
||||
packages: write
|
||||
pages: none
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
|
||||
needs:
|
||||
- build
|
||||
# This job depends on the build job to complete before it starts.
|
||||
# It ensures that the Docker images for all platforms are built before merging the manifests.
|
||||
steps:
|
||||
- name: Download digests
|
||||
# This step downloads the digest files uploaded in the build job.
|
||||
# It uses the actions/download-artifact action to download the artifacts with the pattern digests-*.
|
||||
# The downloaded files are merged into the /tmp/digests directory.
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
|
||||
- name: Docker meta
|
||||
# This step generates metadata for the Docker image.
|
||||
# It uses the docker/metadata-action to create metadata based on the repository information.
|
||||
# The metadata includes information such as the image name, tags, and labels.
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
with:
|
||||
images: ${{ env.GHCR_IMAGE }}
|
||||
annotations: |
|
||||
type=org.opencontainers.image.description,value=${{ github.event.repository.description || 'No description provided' }}
|
||||
tags: |
|
||||
type=raw,value=gateway
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
# This step sets up Docker Buildx, which is a Docker CLI plugin for extended build capabilities with BuildKit.
|
||||
with:
|
||||
driver-opts: |
|
||||
network=host
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
# This step logs in to the GitHub Container Registry (GHCR) using the docker/login-action.
|
||||
# It uses the GitHub actor's username and the GITHUB_TOKEN secret for authentication.
|
||||
# The login is necessary to push the merged manifest list to GHCR.
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get execution timestamp with RFC3339 format
|
||||
# This step gets the current execution timestamp in RFC3339 format.
|
||||
# It uses the date command to get the current UTC time and formats it as a string.
|
||||
# The timestamp is used for annotating the Docker manifest list.
|
||||
id: timestamp
|
||||
run: |
|
||||
echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create manifest list and pushs
|
||||
# This step creates a manifest list for the Docker images built for different platforms.
|
||||
# It uses the docker buildx imagetools create command to create the manifest list.
|
||||
# The manifest list is annotated with metadata such as description, creation timestamp, and source URL.
|
||||
# The annotations are obtained from the metadata generated in the previous steps.
|
||||
# The manifest list is pushed to the GitHub Container Registry (GHCR) with the specified tags.
|
||||
working-directory: /tmp/digests
|
||||
id: manifest-annotate
|
||||
continue-on-error: true
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
--annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \
|
||||
--annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \
|
||||
--annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \
|
||||
--annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \
|
||||
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
|
||||
- name: Create manifest list and push without annotations
|
||||
# This step creates a manifest list for the Docker images built for different platforms.
|
||||
# It uses the docker buildx imagetools create command to create the manifest list.
|
||||
# The manifest list is created without annotations if the previous step fails.
|
||||
# The manifest list is pushed to the GitHub Container Registry (GHCR) with the specified tags.
|
||||
if: steps.manifest-annotate.outcome == 'failure'
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
# This step inspects the created manifest list to verify its contents.
|
||||
# It uses the docker buildx imagetools inspect command to display information about the manifest list.
|
||||
# The inspection output will show the platforms and tags associated with the manifest list.
|
||||
id: inspect
|
||||
run: |
|
||||
docker buildx imagetools inspect '${{ env.GHCR_IMAGE }}:${{ steps.meta.outputs.version }}'
|
||||
295
.github/workflows/multi-build.yaml
vendored
295
.github/workflows/multi-build.yaml
vendored
@@ -1,295 +0,0 @@
|
||||
# This workflow builds a multi-arch Docker image using GitHub Actions and separated Github Runners with native support for ARM64 and AMD64 architectures, without using QEMU emulation.
|
||||
# It uses Docker Buildx to build and push the image to GitHub Container Registry (GHCR).
|
||||
name: Build multi arch Docker Image with separate Github Runners
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dsh_version:
|
||||
description: "DSH git ref to build (tag or branch)"
|
||||
required: true
|
||||
default: dsh-v0.2.1-rc.2
|
||||
type: string
|
||||
pnpm_version:
|
||||
description: "pnpm version used to build DSH"
|
||||
required: true
|
||||
default: "11.28.5"
|
||||
type: string
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/multi-build.yaml'
|
||||
env:
|
||||
# The name of the Docker image to be built and pushed to GHCR
|
||||
# The image name is derived from the GitHub repository name and the GitHub Container Registry (GHCR) URL.
|
||||
# The image name will be in the format: ghcr.io/<owner>/<repo>
|
||||
GHCR_IMAGE: ghcr.io/${{ github.repository }}
|
||||
# workflow_dispatch inputs win; pushes to main fall back to the pinned
|
||||
# defaults. Both values flow into the Docker build args and the image tags.
|
||||
DSH_VERSION: ${{ inputs.dsh_version || 'dsh-v0.2.1-rc.2' }}
|
||||
PNPM_VERSION: ${{ inputs.pnpm_version || '11.28.5' }}
|
||||
permissions:
|
||||
# Global permissions for the workflow, which can be overridden at the job level
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
# This concurrency group ensures that only one job in the group runs at a time.
|
||||
# If a new job is triggered, the previous one will be canceled.
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# The build job builds the Docker image for each platform specified in the matrix.
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
# The matrix includes two platforms: linux/amd64 and linux/arm64.
|
||||
# The build job will run for each platform in the matrix.
|
||||
|
||||
permissions:
|
||||
# Permissions for the build job, which can be overridden at the step level
|
||||
# The permissions are set to allow the job to write to the GitHub Container Registry (GHCR) and read from the repository.
|
||||
attestations: write
|
||||
actions: read
|
||||
checks: write
|
||||
contents: write
|
||||
deployments: none
|
||||
id-token: write
|
||||
issues: read
|
||||
discussions: read
|
||||
packages: write
|
||||
pages: none
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
|
||||
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-latest' || matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' }}
|
||||
# The job runs on different runners based on the platform.
|
||||
# For linux/amd64, it runs on the latest Ubuntu runner.
|
||||
# For linux/arm64, it runs on an Ubuntu 24.04 ARM runner.
|
||||
# The runner is selected based on the platform specified in the matrix.
|
||||
|
||||
name: Build Docker image for ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Prepare environment for current platform
|
||||
# This step sets up the environment for the current platform being built.
|
||||
# It replaces the '/' character in the platform name with '-' and sets it as an environment variable.
|
||||
# This is useful for naming artifacts and other resources that cannot contain '/'.
|
||||
# The environment variable PLATFORMS_PAIR will be used later in the workflow.
|
||||
id: prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
# This step checks out the code from the repository.
|
||||
# It uses the actions/checkout action to clone the repository into the runner's workspace.
|
||||
|
||||
- name: Docker meta default
|
||||
# This step generates metadata for the Docker image.
|
||||
# It uses the docker/metadata-action to create metadata based on the repository information.
|
||||
# The metadata includes information such as the image name, tags, and labels.
|
||||
# The metadata will be used later in the workflow to build and push the Docker image.
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
with:
|
||||
images: ${{ env.GHCR_IMAGE }}
|
||||
|
||||
|
||||
- name: Set up Docker Context for Buildx
|
||||
# This step sets up a Docker context for Buildx.
|
||||
# It creates a new context named "builders" that will be used for building the Docker image.
|
||||
# The context allows Buildx to use the Docker daemon for building images.
|
||||
id: buildx-context
|
||||
run: |
|
||||
docker context create builders
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
# This step sets up Docker Buildx, which is a Docker CLI plugin for extended build capabilities with BuildKit.
|
||||
# It uses the docker/setup-buildx-action to configure Buildx with the specified context and platforms.
|
||||
# The platforms are specified in the matrix and will be used for building the Docker image.
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
with:
|
||||
endpoint: builders
|
||||
platforms: ${{ matrix.platform }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
# This step logs in to the GitHub Container Registry (GHCR) using the docker/login-action.
|
||||
# It uses the GitHub actor's username and the GITHUB_TOKEN secret for authentication.
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Build and push by digest
|
||||
# This step builds and pushes the Docker image using Buildx.
|
||||
# It uses the docker/build-push-action to build the image with the specified context and platforms.
|
||||
# The image is built with the labels and annotations generated in the previous steps.
|
||||
# The outputs are configured to push the image by digest, which allows for better caching and versioning.
|
||||
# The cache-from and cache-to options are used to enable caching for the build process.
|
||||
# The cache is stored in GitHub Actions cache and is scoped to the repository, branch, and platform.
|
||||
id: build
|
||||
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
|
||||
env:
|
||||
DOCKER_BUILDKIT: 1
|
||||
DSH_PORT: 3080
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ matrix.platform }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
DSH_VERSION=${{ env.DSH_VERSION }}
|
||||
PNPM_VERSION=${{ env.PNPM_VERSION }}
|
||||
annotations: ${{ steps.meta.outputs.annotations }}
|
||||
outputs: type=image,name=${{ env.GHCR_IMAGE }},push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=true
|
||||
cache-from: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform }}
|
||||
cache-to: type=gha,scope=${{ github.repository }}-${{ github.ref_name }}-${{ matrix.platform }}
|
||||
|
||||
|
||||
- name: Export digest
|
||||
# This step exports the digest of the built image to a file.
|
||||
# It creates a directory in /tmp/digests and saves the digest of the image to a file.
|
||||
# The digest is obtained from the output of the build step.
|
||||
# The digest is used to uniquely identify the built image and can be used for further processing or verification.
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
# This step uploads the digest file to the GitHub Actions artifact storage.
|
||||
# It uses the actions/upload-artifact action to upload the file created in the previous step.
|
||||
# The artifact is named digests-${{ env.PLATFORM_PAIR }}, where PLATFORM_PAIR is the platform name with '/' replaced by '-'.
|
||||
# The artifact is retained for 1 day, and if no files are found, it will throw an error.
|
||||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||||
with:
|
||||
name: digests-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
|
||||
merge:
|
||||
# This job merges the Docker manifests for the different platforms built in the previous job.
|
||||
name: Merge Docker manifests
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
attestations: write
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: none
|
||||
id-token: write
|
||||
issues: read
|
||||
discussions: read
|
||||
packages: write
|
||||
pages: none
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
|
||||
needs:
|
||||
- build
|
||||
# This job depends on the build job to complete before it starts.
|
||||
# It ensures that the Docker images for all platforms are built before merging the manifests.
|
||||
steps:
|
||||
- name: Download digests
|
||||
# This step downloads the digest files uploaded in the build job.
|
||||
# It uses the actions/download-artifact action to download the artifacts with the pattern digests-*.
|
||||
# The downloaded files are merged into the /tmp/digests directory.
|
||||
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||||
with:
|
||||
path: /tmp/digests
|
||||
pattern: digests-*
|
||||
merge-multiple: true
|
||||
|
||||
|
||||
- name: Docker meta
|
||||
# This step generates metadata for the Docker image.
|
||||
# It uses the docker/metadata-action to create metadata based on the repository information.
|
||||
# The metadata includes information such as the image name, tags, and labels.
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
|
||||
with:
|
||||
images: ${{ env.GHCR_IMAGE }}
|
||||
annotations: |
|
||||
type=org.opencontainers.image.description,value=${{ github.event.repository.description || 'No description provided' }}
|
||||
tags: |
|
||||
type=raw,value=main,enable=${{ github.ref_name == 'main' }}
|
||||
type=raw,value=latest,enable=${{ github.ref_name == 'main' }}
|
||||
type=raw,value=${{ env.DSH_VERSION }},enable=${{ github.ref_name == 'main' || github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
# This step sets up Docker Buildx, which is a Docker CLI plugin for extended build capabilities with BuildKit.
|
||||
with:
|
||||
driver-opts: |
|
||||
network=host
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
# This step logs in to the GitHub Container Registry (GHCR) using the docker/login-action.
|
||||
# It uses the GitHub actor's username and the GITHUB_TOKEN secret for authentication.
|
||||
# The login is necessary to push the merged manifest list to GHCR.
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get execution timestamp with RFC3339 format
|
||||
# This step gets the current execution timestamp in RFC3339 format.
|
||||
# It uses the date command to get the current UTC time and formats it as a string.
|
||||
# The timestamp is used for annotating the Docker manifest list.
|
||||
id: timestamp
|
||||
run: |
|
||||
echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create manifest list and pushs
|
||||
# This step creates a manifest list for the Docker images built for different platforms.
|
||||
# It uses the docker buildx imagetools create command to create the manifest list.
|
||||
# The manifest list is annotated with metadata such as description, creation timestamp, and source URL.
|
||||
# The annotations are obtained from the metadata generated in the previous steps.
|
||||
# The manifest list is pushed to the GitHub Container Registry (GHCR) with the specified tags.
|
||||
working-directory: /tmp/digests
|
||||
id: manifest-annotate
|
||||
continue-on-error: true
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
--annotation='index:org.opencontainers.image.description=${{ github.event.repository.description }}' \
|
||||
--annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \
|
||||
--annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \
|
||||
--annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \
|
||||
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Create manifest list and push without annotations
|
||||
# This step creates a manifest list for the Docker images built for different platforms.
|
||||
# It uses the docker buildx imagetools create command to create the manifest list.
|
||||
# The manifest list is created without annotations if the previous step fails.
|
||||
# The manifest list is pushed to the GitHub Container Registry (GHCR) with the specified tags.
|
||||
if: steps.manifest-annotate.outcome == 'failure'
|
||||
working-directory: /tmp/digests
|
||||
run: |
|
||||
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
|
||||
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
|
||||
|
||||
- name: Inspect image
|
||||
# This step inspects the created manifest list to verify its contents.
|
||||
# It uses the docker buildx imagetools inspect command to display information about the manifest list.
|
||||
# The inspection output will show the platforms and tags associated with the manifest list.
|
||||
id: inspect
|
||||
run: |
|
||||
docker buildx imagetools inspect '${{ env.GHCR_IMAGE }}:${{ steps.meta.outputs.version }}'
|
||||
38
.github/workflows/pr-title.yaml
vendored
Normal file
38
.github/workflows/pr-title.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# Enforce Conventional Commit pull request titles so git-cliff can group
|
||||
# release notes. The PR is squash-merged, so the title becomes the commit.
|
||||
name: PR title
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: pr-title
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Validate title
|
||||
uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
docs
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
build
|
||||
ci
|
||||
chore
|
||||
revert
|
||||
requireScope: false
|
||||
subjectPattern: ^(?![A-Z]).+$
|
||||
subjectPatternError: |
|
||||
The PR title subject must start in lowercase and must not end with a period.
|
||||
268
.github/workflows/release.yaml
vendored
Normal file
268
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,268 @@
|
||||
# Release pipeline. Pushing a tag that matches the identity in versions.env
|
||||
# publishes both images (version + latest), creates the GitHub Release from
|
||||
# git-cliff, and opens a PR that refreshes CHANGELOG.md on main.
|
||||
#
|
||||
# The tag must be dsh-vX.Y.Z or dsh-vX.Y.Z-rN and must match versions.env;
|
||||
# tags are immutable, so a broken release is fixed with a new -rN revision.
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "dsh-v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Existing tag to release (must match versions.env)
|
||||
required: true
|
||||
type: string
|
||||
publish_legacy_gateway_alias:
|
||||
description: Also publish the legacy :gateway alias (one release only)
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
|
||||
concurrency:
|
||||
group: release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
meta:
|
||||
name: Validate release identity
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
outputs:
|
||||
identity: ${{ steps.meta.outputs.identity }}
|
||||
dsh_version: ${{ steps.meta.outputs.dsh_version }}
|
||||
pnpm_version: ${{ steps.meta.outputs.pnpm_version }}
|
||||
release_revision: ${{ steps.meta.outputs.release_revision }}
|
||||
ghcr_image: ${{ steps.meta.outputs.ghcr_image }}
|
||||
gateway_image: ${{ steps.meta.outputs.gateway_image }}
|
||||
tag: ${{ steps.meta.outputs.tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Validate tag against versions.env
|
||||
id: meta
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="${{ inputs.tag || github.ref_name }}"
|
||||
while IFS= read -r line; do
|
||||
case "$line" in ''|'#'*) continue ;; esac
|
||||
echo "$line" >> "$GITHUB_ENV"
|
||||
done < versions.env
|
||||
identity="$DSH_VERSION"
|
||||
[ -z "$RELEASE_REVISION" ] || identity="$DSH_VERSION-$RELEASE_REVISION"
|
||||
if [ "$tag" != "$identity" ]; then
|
||||
echo "tag '$tag' does not match versions.env identity '$identity'" >&2
|
||||
exit 1
|
||||
fi
|
||||
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
|
||||
git fetch origin main '+refs/heads/release/*:refs/remotes/origin/release/*'
|
||||
commit=$(git rev-list -n1 "refs/tags/$tag")
|
||||
allowed=0
|
||||
for ref in origin/main origin/release/*; do
|
||||
if git rev-parse -q --verify "$ref" >/dev/null && git merge-base --is-ancestor "$commit" "$ref"; then
|
||||
allowed=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$allowed" -ne 1 ]; then
|
||||
echo "tag commit $commit is not on main or a release branch" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "tag '$tag' does not exist in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! git ls-remote --tags --exit-code "$DSH_REPO" "$DSH_VERSION" >/dev/null; then
|
||||
echo "upstream ref '$DSH_VERSION' not found in $DSH_REPO" >&2
|
||||
exit 1
|
||||
fi
|
||||
{
|
||||
echo "identity=$identity"
|
||||
echo "dsh_version=$DSH_VERSION"
|
||||
echo "pnpm_version=$PNPM_VERSION"
|
||||
echo "release_revision=$RELEASE_REVISION"
|
||||
echo "ghcr_image=$GHCR_IMAGE"
|
||||
echo "gateway_image=$GATEWAY_IMAGE"
|
||||
echo "tag=$tag"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Verify repository pins
|
||||
run: ./scripts/check-versions.sh
|
||||
|
||||
tests:
|
||||
name: Unit tests and lint
|
||||
needs: meta
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
defaults:
|
||||
run:
|
||||
working-directory: examples/dsh-agents-as-a-service/gateway
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
examples/dsh-agents-as-a-service/gateway/requirements.txt
|
||||
examples/dsh-agents-as-a-service/gateway/requirements-dev.txt
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt -r requirements-dev.txt
|
||||
- name: Lint
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python -m ruff check .
|
||||
python -m ruff format --check .
|
||||
- name: Unit tests
|
||||
run: python -m pytest -q
|
||||
|
||||
build-main:
|
||||
name: Publish main image
|
||||
needs: [meta, tests]
|
||||
uses: ./.github/workflows/_build-image.yaml
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
with:
|
||||
image: ${{ needs.meta.outputs.ghcr_image }}
|
||||
version: ${{ needs.meta.outputs.identity }}
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
build-args: |
|
||||
DSH_VERSION=${{ needs.meta.outputs.dsh_version }}
|
||||
RELEASE_REVISION=${{ needs.meta.outputs.release_revision }}
|
||||
PNPM_VERSION=${{ needs.meta.outputs.pnpm_version }}
|
||||
VCS_REF=${{ github.sha }}
|
||||
tags: |
|
||||
${{ needs.meta.outputs.ghcr_image }}:${{ needs.meta.outputs.identity }}
|
||||
${{ needs.meta.outputs.ghcr_image }}:latest
|
||||
|
||||
build-gateway:
|
||||
name: Publish gateway image
|
||||
needs: [meta, tests]
|
||||
uses: ./.github/workflows/_build-image.yaml
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
with:
|
||||
image: ${{ needs.meta.outputs.gateway_image }}
|
||||
version: ${{ needs.meta.outputs.identity }}
|
||||
context: examples/dsh-agents-as-a-service/gateway
|
||||
dockerfile: Dockerfile
|
||||
tags: |
|
||||
${{ needs.meta.outputs.gateway_image }}:${{ needs.meta.outputs.identity }}
|
||||
${{ needs.meta.outputs.gateway_image }}:latest
|
||||
|
||||
legacy-gateway-alias:
|
||||
name: Publish legacy :gateway alias (temporary)
|
||||
needs: [meta, build-gateway]
|
||||
if: github.event_name != 'workflow_dispatch' || inputs.publish_legacy_gateway_alias
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Retag gateway as the legacy package tag
|
||||
env:
|
||||
SOURCE: ${{ needs.meta.outputs.gateway_image }}:${{ needs.meta.outputs.identity }}
|
||||
LEGACY: ${{ needs.meta.outputs.ghcr_image }}:gateway
|
||||
run: |
|
||||
set -euo pipefail
|
||||
docker buildx imagetools create -t "$LEGACY" "$SOURCE"
|
||||
echo "Deprecated alias $LEGACY now points at $SOURCE" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
publish-release:
|
||||
name: Changelog and GitHub Release
|
||||
needs: [meta, build-main, build-gateway]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install git-cliff
|
||||
env:
|
||||
GIT_CLIFF_VERSION: v2.14.2
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="${GIT_CLIFF_VERSION#v}"
|
||||
curl -fsSL -o /tmp/git-cliff.tar.gz "https://github.com/orhun/git-cliff/releases/download/$GIT_CLIFF_VERSION/git-cliff-$version-x86_64-unknown-linux-gnu.tar.gz"
|
||||
tar -xzf /tmp/git-cliff.tar.gz -C /tmp
|
||||
install -m 0755 "/tmp/git-cliff-$version/git-cliff" /usr/local/bin/git-cliff
|
||||
git-cliff --version
|
||||
|
||||
- name: Generate CHANGELOG and release notes
|
||||
env:
|
||||
IDENTITY: ${{ needs.meta.outputs.identity }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git-cliff --tag "$IDENTITY" --output CHANGELOG.md
|
||||
git-cliff --tag "$IDENTITY" --latest --strip header --output /tmp/release-notes.md
|
||||
echo "### Release $IDENTITY" >> "$GITHUB_STEP_SUMMARY"
|
||||
head -n 40 /tmp/release-notes.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Create GitHub Release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
IDENTITY: ${{ needs.meta.outputs.identity }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if gh release view "$IDENTITY" >/dev/null 2>&1; then
|
||||
echo "release $IDENTITY already exists; skipping" >&2
|
||||
else
|
||||
gh release create "$IDENTITY" --title "$IDENTITY" --notes-file /tmp/release-notes.md --verify-tag
|
||||
fi
|
||||
|
||||
- name: Open CHANGELOG pull request
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
IDENTITY: ${{ needs.meta.outputs.identity }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
if git diff --quiet -- CHANGELOG.md; then
|
||||
echo "CHANGELOG.md already up to date"
|
||||
exit 0
|
||||
fi
|
||||
branch="chore/changelog-$IDENTITY"
|
||||
git switch -c "$branch"
|
||||
git add CHANGELOG.md
|
||||
git commit -m "chore(changelog): refresh for $IDENTITY"
|
||||
git push origin "$branch"
|
||||
gh pr create \
|
||||
--base main \
|
||||
--head "$branch" \
|
||||
--title "chore(changelog): refresh for $IDENTITY" \
|
||||
--body "Generated by git-cliff for $IDENTITY. Do not edit CHANGELOG.md by hand."
|
||||
137
.github/workflows/validate.yaml
vendored
137
.github/workflows/validate.yaml
vendored
@@ -1,137 +0,0 @@
|
||||
name: Validate
|
||||
|
||||
# Validates the Kubernetes manifests against the real agent-sandbox CRD
|
||||
# schema (server-side dry-run on a throwaway kind cluster), runs the gateway
|
||||
# unit tests + lint, and builds/smoke-tests the gateway image without a
|
||||
# cluster. Runs on every PR and on pushes to main.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate-manifests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@1544676c07bb3570fcb70166beaa07c4528fd4aa # v1.15.1
|
||||
with:
|
||||
version: v0.33.0
|
||||
cluster_name: dsh-validate
|
||||
|
||||
- name: Install agent-sandbox (extensions)
|
||||
run: |
|
||||
VERSION=v1.0.7
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/sandbox-with-extensions.yaml
|
||||
kubectl wait --for=condition=Established crd/sandboxtemplates.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxwarmpools.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxclaims.extensions.agents.x-k8s.io --timeout=180s
|
||||
kubectl wait --for=condition=Established crd/sandboxes.agents.x-k8s.io --timeout=180s
|
||||
|
||||
- name: Validate manifests (server dry-run)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
kubectl create namespace dsh-demo
|
||||
for f in examples/dsh-agents-as-a-service/*.yaml; do
|
||||
echo "== $f =="
|
||||
kubectl apply --dry-run=server -f "$f"
|
||||
done
|
||||
kubectl apply --dry-run=server -f deploy/k8s/sandbox-template.yaml
|
||||
echo "all manifests validated"
|
||||
|
||||
gateway-tests:
|
||||
# The gateway lives in examples/dsh-agents-as-a-service/gateway/. Its
|
||||
# runtime pins and dev pins are kept separate so the image only ships
|
||||
# requirements.txt while CI installs both files.
|
||||
name: Gateway unit tests and lint
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: examples/dsh-agents-as-a-service/gateway
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.12"
|
||||
cache: pip
|
||||
cache-dependency-path: |
|
||||
examples/dsh-agents-as-a-service/gateway/requirements.txt
|
||||
examples/dsh-agents-as-a-service/gateway/requirements-dev.txt
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt -r requirements-dev.txt
|
||||
|
||||
- name: Lint (ruff)
|
||||
run: python -m ruff check .
|
||||
|
||||
- name: Run gateway unit tests
|
||||
run: python -m pytest -v
|
||||
|
||||
gateway-image:
|
||||
# No cluster and no real credentials: the gateway must start and answer
|
||||
# /healthz with a dummy kubeconfig, which catches broken imports, missing
|
||||
# runtime deps, and an unresponsive ASGI entrypoint.
|
||||
name: Build and smoke-test gateway image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Build gateway image
|
||||
run: |
|
||||
docker build \
|
||||
-t dsh-aaas-gateway:ci \
|
||||
examples/dsh-agents-as-a-service/gateway/
|
||||
|
||||
- name: Smoke-test /healthz without a cluster
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cat > /tmp/kubeconfig <<'EOF'
|
||||
apiVersion: v1
|
||||
kind: Config
|
||||
clusters:
|
||||
- name: dummy
|
||||
cluster:
|
||||
server: https://127.0.0.1:6443
|
||||
insecure-skip-tls-verify: true
|
||||
contexts:
|
||||
- name: dummy
|
||||
context:
|
||||
cluster: dummy
|
||||
user: dummy
|
||||
users:
|
||||
- name: dummy
|
||||
user: {}
|
||||
current-context: dummy
|
||||
EOF
|
||||
docker run -d --name gateway-smoke \
|
||||
-p 18080:8080 \
|
||||
-e NAMESPACE=dsh-demo \
|
||||
-e POOL=dsh-pool \
|
||||
-e KUBECONFIG=/tmp/kubeconfig \
|
||||
-v /tmp/kubeconfig:/tmp/kubeconfig:ro \
|
||||
dsh-aaas-gateway:ci
|
||||
trap 'docker logs gateway-smoke || true; docker rm -f gateway-smoke || true' EXIT
|
||||
|
||||
code=""
|
||||
for _ in $(seq 1 30); do
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:18080/healthz || true)
|
||||
[ "$code" = "200" ] && break
|
||||
sleep 1
|
||||
done
|
||||
if [ "$code" != "200" ]; then
|
||||
echo "gateway /healthz returned '$code' instead of 200" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "gateway image healthy: GET /healthz -> 200"
|
||||
Reference in New Issue
Block a user