quality / commitlint (push) Skipped
quality / changes (push) Successful in 7s
quality / docker-check (push) Skipped
quality / openapi (push) Successful in 21s
quality / web (push) Successful in 1m12s
quality / go (push) Successful in 1m3s
quality / bird2 (push) Successful in 16s
CD / quality (push) Successful in 3m7s
CD / publish (push) Successful in 3m58s
- Introduced `docker_hub_token` and `docker_hub_username` secrets for Docker Hub login in CI/CD workflows. - Updated `.gitea/workflows/ci.yaml`, `.gitea/workflows/cd.yaml`, and `.gitea/workflows/quality.yaml` to utilize these secrets for Docker Hub authentication. - Enhanced README and releasing documentation to clarify the use of Docker Hub credentials for image mirroring and builds.
439 lines
15 KiB
YAML
439 lines
15 KiB
YAML
# Quality gates (reusable). Callers: ci.yaml (PR), cd.yaml (push main).
|
|
name: quality
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
is_pull_request:
|
|
type: boolean
|
|
required: true
|
|
base_sha:
|
|
type: string
|
|
required: false
|
|
default: ""
|
|
head_sha:
|
|
type: string
|
|
required: false
|
|
default: ""
|
|
before_sha:
|
|
type: string
|
|
required: false
|
|
default: ""
|
|
allow_registry_login:
|
|
type: boolean
|
|
required: false
|
|
default: false
|
|
secrets:
|
|
ACTIONS_PAT:
|
|
required: false
|
|
docker_hub_token:
|
|
required: false
|
|
docker_hub_username:
|
|
required: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
changes:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
openapi: ${{ steps.detect.outputs.openapi }}
|
|
go: ${{ steps.detect.outputs.go }}
|
|
web: ${{ steps.detect.outputs.web }}
|
|
bird_conf: ${{ steps.detect.outputs.bird_conf }}
|
|
docker_go: ${{ steps.detect.outputs.docker_go }}
|
|
docker_web: ${{ steps.detect.outputs.docker_web }}
|
|
docker_bird: ${{ steps.detect.outputs.docker_bird }}
|
|
steps:
|
|
- if: ${{ inputs.is_pull_request }}
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
with:
|
|
fetch-depth: 0
|
|
- if: ${{ inputs.is_pull_request == false }}
|
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
with:
|
|
fetch-depth: 2
|
|
- id: detect
|
|
name: Detect changed paths per module
|
|
env:
|
|
IS_PR: ${{ inputs.is_pull_request }}
|
|
BASE_SHA: ${{ inputs.base_sha }}
|
|
HEAD_SHA: ${{ inputs.head_sha }}
|
|
BEFORE_SHA: ${{ inputs.before_sha }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
openapi=false
|
|
go=false
|
|
web=false
|
|
bird_conf=false
|
|
docker_go=false
|
|
docker_web=false
|
|
docker_bird=false
|
|
|
|
set_all_flags_true() {
|
|
openapi=true
|
|
go=true
|
|
web=true
|
|
bird_conf=true
|
|
docker_go=true
|
|
docker_web=true
|
|
docker_bird=true
|
|
}
|
|
|
|
write_outputs() {
|
|
for v in openapi go web bird_conf docker_go docker_web docker_bird; do
|
|
eval "echo \"\$v=\$$v\"" >> "$GITHUB_OUTPUT"
|
|
done
|
|
}
|
|
|
|
if [ "$IS_PR" = "true" ]; then
|
|
FILES="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")"
|
|
else
|
|
after="${HEAD_SHA:-$(git rev-parse HEAD)}"
|
|
before="$BEFORE_SHA"
|
|
if [ -n "$before" ] && [ "$before" != "0000000000000000000000000000000000000000" ]; then
|
|
FILES="$(git diff --name-only "$before" "$after")"
|
|
elif git rev-parse --verify HEAD~1 >/dev/null 2>&1; then
|
|
FILES="$(git diff --name-only HEAD~1 HEAD)"
|
|
else
|
|
set_all_flags_true
|
|
write_outputs
|
|
echo "No parent commit — full pipeline (all modules)"
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$(printf '%s' "$FILES" | tr -d '[:space:]')" ]; then
|
|
set_all_flags_true
|
|
write_outputs
|
|
echo "Empty diff — full pipeline fallback"
|
|
exit 0
|
|
fi
|
|
|
|
full_pipeline=false
|
|
|
|
while IFS= read -r f || [ -n "${f:-}" ]; do
|
|
[ -z "${f:-}" ] && continue
|
|
case "$f" in
|
|
.gitea/workflows/*|.golangci.yml|.pre-commit-config.yaml|scripts/*)
|
|
full_pipeline=true
|
|
;;
|
|
docs/openapi.yaml|redocly.yaml)
|
|
openapi=true
|
|
;;
|
|
docs/api.md|docs/access.md)
|
|
openapi=true
|
|
go=true
|
|
;;
|
|
.cursor/*|.claude/*|.codegraph/*|memory-bank/*)
|
|
;;
|
|
*.md|AGENTS.md)
|
|
;;
|
|
apps/web/README.md|apps/web/components.json|packages/ui/components.json)
|
|
;;
|
|
apps/web/*|packages/ui/*|packages/shared/*)
|
|
web=true
|
|
;;
|
|
deploy/bird/*)
|
|
bird_conf=true
|
|
go=true
|
|
;;
|
|
deploy/compose/*|deploy/docker/*|.dockerignore)
|
|
docker_go=true
|
|
docker_web=true
|
|
docker_bird=true
|
|
go=true
|
|
;;
|
|
go.mod|go.sum|go.work)
|
|
go=true
|
|
;;
|
|
migrations/*)
|
|
go=true
|
|
;;
|
|
cmd/*|internal/*|*.go)
|
|
go=true
|
|
bird_conf=true
|
|
;;
|
|
docs/*)
|
|
;;
|
|
package.json|package-lock.json|pnpm-lock.yaml|pnpm-workspace.yaml|.releaserc.json)
|
|
full_pipeline=true
|
|
;;
|
|
*)
|
|
;;
|
|
esac
|
|
done <<< "$FILES"
|
|
|
|
if $full_pipeline; then
|
|
set_all_flags_true
|
|
fi
|
|
|
|
write_outputs
|
|
|
|
echo "Changed files (first 30):"
|
|
printf '%s\n' "$FILES" | head -n 30
|
|
echo "--- flags ---"
|
|
echo "openapi=$openapi go=$go web=$web bird_conf=$bird_conf"
|
|
echo "docker_go=$docker_go docker_web=$docker_web docker_bird=$docker_bird full_pipeline=$full_pipeline"
|
|
|
|
openapi:
|
|
needs: [changes]
|
|
if: needs.changes.outputs.openapi == 'true' || needs.changes.outputs.web == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "22"
|
|
- name: Export cache paths
|
|
run: sh scripts/ci/export-cache-env.sh
|
|
- id: pnpm-hash
|
|
run: echo "key=$(sha256sum pnpm-lock.yaml | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
|
- id: pnpm-cache
|
|
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
|
with:
|
|
path: |
|
|
${{ env.PNPM_STORE_DIR }}
|
|
${{ env.COREPACK_HOME }}
|
|
node_modules
|
|
apps/web/node_modules
|
|
packages/ui/node_modules
|
|
key: pnpm-${{ runner.os }}-${{ steps.pnpm-hash.outputs.key }}
|
|
restore-keys: |
|
|
pnpm-${{ runner.os }}-
|
|
- name: pnpm install, Redocly, codegen check
|
|
env:
|
|
PNPM_CACHE_HIT: ${{ steps.pnpm-cache.outputs.cache-hit }}
|
|
run: |
|
|
set -euxo pipefail
|
|
sh scripts/ci/pnpm-ci.sh
|
|
pnpm exec redocly lint docs/openapi.yaml
|
|
chmod +x scripts/check-openapi-gen.sh
|
|
sh scripts/check-openapi-gen.sh
|
|
|
|
web:
|
|
needs: [changes]
|
|
if: needs.changes.outputs.web == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "22"
|
|
- name: Export cache paths
|
|
run: sh scripts/ci/export-cache-env.sh
|
|
- id: pnpm-hash
|
|
run: echo "key=$(sha256sum pnpm-lock.yaml | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
|
- id: pnpm-cache
|
|
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
|
with:
|
|
path: |
|
|
${{ env.PNPM_STORE_DIR }}
|
|
${{ env.COREPACK_HOME }}
|
|
node_modules
|
|
apps/web/node_modules
|
|
packages/ui/node_modules
|
|
key: pnpm-${{ runner.os }}-${{ steps.pnpm-hash.outputs.key }}
|
|
restore-keys: |
|
|
pnpm-${{ runner.os }}-
|
|
- name: pnpm install, typecheck, lint, test, build
|
|
env:
|
|
PNPM_CACHE_HIT: ${{ steps.pnpm-cache.outputs.cache-hit }}
|
|
run: |
|
|
set -euxo pipefail
|
|
sh scripts/ci/pnpm-ci.sh
|
|
pnpm --filter @evobgp/web run typecheck
|
|
pnpm --filter @evobgp/web run lint
|
|
pnpm --filter @evobgp/web run test
|
|
pnpm --filter @evobgp/web run build
|
|
|
|
go:
|
|
needs: [changes]
|
|
if: needs.changes.outputs.go == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
|
with:
|
|
go-version: "1.24"
|
|
cache: false
|
|
- name: Export cache paths
|
|
run: sh scripts/ci/export-cache-env.sh
|
|
- id: go-hash
|
|
run: echo "key=$(sha256sum go.sum | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
|
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
|
with:
|
|
path: |
|
|
${{ env.GOMODCACHE }}
|
|
${{ env.GOCACHE }}
|
|
${{ env.GOBIN }}
|
|
${{ env.GOLANGCI_LINT_CACHE }}
|
|
key: go-${{ runner.os }}-1.24-gl1.64.8-${{ steps.go-hash.outputs.key }}
|
|
restore-keys: |
|
|
go-${{ runner.os }}-1.24-gl1.64.8-
|
|
go-${{ runner.os }}-1.24-
|
|
- name: Download modules
|
|
env:
|
|
GOMODCACHE: ${{ env.GOMODCACHE }}
|
|
GOCACHE: ${{ env.GOCACHE }}
|
|
run: go mod download
|
|
- name: Vet
|
|
env:
|
|
GOFLAGS: -mod=readonly
|
|
run: go vet ./...
|
|
- name: Lint httpapi (ERR-01 / ARCH-01)
|
|
run: sh scripts/lint-httpapi.sh
|
|
- name: Check migration pairs (DEP-03)
|
|
run: sh scripts/check-migrations-pair.sh
|
|
- name: Validate remote speaker compose
|
|
run: sh scripts/validate-remote-speaker-compose.sh
|
|
- name: golangci-lint
|
|
env:
|
|
GOLANGCI_LINT_VERSION: v1.64.8
|
|
run: sh scripts/ci/golangci-lint.sh
|
|
- name: Test
|
|
env:
|
|
GOFLAGS: -mod=readonly
|
|
GOMODCACHE: ${{ env.GOMODCACHE }}
|
|
GOCACHE: ${{ env.GOCACHE }}
|
|
run: go test ./... -race -count=1
|
|
- name: Build all commands
|
|
env:
|
|
GOFLAGS: -mod=readonly
|
|
GOMODCACHE: ${{ env.GOMODCACHE }}
|
|
GOCACHE: ${{ env.GOCACHE }}
|
|
run: |
|
|
set -euxo pipefail
|
|
out="${RUNNER_TEMP}/evobgp-bin"
|
|
mkdir -p "$out"
|
|
for d in cmd/*/; do
|
|
name="$(basename "$d")"
|
|
go build -o "$out/$name" "./$d"
|
|
done
|
|
|
|
bird2:
|
|
runs-on: ubuntu-latest
|
|
needs: [changes, go]
|
|
if: >-
|
|
always() &&
|
|
needs.changes.result == 'success' &&
|
|
needs.go.result != 'failure' &&
|
|
(needs.changes.outputs.go == 'true' ||
|
|
needs.changes.outputs.bird_conf == 'true' ||
|
|
needs.changes.outputs.docker_bird == 'true' ||
|
|
needs.changes.outputs.docker_go == 'true')
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
- name: Install bird2 (репозиторий Ubuntu runner, как в образе evobgp-bird2)
|
|
run: |
|
|
set -euxo pipefail
|
|
if command -v sudo >/dev/null 2>&1; then SUDO=sudo; else SUDO=""; fi
|
|
$SUDO apt-get update -qq
|
|
DEBIAN_FRONTEND=noninteractive $SUDO apt-get install -y -qq bird2
|
|
bird --version
|
|
- name: bird -p on all scenario bird.conf files
|
|
env:
|
|
WORKSPACE: ${{ github.workspace }}
|
|
run: |
|
|
set -euxo pipefail
|
|
WS="${WORKSPACE:-$PWD}"
|
|
cd "$WS"
|
|
if [ ! -f internal/birdfmt/testdata/scenarios/minimal/bird.conf ]; then
|
|
echo "Нет сценариев BIRD в checkout. Проверьте, что internal/birdfmt/testdata/scenarios закоммичен и push в remote."
|
|
ls -la internal/birdfmt/testdata/ 2>/dev/null || ls -la
|
|
exit 1
|
|
fi
|
|
for conf in internal/birdfmt/testdata/scenarios/*/bird.conf; do
|
|
echo "==> $conf"
|
|
bird -c "$WS/$conf" -p
|
|
done
|
|
|
|
commitlint:
|
|
if: inputs.is_pull_request
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "22"
|
|
- name: Export cache paths
|
|
run: sh scripts/ci/export-cache-env.sh
|
|
- id: pnpm-hash
|
|
run: echo "key=$(sha256sum pnpm-lock.yaml | awk '{print $1}')" >> "$GITHUB_OUTPUT"
|
|
- id: pnpm-cache
|
|
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
|
with:
|
|
path: |
|
|
${{ env.PNPM_STORE_DIR }}
|
|
${{ env.COREPACK_HOME }}
|
|
node_modules
|
|
apps/web/node_modules
|
|
packages/ui/node_modules
|
|
key: pnpm-${{ runner.os }}-${{ steps.pnpm-hash.outputs.key }}
|
|
restore-keys: |
|
|
pnpm-${{ runner.os }}-
|
|
- name: Lint commit messages
|
|
env:
|
|
BASE_SHA: ${{ inputs.base_sha }}
|
|
HEAD_SHA: ${{ inputs.head_sha }}
|
|
PNPM_CACHE_HIT: ${{ steps.pnpm-cache.outputs.cache-hit }}
|
|
run: |
|
|
set -euxo pipefail
|
|
sh scripts/ci/pnpm-ci.sh
|
|
pnpm exec commitlint --from "$BASE_SHA" --to "$HEAD_SHA"
|
|
|
|
docker-check:
|
|
needs: [changes]
|
|
if: >-
|
|
inputs.is_pull_request &&
|
|
(needs.changes.outputs.docker_go == 'true' ||
|
|
needs.changes.outputs.docker_web == 'true' ||
|
|
needs.changes.outputs.docker_bird == 'true')
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
|
with:
|
|
name: evobgp
|
|
driver: docker-container
|
|
cleanup: false
|
|
- name: Log in to Docker Hub
|
|
if: secrets.docker_hub_token != ''
|
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
|
with:
|
|
username: ${{ secrets.docker_hub_username || gitea.actor }}
|
|
password: ${{ secrets.docker_hub_token }}
|
|
- name: Log in to Gitea Registry
|
|
if: inputs.allow_registry_login
|
|
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
|
with:
|
|
registry: git.shx.one
|
|
username: ${{ gitea.actor }}
|
|
password: ${{ secrets.ACTIONS_PAT }}
|
|
- name: bake --print
|
|
working-directory: deploy/docker
|
|
env:
|
|
BUILDX_BAKE_ENTITLEMENTS_FS: "0"
|
|
BUILDX_BAKE_FILE_RELATIVE_PATHS: "1"
|
|
run: docker buildx bake --allow=fs.read="${{ github.workspace }}" -f docker-bake.hcl --print default
|
|
- name: bake (no push)
|
|
if: inputs.allow_registry_login
|
|
working-directory: deploy/docker
|
|
env:
|
|
BUILDX_BAKE_ENTITLEMENTS_FS: "0"
|
|
BUILDX_BAKE_FILE_RELATIVE_PATHS: "1"
|
|
CACHE_REF_GO: git.shx.one/${{ github.repository_owner }}/evobgp-buildcache:go-buildcache
|
|
CACHE_REF_WEB: git.shx.one/${{ github.repository_owner }}/evobgp-buildcache:web-buildcache
|
|
CACHE_REF_BIRDC: git.shx.one/${{ github.repository_owner }}/evobgp-buildcache:birdc-buildcache
|
|
run: |
|
|
set -euxo pipefail
|
|
owner_lc="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')"
|
|
export CACHE_REF_GO="git.shx.one/${owner_lc}/evobgp-buildcache:go-buildcache"
|
|
export CACHE_REF_WEB="git.shx.one/${owner_lc}/evobgp-buildcache:web-buildcache"
|
|
export CACHE_REF_BIRDC="git.shx.one/${owner_lc}/evobgp-buildcache:birdc-buildcache"
|
|
docker buildx bake --allow=fs.read="${{ github.workspace }}" -f docker-bake.hcl default
|