Skip to content

Kookster310 send deploy EKS 🚀 #199

Kookster310 send deploy EKS 🚀

Kookster310 send deploy EKS 🚀 #199

Workflow file for this run

name: deploy-k8s
run-name: ${{ github.actor }} send deploy EKS 🚀
on:
pull_request:
types: [opened, reopened, synchronize, edited, closed]
#schedule:
# - cron: '30 2 * * *' # run daily
workflow_dispatch:
inputs:
delete:
description: 'CI Instance ID to delete. If present, all other jobs will be skipped.'
required: false
default: ''
workflow_call:
env:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
AWS_URL: ${{ secrets.AWS_URL }}
pull_req_id: ${{github.event.pull_request.number}}
DATE: $(date -d '-1 day' '+%Y-%m-%d'|sed 's/-//g')
CURRENT_DATE: $(date '+%Y-%m-%d %H:%M:%S'|sed 's/-//g')
CI_PACKAGE_BRANCH: ${{ github.event.pull_request.head.ref || github.event.ref || 'develop' }}
CI_PROJECT: ${{github.event.pull_request.head.repo.name || github.event.repository.name || 'processmaker' }}
CI_PR_BODY: ${{ github.event_name == 'schedule' && 'No ci tags needed here' || github.event.pull_request.body }}
IMAGE_TAG: $(echo "$CI_PROJECT-$CI_PACKAGE_BRANCH" | sed "s;/;-;g" | sed "s/refs-heads-//g")
DEPLOY: ${{ secrets.DEPLOY }}
GH_USER: ${{ secrets.GH_USER }}
GH_EMAIL: ${{ secrets.GH_EMAIL }}
DOM_EKS: ${{ secrets.DOM_EKS }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
BUILD_BASE: ${{ (contains(github.event.pull_request.body, 'ci:build-base') || github.event_name == 'schedule') && '1' || '0' }}
MULTITENANCY: ${{ (contains(github.event.pull_request.body, 'ci:multitenancy')) && 'true' || 'false' }}
BASE_IMAGE: ${{ secrets.REGISTRY_HOST }}/processmaker/processmaker:base
CUSTOMER_LICENSES_PAT: ${{ secrets.CUSTOMER_LICENSES_PAT }}
# K8S_BRANCH: ${{ contains(github.event.pull_request.body, 'ci:next') && 'next' || 'release-2024-fall' }}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ inputs.delete }}
cancel-in-progress: true
jobs:
imageEKS:
name: build-docker-image-EKS
if: github.event.action != 'closed' && inputs.delete == ''
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: processmaker/.github
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Set image name
run: |
echo "IMAGE=${{ secrets.REGISTRY_HOST }}/processmaker/enterprise:$RESOLVED_IMAGE_TAG" >> $GITHUB_ENV
- name: Generate image EKS
if: ${{ !contains(github.event.pull_request.body, 'ci:skip-build') }}
run: |
cd pm4-k8s-distribution/images
export CI_RELEASE_BRANCH=$RELEASE_BRANCH
branch=$(echo "${{ env.CI_PACKAGE_BRANCH }}" | sed 's/refs-heads-//g') tag=${{env.IMAGE_TAG}} bash build.k8s-cicd.sh
echo "VERSION=${{ env.IMAGE_TAG }}" >> $GITHUB_ENV
- name: List Images
run: |
docker images
# - name: Run Trivy vulnerability scanner
# uses: aquasecurity/trivy-action@master
# with:
# image-ref: processmaker/enterprise:${{ env.VERSION }}
# format: 'table'
# exit-code: '0'
# ignore-unfixed: false
# vuln-type: 'os,library'
# scanners: 'vuln,secret'
# severity: 'MEDIUM,HIGH,CRITICAL'
# env:
# TRIVY_TIMEOUT: 30m
- name: Login to Harbor
uses: docker/login-action@v2
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push Enterprise Image to Harbor
if: ${{ !contains(github.event.pull_request.body, 'ci:skip-build') }}
run: |
docker tag processmaker/enterprise:${{env.IMAGE_TAG}} ${{ secrets.REGISTRY_HOST }}/processmaker/enterprise:${{env.IMAGE_TAG}}
docker push ${{ secrets.REGISTRY_HOST }}/processmaker/enterprise:${{env.IMAGE_TAG}}
- name: Check rate limit on failure
if: failure()
run: |
echo "=== Checking GitHub API rate limit status ==="
curl -I --header "Authorization: Bearer ${{ secrets.GIT_TOKEN }}" https://api.github.com
echo ""
echo "=== Rate limit check complete ==="
imageEKSBase:
name: build-docker-image-EKS-base
if: github.event.action != 'closed' && inputs.delete == '' && contains(github.event.pull_request.body, 'ci:performance-tests')
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: processmaker/.github
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Set base branch and image tag
run: |
BASE_BRANCH="${{ github.event.pull_request.base.ref || github.event.repository.default_branch || 'develop' }}"
BASE_IMAGE_TAG="processmaker-$(echo "${BASE_BRANCH}" | sed 's;/;-;g')"
echo "BASE_BRANCH=${BASE_BRANCH}" >> $GITHUB_ENV
echo "BASE_IMAGE_TAG=${BASE_IMAGE_TAG}" >> $GITHUB_ENV
echo "Base branch: ${BASE_BRANCH} -> image tag: ${BASE_IMAGE_TAG}"
- name: Generate image EKS (base)
if: env.BASE_BRANCH != 'develop' && !contains(github.event.pull_request.body, 'ci:skip-build')
run: |
cd pm4-k8s-distribution/images
export CI_RELEASE_BRANCH=$RELEASE_BRANCH
branch="${{ env.BASE_BRANCH }}" tag="${{ env.BASE_IMAGE_TAG }}" bash build.k8s-cicd.sh
echo "VERSION=${{ env.BASE_IMAGE_TAG }}" >> $GITHUB_ENV
- name: List Images
if: env.BASE_BRANCH != 'develop' && !contains(github.event.pull_request.body, 'ci:skip-build')
run: docker images
- name: Login to Harbor
if: env.BASE_BRANCH != 'develop' && !contains(github.event.pull_request.body, 'ci:skip-build')
uses: docker/login-action@v2
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Push Enterprise Image to Harbor (base)
if: env.BASE_BRANCH != 'develop' && !contains(github.event.pull_request.body, 'ci:skip-build')
run: |
docker tag processmaker/enterprise:${{ env.BASE_IMAGE_TAG }} ${{ secrets.REGISTRY_HOST }}/processmaker/enterprise:${{ env.BASE_IMAGE_TAG }}
docker push ${{ secrets.REGISTRY_HOST }}/processmaker/enterprise:${{ env.BASE_IMAGE_TAG }}
- name: Base image ready
if: env.BASE_BRANCH == 'develop' || contains(github.event.pull_request.body, 'ci:skip-build')
run: |
echo "Skipped base build (base is develop or ci:skip-build); baseline will use existing image if needed."
deployEKS:
name: deploy-EKS
if: contains(github.event.pull_request.body, 'ci:deploy')
needs: imageEKS
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout .github repo
uses: actions/checkout@v4
with:
repository: processmaker/.github
ref: main
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Install pm4-tools
run: |
cd pm4-k8s-distribution/images/pm4-tools
composer install --no-interaction
cd ..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Set up kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
echo ${{ secrets.AWS_ACCESS_KEY_ID }} | md5sum
- name: Authenticate with Amazon EKS
run: aws eks update-kubeconfig --region us-east-1 --name pm4-eng
- name: Deploy instance EKS
env:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
CURRENT_DATE: ${{ env.CURRENT_DATE }}
HELM_REPO: ${{ secrets.HELM_REPO }}
HELM_USERNAME: ${{ secrets.HELM_USERNAME }}
HELM_PASSWORD: ${{ secrets.HELM_PASSWORD }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPEN_AI_SECRET: ${{ secrets.OPENAI_API_KEY }}
ANALYTICS_AWS_ACCESS_KEY: ${{ secrets.ANALYTICS_AWS_ACCESS_KEY }}
ANALYTICS_AWS_SECRET_KEY: ${{ secrets.ANALYTICS_AWS_SECRET_KEY }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
REGISTRY_HOST: ${{ secrets.REGISTRY_HOST }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
TWILIO_SID: ${{ secrets.TWILIO_SID }}
TWILIO_TOKEN: ${{ secrets.TWILIO_TOKEN }}
versionHelm: ${{ env.versionHelm }}
DOM_EKS: ${{ env.DOM_EKS }}
KEYCLOAK_CLIENT_SECRET: ${{ secrets.KEYCLOAK_CLIENT_SECRET }}
KEYCLOAK_PASSWORD: ${{ secrets.KEYCLOAK_PASSWORD }}
CUSTOMER_LICENSES_PAT: ${{ secrets.CUSTOMER_LICENSES_PAT }}
RDS_ADMIN_USERNAME: ${{ secrets.RDS_ADMIN_USERNAME }}
RDS_ADMIN_PASSWORD: ${{ secrets.RDS_ADMIN_PASSWORD }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
instance=$(echo -n ${{env.IMAGE_TAG}} | md5sum | head -c 10)
echo "INSTANCE: $instance"
echo "IMAGE_TAG: $IMAGE_TAG"
sed -i "s#{{INSTANCE}}#$instance#g" .github/scripts/deploy-instance.sh
sed -i "s#{{INSTANCE}}#$instance#g" .github/templates/instance.yaml
sed -i "s#{{INSTANCE}}#$instance#g" .github/templates/db.yaml
sed -i "s#{{IMAGE_TAG}}#$IMAGE_TAG#g" .github/templates/instance.yaml
sed -i "s#{{KEYCLOAK_CLIENT_SECRET}}#$KEYCLOAK_CLIENT_SECRET#g" .github/templates/instance.yaml
sed -i "s#{{KEYCLOAK_PASSWORD}}#$KEYCLOAK_PASSWORD#g" .github/templates/instance.yaml
sed -i "s#{{CUSTOMER_LICENSES_PAT}}#$CUSTOMER_LICENSES_PAT#g" .github/templates/instance.yaml
sed -i "s#{{MYSQL_USER}}#$RDS_ADMIN_USERNAME#g" .github/templates/instance.yaml
sed -i "s#{{MYSQL_PASSWORD}}#$RDS_ADMIN_PASSWORD#g" .github/templates/instance.yaml
sed -i "s#{{MULTITENANCY}}#$MULTITENANCY#g" .github/templates/instance.yaml
sed -i "s#{{MYSQL_USERNAME}}#$RDS_ADMIN_USERNAME#g" .github/templates/db.yaml
sed -i "s#{{MYSQL_PASSWORD}}#$RDS_ADMIN_PASSWORD#g" .github/templates/db.yaml
echo "=== Checking instance.yaml after replacements ==="
cat .github/templates/instance.yaml
echo "=== Checking db.yaml after replacements ==="
cat .github/templates/db.yaml
chmod +x .github/scripts/deploy-instance.sh
bash .github/scripts/deploy-instance.sh
if [ "$MULTITENANCY" = "true" ]; then
export INSTANCE_URL="https://tenant-1.ci-$instance.engk8s.processmaker.net"
else
export INSTANCE_URL="https://ci-$instance.engk8s.processmaker.net"
fi
echo "Instance URL: $INSTANCE_URL"
bash .github/scripts/gh_comment.sh "$CI_PROJECT" "$pull_req_id"
runAPITest:
name: Run API Tests
needs: [deployEKS]
if: contains(github.event.pull_request.body, 'ci:api-test')
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: processmaker/.github
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Install pm4-tools
run: |
echo "versionHelm=$(grep "version:" "pm4-k8s-distribution/charts/enterprise/Chart.yaml" | awk '{print $2}' | sed 's/\"//g')" >> $GITHUB_ENV
cd pm4-k8s-distribution/images/pm4-tools
composer install --no-interaction
cd ..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID1 }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY1 }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Set up kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Authenticate with Amazon EKS
run: aws eks update-kubeconfig --region us-east-1 --name pm4-eng
- name: Run the API tests
run: |
INSTANCE=$(echo -n ${{env.IMAGE_TAG}} | md5sum | head -c 10)
namespace="ci-$INSTANCE-ns-pm4"
pr_body=$(jq -r .pull_request.body < "$GITHUB_EVENT_PATH" | base64)
kubectl get pods --namespace=$namespace
pod_names=$(kubectl get pods --namespace=$namespace --field-selector=status.phase=Running -o jsonpath="{.items[*].metadata.name}" | tr ' ' '\n' | grep -E '(-processmaker-scheduler-)')
for pod in $pod_names; do
code='
has_processmaker=$(ls /opt | grep processmaker)
has_sudo=$(ls /usr/bin | grep sudo)
has_php=$(ls /usr/bin | grep php)
if [ ! -z "$has_processmaker" ] && [ ! -z "$has_sudo" ] && [ ! -z "$has_php" ]; then
echo $pr_body | base64 -d > /tmp/pr_body
cd /opt/processmaker
docker system prune -af
sudo -u nginx php artisan package-api-testing:run --body="$pr_body"
else
exit 1
fi'
kubectl exec -n $namespace $pod -- /bin/sh -c "pr_body='${pr_body}';${code}" | tee /tmp/comment.md && break || true
done
# Send the content of /tmp/comment.md as a PR comment
MESSAGE=$(cat /tmp/comment.md)
GIT_TOKEN=${{ secrets.GIT_TOKEN }}
GITHUB_REPOSITORY=${{ github.repository }}
PR_NUMBER=$(jq -r .number < "$GITHUB_EVENT_PATH")
if [ -z "$PR_NUMBER" ]; then
echo "The PR number is not available. Make sure this script is executed in a context of Pull Request."
exit 1
fi
URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments"
json_payload=$(jq -n --arg message "$MESSAGE" '{"body": $message}')
curl -s \
-H "Authorization: token ${GIT_TOKEN}" \
-H "Accept: application/vnd.github.v3+json" \
-d "$json_payload" \
"${URL}"
deleteEKS:
name: Delete Instance
if: github.event.action == 'closed' || inputs.delete != ''
runs-on: self-hosted
steps:
- name: Delete instance EKS
run: |
# If inputs.delete does not equal '', set the IMAGE_TAG to the value of inputs.delete
if [ "${{ inputs.delete }}" != "" ]; then
IMAGE_TAG=${{ inputs.delete }}
else
IMAGE_TAG=${{ env.IMAGE_TAG }}
fi
INSTANCE=$(echo -n $IMAGE_TAG | md5sum | head -c 10)
if kubectl get namespace/ci-$INSTANCE-ns-pm4 ; then
echo "Deleting Instace :: ci-$INSTANCE"
helm delete ci-$INSTANCE
kubectl delete namespace ci-$INSTANCE-ns-pm4
#Drop database
deploy_db="pm4_ci-${INSTANCE}%"
deploy_ai="\`pm4_ci-$INSTANCE_ai\`"
# check that that string length of $deploy_db is 12 or more as a safety check. If its less than 12, exit now
if [ ${#deploy_db} -lt 12 ]; then
exit 1
fi
# Drop the main database including any tenant databases
mysql -u${{ secrets.USER_MYSQL_ENG }} -p${{ secrets.PASS_MYSQL_ENG }} -h ${{ secrets.RDS_ENG }} -N -e "SHOW DATABASES LIKE '${deploy_db}'" | xargs -I{} mysql -u${{ secrets.USER_MYSQL_ENG }} -p${{ secrets.PASS_MYSQL_ENG }} -h ${{ secrets.RDS_ENG }} -e "DROP DATABASE IF EXISTS \`{}\`;"
mysql -u${{ secrets.USER_MYSQL_ENG }} -p${{ secrets.PASS_MYSQL_ENG }} -e "DROP DATABASE IF EXISTS $deploy_ai" -h ${{ secrets.RDS_ENG }}
mysql -u${{ secrets.USER_MYSQL_ENG }} -p${{ secrets.PASS_MYSQL_ENG }} -e "DROP USER IF EXISTS 'user_ci-$INSTANCE'@'%'" -h ${{ secrets.RDS_ENG }}
mysql -u${{ secrets.USER_MYSQL_ENG }} -p${{ secrets.PASS_MYSQL_ENG }} -e "DROP USER IF EXISTS 'user_ci-$INSTANCE_ai'@'%'" -h ${{ secrets.RDS_ENG }}
#Drop image Harbor
curl -X DELETE -u ${{ secrets.REGISTRY_USERNAME }}:${{ secrets.REGISTRY_PASSWORD }} "https://${{ secrets.REGISTRY_HOST }}/api/v2.0/projects/processmaker/repositories/enterprise/artifacts/${IMAGE_TAG}"
echo "The instance [https://ci-$INSTANCE.engk8s.processmaker.net] was deleted!!"
else
echo "The pull request does not have an instance on K8s [https://ci-$INSTANCE.engk8s.processmaker.net] not found!!"
fi
# Performance test releases (safety net if perf job was cancelled). Perf installs in default namespace.
# Release names include 8-char hex suffix: ci-${INSTANCE}-perf-baseline-<hex8>, ci-${INSTANCE}-perf-update-<hex8>
for release in $(helm list -n default -q 2>/dev/null | grep -E "^ci-${INSTANCE}-perf-(baseline|update)-[a-f0-9]{8}$" || true); do
echo "Uninstalling performance release: $release (default namespace)"
helm uninstall "$release" --namespace default 2>/dev/null || true
ns="${release}-ns-pm4"
if kubectl get namespace "$ns" &>/dev/null; then
echo "Deleting performance namespace: $ns"
kubectl delete namespace "$ns" --timeout=120s --ignore-not-found=true || true
fi
done
perfBaseline:
name: perf-baseline
if: github.event.action != 'closed' && inputs.delete == '' && contains(github.event.pull_request.body, 'ci:performance-tests')
needs: [imageEKS, imageEKSBase]
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout .github repo
uses: actions/checkout@v4
with:
repository: processmaker/.github
ref: automated-performance-tests
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Install pm4-tools
run: |
echo "versionHelm=$(grep "version:" "pm4-k8s-distribution/charts/enterprise/Chart.yaml" | awk '{print $2}' | sed 's/\"//g')" >> $GITHUB_ENV
cd pm4-k8s-distribution/images/pm4-tools
composer install --no-interaction
cd ../..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Set up kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Authenticate with Amazon EKS
run: aws eks update-kubeconfig --region us-east-1 --name pm4-eng
- name: Prepare performance test (baseline)
id: perf_prep
run: |
INSTANCE=$(echo -n ${{ env.IMAGE_TAG }} | md5sum | head -c 10)
RANDOM_SUFFIX=$(openssl rand -hex 4)
echo "instance=${INSTANCE}" >> $GITHUB_OUTPUT
echo "INSTANCE=${INSTANCE}" >> $GITHUB_ENV
echo "random_suffix=${RANDOM_SUFFIX}" >> $GITHUB_OUTPUT
echo "RANDOM_SUFFIX=${RANDOM_SUFFIX}" >> $GITHUB_ENV
echo "Instance ID: ${INSTANCE} (hostname suffix: ${RANDOM_SUFFIX})"
- name: Substitute perf instance template (baseline)
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
BASE_BRANCH="${{ github.event.pull_request.base.ref || github.event.repository.default_branch || 'develop' }}"
BASE_IMAGE_TAG="processmaker-$(echo "${BASE_BRANCH}" | sed 's;/;-;g')"
PERF_NODE_LABEL="ci-${INSTANCE}-baseline"
echo "BASE_BRANCH=${BASE_BRANCH}" >> $GITHUB_ENV
echo "BASE_IMAGE_TAG=${BASE_IMAGE_TAG}" >> $GITHUB_ENV
sed -i "s#{{INSTANCE}}#${INSTANCE}#g" .github/templates/instance-perf.yaml
sed -i "s#{{PERF_NODE_LABEL}}#${PERF_NODE_LABEL}#g" .github/templates/instance-perf.yaml
sed -i "s#{{APP_VERSION}}#${BASE_IMAGE_TAG}#g" .github/templates/instance-perf.yaml
sed -i "s#{{CUSTOMER_LICENSES_PAT}}#${{ secrets.CUSTOMER_LICENSES_PAT }}#g" .github/templates/instance-perf.yaml
sed -i "s#{{KEYCLOAK_CLIENT_SECRET}}#${{ secrets.KEYCLOAK_CLIENT_SECRET }}#g" .github/templates/instance-perf.yaml
sed -i "s#{{KEYCLOAK_PASSWORD}}#${{ secrets.KEYCLOAK_PASSWORD }}#g" .github/templates/instance-perf.yaml
- name: Create performance node group (baseline)
run: |
export INSTANCE="${{ steps.perf_prep.outputs.instance }}"
export PERF_SUFFIX=baseline
chmod +x .github/scripts/create-perf-nodegroup.sh
.github/scripts/create-perf-nodegroup.sh
- name: Wait for performance node Ready (baseline)
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
echo "Waiting for node with label performance=ci-${INSTANCE}-baseline to be Ready..."
for i in $(seq 1 30); do
READY=$(kubectl get nodes -l "performance=ci-${INSTANCE}-baseline" -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)
if [ "$READY" = "True" ]; then
echo "Node is Ready."
break
fi
echo "Waiting... ($i/30)"
sleep 20
done
kubectl get nodes -l "performance=ci-${INSTANCE}-baseline"
- name: Checkout automated-performance-metrics
uses: actions/checkout@v4
with:
repository: ProcessMaker/automated-performance-metrics
ref: main
path: automated-performance-metrics
token: ${{ secrets.GIT_TOKEN }}
- name: Install k6
run: |
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install -y k6
- name: Discover k6 tests
run: |
find automated-performance-metrics/scripts -name '*.js' -type f | sort > perf-test-scripts.txt
if [ ! -s perf-test-scripts.txt ]; then
echo "automated-performance-metrics/scripts/Api/users-index.js" > perf-test-scripts.txt
fi
echo "Discovered k6 tests:"
cat perf-test-scripts.txt
- name: Deploy baseline (base branch) and verify
id: baseline
env:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
HELM_REPO: ${{ secrets.HELM_REPO }}
HELM_USERNAME: ${{ secrets.HELM_USERNAME }}
HELM_PASSWORD: ${{ secrets.HELM_PASSWORD }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANALYTICS_AWS_ACCESS_KEY: ${{ secrets.ANALYTICS_AWS_ACCESS_KEY }}
ANALYTICS_AWS_SECRET_KEY: ${{ secrets.ANALYTICS_AWS_SECRET_KEY }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
REGISTRY_HOST: ${{ secrets.REGISTRY_HOST }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
TWILIO_SID: ${{ secrets.TWILIO_SID }}
TWILIO_TOKEN: ${{ secrets.TWILIO_TOKEN }}
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-baseline-${RANDOM_SUFFIX}"
export RELEASE_NAME APP_VERSION="${{ env.BASE_IMAGE_TAG }}" INSTANCE
export versionHelm="${{ env.versionHelm }}"
chmod +x .github/scripts/deploy-perf-instance.sh
.github/scripts/deploy-perf-instance.sh
BASE_URL="https://${RELEASE_NAME}.engk8s.processmaker.net"
echo "Checking $BASE_URL/login ..."
for i in $(seq 1 30); do
CODE=$(curl -s -o /dev/null -w "%{http_code}" -k "$BASE_URL/login" || echo 000)
if [ "$CODE" = "200" ]; then
echo "Baseline /login returned 200."
break
fi
echo "Attempt $i: got $CODE"
sleep 15
done
CODE=$(curl -s -o /dev/null -w "%{http_code}" -k "$BASE_URL/login")
if [ "$CODE" != "200" ]; then
echo "Baseline /login did not return 200 (got $CODE). Continuing to cleanup."
fi
- name: Wait for TLS certificate (baseline)
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-baseline-${RANDOM_SUFFIX}"
APP_NS="${RELEASE_NAME}-ns-pm4"
CERT_NAME="${RELEASE_NAME}-tls-secret"
echo "Waiting for cert-manager to issue TLS certificate for ${RELEASE_NAME}..."
READY=""
for i in $(seq 1 5); do
READY=$(kubectl get certificate -n "$APP_NS" -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | tr ' ' '\n' | head -1)
if [ "$READY" = "True" ]; then
echo "TLS certificate Ready."
break
fi
echo "Waiting for TLS certificate... ($i/5)"
sleep 10
done
if [ "$READY" != "True" ]; then
echo "Certificate not ready after 5 loops; deleting to trigger regeneration..."
kubectl delete certificate -n "$APP_NS" "$CERT_NAME" --ignore-not-found=true || true
sleep 5
echo "Waiting up to 5 more loops for renewed certificate..."
for i in $(seq 1 5); do
READY=$(kubectl get certificate -n "$APP_NS" -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | tr ' ' '\n' | head -1)
if [ "$READY" = "True" ]; then
echo "TLS certificate Ready (after retry)."
break
fi
echo "Waiting for TLS certificate (retry)... ($i/5)"
sleep 10
done
fi
if [ "$READY" != "True" ]; then
echo "TLS certificate did not become Ready; k6 may see certificate errors."
fi
- name: Generate admin access token (baseline)
id: token_baseline
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-baseline-${RANDOM_SUFFIX}"
APP_NS="${RELEASE_NAME}-ns-pm4"
POD=""
attempts=0
while [ -z "$POD" ]; do
POD=$(kubectl get pods -n "$APP_NS" -l service=web --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
[ -n "$POD" ] && break
attempts=$((attempts + 1))
[ $attempts -ge 5 ] && break
sleep 10
done
echo "Using web pod: ${POD:-none}"
TOKEN=$(kubectl exec -n "$APP_NS" "$POD" -- sudo -u nginx php /opt/processmaker/artisan processmaker:generate-access-token admin 2>/dev/null | tr -d '\r\n') || true
if [ -z "$TOKEN" ]; then
echo "Could not generate baseline token"
echo "token=" >> $GITHUB_OUTPUT
exit 0
fi
echo "token=${TOKEN}" >> $GITHUB_OUTPUT
- name: Run k6 on baseline
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-baseline-${RANDOM_SUFFIX}"
APP_URL="https://${RELEASE_NAME}.engk8s.processmaker.net"
TOKEN="${{ steps.token_baseline.outputs.token }}"
if [ -z "$TOKEN" ]; then
echo "Skipping baseline k6: no token"
exit 0
fi
export BASE_PATH="${APP_URL}"
export BEARER_TOKEN="Bearer ${TOKEN}"
while IFS= read -r script; do
[ -z "$script" ] && continue
test_id=$(echo "$script" | sed 's|automated-performance-metrics/scripts/||;s|\.js$||;s|/|-|g')
echo "Running baseline k6: $script (id: $test_id)"
k6 run "$script" 2>&1 | tee "baseline-${test_id}-k6-results.txt" || true
done < perf-test-scripts.txt
- name: Upload baseline results
uses: actions/upload-artifact@v4
with:
name: baseline-results
path: |
perf-test-scripts.txt
baseline-*-k6-results.txt
- name: Cleanup baseline
if: always()
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-baseline-${RANDOM_SUFFIX}"
APP_NS="${RELEASE_NAME}-ns-pm4"
helm uninstall "${RELEASE_NAME}" --namespace default 2>/dev/null || true
kubectl delete namespace "${APP_NS}" --timeout=120s --ignore-not-found=true || true
- name: Delete performance node group (baseline)
if: always()
run: |
export INSTANCE="${{ steps.perf_prep.outputs.instance }}"
export PERF_SUFFIX=baseline
chmod +x .github/scripts/delete-perf-nodegroup.sh
.github/scripts/delete-perf-nodegroup.sh
perfUpdate:
name: perf-update
if: github.event.action != 'closed' && inputs.delete == '' && contains(github.event.pull_request.body, 'ci:performance-tests')
needs: [imageEKS, imageEKSBase]
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout .github repo
uses: actions/checkout@v4
with:
repository: processmaker/.github
ref: automated-performance-tests
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Install pm4-tools
run: |
echo "versionHelm=$(grep "version:" "pm4-k8s-distribution/charts/enterprise/Chart.yaml" | awk '{print $2}' | sed 's/\"//g')" >> $GITHUB_ENV
cd pm4-k8s-distribution/images/pm4-tools
composer install --no-interaction
cd ../..
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Set up kubectl
run: |
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
- name: Authenticate with Amazon EKS
run: aws eks update-kubeconfig --region us-east-1 --name pm4-eng
- name: Prepare performance test (update)
id: perf_prep
run: |
INSTANCE=$(echo -n ${{ env.IMAGE_TAG }} | md5sum | head -c 10)
RANDOM_SUFFIX=$(openssl rand -hex 4)
echo "instance=${INSTANCE}" >> $GITHUB_OUTPUT
echo "INSTANCE=${INSTANCE}" >> $GITHUB_ENV
echo "random_suffix=${RANDOM_SUFFIX}" >> $GITHUB_OUTPUT
echo "RANDOM_SUFFIX=${RANDOM_SUFFIX}" >> $GITHUB_ENV
echo "Instance ID: ${INSTANCE} (hostname suffix: ${RANDOM_SUFFIX})"
- name: Substitute perf instance template (update)
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
PERF_NODE_LABEL="ci-${INSTANCE}-update"
sed -i "s#{{INSTANCE}}#${INSTANCE}#g" .github/templates/instance-perf.yaml
sed -i "s#{{PERF_NODE_LABEL}}#${PERF_NODE_LABEL}#g" .github/templates/instance-perf.yaml
sed -i "s#{{APP_VERSION}}#${{ env.IMAGE_TAG }}#g" .github/templates/instance-perf.yaml
sed -i "s#{{CUSTOMER_LICENSES_PAT}}#${{ secrets.CUSTOMER_LICENSES_PAT }}#g" .github/templates/instance-perf.yaml
sed -i "s#{{KEYCLOAK_CLIENT_SECRET}}#${{ secrets.KEYCLOAK_CLIENT_SECRET }}#g" .github/templates/instance-perf.yaml
sed -i "s#{{KEYCLOAK_PASSWORD}}#${{ secrets.KEYCLOAK_PASSWORD }}#g" .github/templates/instance-perf.yaml
- name: Create performance node group (update)
run: |
export INSTANCE="${{ steps.perf_prep.outputs.instance }}"
export PERF_SUFFIX=update
chmod +x .github/scripts/create-perf-nodegroup.sh
.github/scripts/create-perf-nodegroup.sh
- name: Wait for performance node Ready (update)
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
echo "Waiting for node with label performance=ci-${INSTANCE}-update to be Ready..."
for i in $(seq 1 30); do
READY=$(kubectl get nodes -l "performance=ci-${INSTANCE}-update" -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)
if [ "$READY" = "True" ]; then
echo "Node is Ready."
break
fi
echo "Waiting... ($i/30)"
sleep 20
done
kubectl get nodes -l "performance=ci-${INSTANCE}-update"
- name: Checkout automated-performance-metrics
uses: actions/checkout@v4
with:
repository: ProcessMaker/automated-performance-metrics
ref: main
path: automated-performance-metrics
token: ${{ secrets.GIT_TOKEN }}
- name: Install k6
run: |
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install -y k6
- name: Discover k6 tests
run: |
find automated-performance-metrics/scripts -name '*.js' -type f | sort > perf-test-scripts.txt
if [ ! -s perf-test-scripts.txt ]; then
echo "automated-performance-metrics/scripts/Api/users-index.js" > perf-test-scripts.txt
fi
echo "Discovered k6 tests:"
cat perf-test-scripts.txt
- name: Deploy update (PR build) and verify
id: update
env:
IMAGE_TAG: ${{ env.IMAGE_TAG }}
HELM_REPO: ${{ secrets.HELM_REPO }}
HELM_USERNAME: ${{ secrets.HELM_USERNAME }}
HELM_PASSWORD: ${{ secrets.HELM_PASSWORD }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANALYTICS_AWS_ACCESS_KEY: ${{ secrets.ANALYTICS_AWS_ACCESS_KEY }}
ANALYTICS_AWS_SECRET_KEY: ${{ secrets.ANALYTICS_AWS_SECRET_KEY }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
REGISTRY_HOST: ${{ secrets.REGISTRY_HOST }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
TWILIO_SID: ${{ secrets.TWILIO_SID }}
TWILIO_TOKEN: ${{ secrets.TWILIO_TOKEN }}
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-update-${RANDOM_SUFFIX}"
export RELEASE_NAME APP_VERSION="${{ env.IMAGE_TAG }}" INSTANCE
export versionHelm="${{ env.versionHelm }}"
.github/scripts/deploy-perf-instance.sh
BASE_URL="https://${RELEASE_NAME}.engk8s.processmaker.net"
echo "Checking $BASE_URL/login ..."
for i in $(seq 1 30); do
CODE=$(curl -s -o /dev/null -w "%{http_code}" -k "$BASE_URL/login" || echo 000)
if [ "$CODE" = "200" ]; then
echo "Update /login returned 200."
break
fi
echo "Attempt $i: got $CODE"
sleep 15
done
CODE=$(curl -s -o /dev/null -w "%{http_code}" -k "$BASE_URL/login")
if [ "$CODE" != "200" ]; then
echo "Update /login did not return 200 (got $CODE). Continuing to cleanup."
fi
- name: Wait for TLS certificate (update)
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-update-${RANDOM_SUFFIX}"
APP_NS="${RELEASE_NAME}-ns-pm4"
CERT_NAME="${RELEASE_NAME}-tls-secret"
echo "Waiting for cert-manager to issue TLS certificate for ${RELEASE_NAME}..."
READY=""
for i in $(seq 1 5); do
READY=$(kubectl get certificate -n "$APP_NS" -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | tr ' ' '\n' | head -1)
if [ "$READY" = "True" ]; then
echo "TLS certificate Ready."
break
fi
echo "Waiting for TLS certificate... ($i/5)"
sleep 10
done
if [ "$READY" != "True" ]; then
echo "Certificate not ready after 5 loops; deleting to trigger regeneration..."
kubectl delete certificate -n "$APP_NS" "$CERT_NAME" --ignore-not-found=true || true
sleep 5
echo "Waiting up to 5 more loops for renewed certificate..."
for i in $(seq 1 5); do
READY=$(kubectl get certificate -n "$APP_NS" -o jsonpath='{.items[*].status.conditions[?(@.type=="Ready")].status}' 2>/dev/null | tr ' ' '\n' | head -1)
if [ "$READY" = "True" ]; then
echo "TLS certificate Ready (after retry)."
break
fi
echo "Waiting for TLS certificate (retry)... ($i/5)"
sleep 10
done
fi
if [ "$READY" != "True" ]; then
echo "TLS certificate did not become Ready; k6 may see certificate errors."
fi
- name: Generate admin access token (update instance)
id: token
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-update-${RANDOM_SUFFIX}"
APP_NS="${RELEASE_NAME}-ns-pm4"
POD=""
attempts=0
while [ -z "$POD" ]; do
POD=$(kubectl get pods -n "$APP_NS" -l service=web --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
[ -n "$POD" ] && break
attempts=$((attempts + 1))
[ $attempts -ge 5 ] && break
sleep 10
done
echo "Using web pod: ${POD:-none}"
TOKEN=$(kubectl exec -n "$APP_NS" "$POD" -- sudo -u nginx php /opt/processmaker/artisan processmaker:generate-access-token admin 2>/dev/null | tr -d '\r\n') || true
if [ -z "$TOKEN" ]; then
echo "Could not generate token (pod may still be starting)"
echo "token=" >> $GITHUB_OUTPUT
exit 0
fi
echo "token=${TOKEN}" >> $GITHUB_OUTPUT
- name: Run k6 on update
id: k6_update
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-update-${RANDOM_SUFFIX}"
APP_URL="https://${RELEASE_NAME}.engk8s.processmaker.net"
TOKEN="${{ steps.token.outputs.token }}"
if [ -z "$TOKEN" ]; then
echo "Skipping update k6: no token"
exit 0
fi
export BASE_PATH="${APP_URL}"
export BEARER_TOKEN="Bearer ${TOKEN}"
while IFS= read -r script; do
[ -z "$script" ] && continue
test_id=$(echo "$script" | sed 's|automated-performance-metrics/scripts/||;s|\.js$||;s|/|-|g')
echo "Running update k6: $script (id: $test_id)"
k6 run "$script" 2>&1 | tee "update-${test_id}-k6-results.txt" || true
done < perf-test-scripts.txt
- name: Upload update results
uses: actions/upload-artifact@v4
with:
name: update-results
path: |
perf-test-scripts.txt
update-*-k6-results.txt
- name: Cleanup update
if: always()
run: |
INSTANCE="${{ steps.perf_prep.outputs.instance }}"
RANDOM_SUFFIX="${{ env.RANDOM_SUFFIX }}"
RELEASE_NAME="ci-${INSTANCE}-perf-update-${RANDOM_SUFFIX}"
APP_NS="${RELEASE_NAME}-ns-pm4"
helm uninstall "${RELEASE_NAME}" --namespace default 2>/dev/null || true
kubectl delete namespace "${APP_NS}" --timeout=120s --ignore-not-found=true || true
- name: Delete performance node group (update)
if: always()
run: |
export INSTANCE="${{ steps.perf_prep.outputs.instance }}"
export PERF_SUFFIX=update
chmod +x .github/scripts/delete-perf-nodegroup.sh
.github/scripts/delete-perf-nodegroup.sh
perfComment:
name: perf-comment
if: github.event.action != 'closed' && inputs.delete == '' && contains(github.event.pull_request.body, 'ci:performance-tests')
needs: [perfBaseline, perfUpdate]
runs-on: ubuntu-latest
steps:
- name: Download baseline results
uses: actions/download-artifact@v4
with:
name: baseline-results
- name: Download update results
uses: actions/download-artifact@v4
with:
name: update-results
- name: Prepare performance comment body
run: |
cp baseline-results/perf-test-scripts.txt . 2>/dev/null || cp update-results/perf-test-scripts.txt .
cp baseline-results/baseline-*-k6-results.txt . 2>/dev/null || true
cp update-results/update-*-k6-results.txt . 2>/dev/null || true
extract_summary() {
sed -n '/█ TOTAL RESULTS\|TOTAL RESULTS/,/^running (0m/p' "$1" 2>/dev/null | sed '/^running (0m/d' || true
}
get_metric() { grep -E "^\s+$1\.+" "$2" 2>/dev/null | head -1 | awk '{ for(i=2;i<=NF;i++) printf "%s%s", $i, (i<NF?" ":""); print "" }' | xargs; }
get_http_duration_avg() { grep -E "^\s+http_req_duration" "$1" 2>/dev/null | head -1 | sed -n 's/.*avg=\([0-9.]*\)ms.*/\1/p'; }
get_http_failed_pct() { grep -E "^\s+http_req_failed" "$1" 2>/dev/null | head -1 | sed -n 's/.*: \([0-9.]*\)%.*/\1/p'; }
BASE_BRANCH="${{ github.event.pull_request.base.ref || github.event.repository.default_branch || 'develop' }}"
{
echo '✅ **Performance tests** completed.'
echo ''
echo "**Baseline:** \`${BASE_BRANCH}\` | **Update:** PR build (\`${{ env.IMAGE_TAG }}\`)"
echo ''
} > perf-comment.md
while IFS= read -r script; do
[ -z "$script" ] && continue
test_id=$(echo "$script" | sed 's|automated-performance-metrics/scripts/||;s|\.js$||;s|/|-|g')
BASE_FILE="baseline-${test_id}-k6-results.txt"
UPD_FILE="update-${test_id}-k6-results.txt"
base_iter="$(get_metric 'iterations' "$BASE_FILE" 2>/dev/null || echo '—')"
upd_iter="$(get_metric 'iterations' "$UPD_FILE" 2>/dev/null || echo '—')"
base_dur="$(get_http_duration_avg "$BASE_FILE" 2>/dev/null || echo '—')"
upd_dur="$(get_http_duration_avg "$UPD_FILE" 2>/dev/null || echo '—')"
base_fail="$(get_http_failed_pct "$BASE_FILE" 2>/dev/null || echo '—')"
upd_fail="$(get_http_failed_pct "$UPD_FILE" 2>/dev/null || echo '—')"
{
echo "### Comparison (\`${test_id}\`)"
echo ''
echo '| Metric | Baseline | Update |'
echo '|--------|----------|--------|'
echo "| iterations | \`${base_iter}\` | \`${upd_iter}\` |"
echo "| http_req_duration (avg ms) | \`${base_dur}\` | \`${upd_dur}\` |"
echo "| http_req_failed (%) | \`${base_fail}\` | \`${upd_fail}\` |"
echo ''
echo "### k6 summary — baseline (\`${test_id}\`)"
echo '```'
extract_summary "$BASE_FILE" | cat -s 2>/dev/null || echo '(no summary)'
echo '```'
echo ''
echo "### k6 summary — update (\`${test_id}\`)"
echo '```'
extract_summary "$UPD_FILE" | cat -s 2>/dev/null || echo '(no summary)'
echo '```'
echo ''
} >> perf-comment.md
done < perf-test-scripts.txt
- name: Comment PR (performance results)
if: success() && github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.existsSync('perf-comment.md')
? fs.readFileSync('perf-comment.md', 'utf8')
: '✅ **Performance tests** completed (k6 results not captured).';
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body
});
runPhpUnit:
name: run-phpunit
if: github.event.action != 'closed' && inputs.delete == ''
needs: imageEKS
runs-on: ${{ vars.RUNNER }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: processmaker/.github
- name: Common
uses: ./.github/actions/common
with:
token: ${{ secrets.GIT_TOKEN }}
- name: Export Params
run: |
echo "IMAGE=${{ secrets.REGISTRY_HOST }}/processmaker/enterprise:${{env.IMAGE_TAG}}" >> $GITHUB_ENV
# - uses: actions/checkout@v2
# with:
# fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
# - name: Clone repo K8S
# run: |
# echo "IMAGE: ${{ env.IMAGE }}"
# git clone --depth 1 -b "$K8S_BRANCH" "https://$GIT_TOKEN@github.com/ProcessMaker/pm4-k8s-distribution.git" pm4-k8s-distribution
- name: Login to Harbor
uses: docker/login-action@v2
with:
registry: ${{ secrets.REGISTRY_HOST }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: PHPUnits
run: |
cd pm4-k8s-distribution/images/pm4-tools
docker pull $IMAGE
docker compose down -v
docker compose build phpunit
docker compose run phpunit
CONTAINER_ID=$(sudo docker ps -a | grep phpunit | awk '{print $1}')
echo "Copying coverage report from PHP Unit Container: $CONTAINER_ID"
if sudo docker exec $CONTAINER_ID test -f /opt/processmaker/coverage.xml; then
sudo docker cp $CONTAINER_ID:/opt/processmaker/coverage.xml coverage.xml
echo "COVERAGE_EXISTS=true" >> $GITHUB_ENV
else
echo "coverage.xml not found in container, skipping coverage archive and SonarQube"
echo "COVERAGE_EXISTS=false" >> $GITHUB_ENV
fi
- name: Archive code coverage
if: env.COVERAGE_EXISTS == 'true'
uses: actions/upload-artifact@v4
with:
name: code-coverage
path: ./pm4-k8s-distribution/images/pm4-tools/coverage.xml
- name: SonarQube Coverage Report
if: env.COVERAGE_EXISTS == 'true'
uses: sonarsource/sonarqube-scan-action@master
env:
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
with:
args: >
-Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }}
-Dsonar.sources=.
-Dsonar.tests=.
-Dsonar.test.inclusions=**/*Test.php
-Dsonar.php.coverage.reportPaths=./pm4-k8s-distribution/images/pm4-tools/coverage.xml