.github/workflows/build.yml
name: Build
on:
push:
branches:
- main
pull_request:
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
UI_IMAGE_NAME: ${{ github.repository }}-ui
KUBECONFIG: /etc/rancher/k3s/k3s.yaml
SRVTAG: dev
IS_GITHUB_ACTIONS: true
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
check-cache:
runs-on: ubuntu-latest
outputs:
backend_hash: ${{ steps.step1.outputs.backend_hash }}
backend_cache_hit: ${{ steps.step1.outputs.backend_cache_hit }}
ui_hash: ${{ steps.step1.outputs.ui_hash }}
ui_cache_hit: ${{ steps.step1.outputs.ui_cache_hit }}
steps:
- uses: actions/checkout@v4
- id: step1
run: |
set +e
export backend_hash=checksum-${{ hashFiles('Dockerfile', 'cmd', 'pkg', 'go.mod', 'go.sum') }};
echo "backend_hash=$backend_hash" >> $GITHUB_OUTPUT;
docker manifest inspect ghcr.io/direktiv/direktiv:$backend_hash;
echo "backend_cache_hit=$?" >> $GITHUB_OUTPUT;
export ui_hash=checksum-${{ hashFiles('ui') }};
echo "ui_hash=$ui_hash" >> $GITHUB_OUTPUT;
docker manifest inspect ghcr.io/direktiv/direktiv-ui:$ui_hash;
echo "ui_cache_hit=$?" >> $GITHUB_OUTPUT;
cat $GITHUB_OUTPUT;
backend-license:
runs-on: ubuntu-latest
needs: [check-cache]
if: needs.check-cache.outputs.backend_cache_hit == '1'
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Run check
run: make tests-license-check
backend-lint:
runs-on: ubuntu-latest
needs: [check-cache]
if: needs.check-cache.outputs.backend_cache_hit == '1'
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.60
backend-test:
runs-on: ubuntu-latest
needs: [check-cache]
if: needs.check-cache.outputs.backend_cache_hit == '1'
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: Run unit tests
run: |
go test ./... -coverprofile coverage.out -covermode count
env:
DIREKTIV_APP: flow
backend-build:
needs: [check-cache, backend-license, backend-lint, backend-test]
runs-on: ubuntu-latest
if: needs.check-cache.outputs.backend_cache_hit == '1'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,format=long
type=raw,value=${{ needs.check-cache.outputs.backend_hash }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-flags: --debug
- name: Set current date
run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: api-${{ env.CURRENT_DATE }}-${{ runner.os }}-buildx-x
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
provenance: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
ui-build:
runs-on: ubuntu-latest
needs: [check-cache]
if: needs.check-cache.outputs.ui_cache_hit == '1'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.UI_IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,format=long
type=raw,value=${{ needs.check-cache.outputs.ui_hash }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-flags: --debug
- name: Set current date
run: echo "CURRENT_DATE=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ui-${{ env.CURRENT_DATE }}-${{ runner.os }}-buildx-x
- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ui
push: true
platforms: linux/amd64,linux/arm64
provenance: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
e2e:
needs: [check-cache, ui-build, backend-build]
if: ${{ !failure() && !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 18
strategy:
fail-fast: false
max-parallel: 20
matrix:
sections:
- type: api
suit: engine
- type: api
suit: events
- type: api
suit: gateway
- type: api
suit: gateway2
- type: api
suit: instances
- type: api
suit: kubernetes
- type: api
suit: part1
- type: api
suit: part2
- type: api
suit: services
- type: api
suit: actions
- type: playwright
suit: "1/4"
- type: playwright
suit: "2/4"
- type: playwright
suit: "3/4"
- type: playwright
suit: "4/4"
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup k3s
run: curl -sfL https://get.k3s.io | sh -s - --disable traefik --write-kubeconfig-mode=644
- name: Install Helm
run: curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
- name: Create namespace for the DB
run: kubectl create ns postgres
- name: Install Postgres using Percona Operator
run: |
helm repo add percona https://percona.github.io/percona-helm-charts/
helm install --create-namespace -n postgres pg-operator percona/pg-operator --version 2.4.2 --wait
kubectl apply -f https://raw.githubusercontent.com/direktiv/direktiv/main/scripts/kubernetes/install/db/basic.yaml
- name: Install Knative
run: |
kubectl apply -f https://github.com/knative/operator/releases/download/knative-v1.12.2/operator.yaml
kubectl create ns knative-serving
kubectl apply -f https://raw.githubusercontent.com/direktiv/direktiv/main/scripts/kubernetes/install/knative/basic.yaml
kubectl apply --filename https://github.com/knative/net-contour/releases/download/knative-v1.11.1/contour.yaml
kubectl delete namespace contour-external
- name: Wait for Direktiv pods to be ready
run: |
sleep 1
kubectl wait -n postgres --for=condition=Ready pod -l app.kubernetes.io/instance=direktiv-cluster --timeout=120s
- name: Generate dev.yaml file with database credentials
run: |
DB_HOST=$(kubectl get secrets -n postgres direktiv-cluster-pguser-direktiv -o 'go-template={{index .data "host"}}' | base64 --decode)
DB_PORT=$(kubectl get secrets -n postgres direktiv-cluster-pguser-direktiv -o 'go-template={{index .data "port"}}' | base64 --decode)
DB_USER=$(kubectl get secrets -n postgres direktiv-cluster-pguser-direktiv -o 'go-template={{index .data "user"}}' | base64 --decode)
DB_PASSWORD=$(kubectl get secrets -n postgres direktiv-cluster-pguser-direktiv -o 'go-template={{index .data "password"}}' | base64 --decode)
DB_NAME=$(kubectl get secrets -n postgres direktiv-cluster-pguser-direktiv -o 'go-template={{index .data "dbname"}}' | base64 --decode)
cat <<EOF > dev.yaml
pullPolicy: IfNotPresent
registry: ghcr.io
image: direktiv/direktiv
tag: ${{ needs.check-cache.outputs.backend_hash }}
flow:
logging: json
frontend:
image: direktiv/direktiv-ui
tag: ${{ needs.check-cache.outputs.ui_hash }}
database:
host: "$DB_HOST"
port: $DB_PORT
user: "$DB_USER"
password: "$DB_PASSWORD"
name: "$DB_NAME"
sslmode: require
EOF
cat dev.yaml
- name: Install Direktiv chart
run: helm install direktiv -f dev.yaml ./charts/direktiv/
- name: Set Environment Variables
run: echo "DIREKTIV_HOST=127.0.0.1:80" >> $GITHUB_ENV
- name: Wait until API is healthy
run: |
endpoint="http://${{ env.DIREKTIV_HOST }}/api/v2/status";
for attempt in {1..20}
do
status_code=$(curl -s -o /dev/null -w "%{http_code}" $endpoint || true);
echo "API status code: $status_code";
if [ $status_code -eq 200 ]
then
echo "API ready" && exit 0;
fi
sleep 1;
done
echo "API failed to be healthy";
kubectl get pods;
kubectl logs deployments/direktiv-flow;
exit 1;
- name: Wait until UI is healthy
run: |
endpoint="http://${{ env.DIREKTIV_HOST }}";
for attempt in {1..20}
do
status_code=$(curl -s -o /dev/null -w "%{http_code}" $endpoint || true);
echo "API status code: $status_code";
if [ $status_code -eq 200 ]
then
echo "UI ready" && exit 0;
fi
sleep 1;
done
echo "UI failed to be healthy";
kubectl get pods;
kubectl describe po direktiv-frontend;
exit 1;
- name: Run API E2E tests
if: matrix.sections.type == 'api'
run: |
npm -prefix tests run jest -- ${{ matrix.sections.suit }}/ --runInBand
env:
DIREKTIV_HOST: ${{ env.DIREKTIV_HOST }}
GITHUB_ACTIONS: true
- name: Run Playwright E2E tests
id: playwrightRun
if: matrix.sections.type == 'playwright'
run: |
PLAYWRIGHT_SHARD=${{ matrix.sections.suit }} make docker-e2e-playwright
- name: Upload Playwright Artifacts
if: failure() && steps.playwrightRun.outcome == 'failure'
uses: actions/upload-artifact@v4
with:
name: test-results
path: ./ui/test-results