feat: finalize projectwork CI jobs, docs and test integration
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
- defaults:
|
||||
name: global
|
||||
project_folder: /workspace/hw8
|
||||
project_folder: /workspace/projectwork
|
||||
test_image_tag: "1.0.0"
|
||||
build_keep: 40
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
- property:
|
||||
name: hw8-build-policy
|
||||
name: projectwork-build-policy
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 40
|
||||
|
||||
@@ -23,6 +23,9 @@ pipeline {
|
||||
set -eux
|
||||
docker version
|
||||
curl -fsS http://registry:5000/v2/_catalog
|
||||
curl -fsS http://host.docker.internal:4444/status
|
||||
docker pull selenoid/vnc:chrome_128.0
|
||||
docker pull selenoid/video-recorder:latest-release || docker pull selenoid/video-recorder:latest
|
||||
docker pull localhost:5005/otus/test-selenium:1.0.0
|
||||
docker pull localhost:5005/otus/test-api:1.0.0
|
||||
docker pull localhost:5005/otus/test-mobile:1.0.0
|
||||
@@ -33,8 +36,8 @@ pipeline {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
HW8_ROOT_PATH="${HW8_ROOT:-/workspace/hw8}"
|
||||
test -f "${HW8_ROOT_PATH}/config/jobs/global.yaml"
|
||||
PROJECT_ROOT_PATH="${OTUS_WORKSPACE_ROOT:-/workspace/projectwork}"
|
||||
test -f "${PROJECT_ROOT_PATH}/config/jobs/global.yaml"
|
||||
'''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ pipeline {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./sources
|
||||
git clone "${QA_REPO_URL}" ./sources
|
||||
git -C ./sources checkout "${QA_REPO_REF}"
|
||||
@@ -40,7 +41,7 @@ pipeline {
|
||||
echo "timestamp_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
} > ./artifacts/run-info.txt
|
||||
|
||||
CID="$(docker create localhost:5005/otus/test-api:1.0.0 bash -lc "set -e; cd /workspace; mvn -f citrus-tests/pom.xml test")"
|
||||
CID="$(docker create localhost:5005/otus/test-api:1.0.0 bash -lc "set -e; cd /workspace; mvn -f citrus-tests/pom.xml -Dallure.results.directory=target/allure-results test")"
|
||||
cleanup_container() {
|
||||
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
@@ -50,6 +51,53 @@ pipeline {
|
||||
docker start -a "${CID}"
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/workspace/citrus-tests/target" "./artifacts/citrus-target" || true
|
||||
mkdir -p ./artifacts/citrus-target/allure-results
|
||||
if ! ls ./artifacts/citrus-target/allure-results/* >/dev/null 2>&1; then
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
STATUS="passed"
|
||||
if [ "${TEST_RC}" -ne 0 ]; then
|
||||
STATUS="failed"
|
||||
fi
|
||||
cat > "./artifacts/citrus-target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "api-citrus-summary-${BUILD_NUMBER}",
|
||||
"name": "API Citrus pipeline summary",
|
||||
"fullName": "pipeline.ApiCitrusSummary",
|
||||
"status": "${STATUS}",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "ApiCitrusSummary"},
|
||||
{"name": "testMethod", "value": "run"}
|
||||
],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "repo.url=${QA_REPO_URL}"
|
||||
echo "repo.ref=${QA_REPO_REF}"
|
||||
echo "repo.sha=${GIT_SHA}"
|
||||
} > ./artifacts/citrus-target/allure-results/environment.properties
|
||||
cat > ./artifacts/citrus-target/allure-results/executor.json <<EOF
|
||||
{
|
||||
"name": "Jenkins",
|
||||
"type": "jenkins",
|
||||
"url": "${JENKINS_URL}",
|
||||
"buildName": "${JOB_NAME} #${BUILD_NUMBER}",
|
||||
"buildUrl": "${BUILD_URL}",
|
||||
"reportUrl": "${BUILD_URL}allure",
|
||||
"buildOrder": ${BUILD_NUMBER}
|
||||
}
|
||||
EOF
|
||||
trap - EXIT INT TERM
|
||||
docker rm -f "${CID}" || true
|
||||
exit "${TEST_RC}"
|
||||
@@ -59,6 +107,13 @@ pipeline {
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'artifacts/citrus-target/allure-results']]
|
||||
} catch (Exception ex) {
|
||||
echo "Allure publisher unavailable: ${ex.message}"
|
||||
}
|
||||
}
|
||||
junit allowEmptyResults: true, testResults: 'artifacts/citrus-target/surefire-reports/*.xml'
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/run-info.txt,artifacts/citrus-target/**'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
pipeline {
|
||||
agent { label 'maven' }
|
||||
options {
|
||||
timestamps()
|
||||
ansiColor('xterm')
|
||||
}
|
||||
stages {
|
||||
stage('Prepare Metadata') {
|
||||
steps {
|
||||
script {
|
||||
wrap([$class: 'BuildUser']) {
|
||||
env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system'
|
||||
env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system'
|
||||
}
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} contract-api"
|
||||
currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; scope=users-contract"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run Contract API Tests') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
PROJECT_ROOT_PATH="${OTUS_WORKSPACE_ROOT:-/workspace/projectwork}"
|
||||
WIREMOCK_DIR="${PROJECT_ROOT_PATH}/config/wiremock"
|
||||
|
||||
test -d "${WIREMOCK_DIR}"
|
||||
test -f "${PROJECT_ROOT_PATH}/contracts-tests/pom.xml"
|
||||
|
||||
rm -rf ./artifacts
|
||||
mkdir -p ./artifacts
|
||||
|
||||
WM_NAME="wiremock-contract-${BUILD_NUMBER}"
|
||||
WM_CID="$(docker run -d --rm --name "${WM_NAME}" -p 18080:8080 wiremock/wiremock:3.9.1)"
|
||||
WM_IP="$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${WM_CID}")"
|
||||
test -n "${WM_IP}"
|
||||
docker exec "${WM_CID}" mkdir -p /home/wiremock/mappings
|
||||
tar -C "${WIREMOCK_DIR}" -cf - mappings | docker cp - "${WM_CID}:/home/wiremock"
|
||||
docker restart "${WM_CID}" >/dev/null
|
||||
|
||||
cleanup_all() {
|
||||
docker rm -f "${WM_CID}" >/dev/null 2>&1 || true
|
||||
docker rm -f "${CID:-}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup_all EXIT INT TERM
|
||||
|
||||
for _ in $(seq 1 30); do
|
||||
if curl -fsS "http://${WM_IP}:8080/__admin/health" >/dev/null; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
curl -fsS "http://${WM_IP}:8080/__admin/health" >/dev/null
|
||||
|
||||
CID="$(docker create \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
localhost:5005/otus/test-api:1.0.0 \
|
||||
bash -lc "set -e; cd /workspace; mvn -f contracts-tests/pom.xml -Dallure.results.directory=target/allure-results -DbaseUrl=http://${WM_IP}:8080 test")"
|
||||
tar -C "${PROJECT_ROOT_PATH}" -cf - contracts-tests | docker cp - "${CID}:/workspace"
|
||||
set +e
|
||||
docker start -a "${CID}"
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/workspace/contracts-tests/target" "./artifacts" || true
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
if ! ls ./artifacts/target/allure-results/* >/dev/null 2>&1; then
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
STATUS="passed"
|
||||
if [ "${TEST_RC}" -ne 0 ]; then
|
||||
STATUS="failed"
|
||||
fi
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "api-contract-summary-${BUILD_NUMBER}",
|
||||
"name": "API contract pipeline summary",
|
||||
"fullName": "pipeline.ApiContractSummary",
|
||||
"status": "${STATUS}",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "ApiContractSummary"},
|
||||
{"name": "testMethod", "value": "run"}
|
||||
],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "scope=users-contract"
|
||||
echo "wiremock.url=http://${WM_IP}:8080"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
cat > ./artifacts/target/allure-results/executor.json <<EOF
|
||||
{
|
||||
"name": "Jenkins",
|
||||
"type": "jenkins",
|
||||
"url": "${JENKINS_URL}",
|
||||
"buildName": "${JOB_NAME} #${BUILD_NUMBER}",
|
||||
"buildUrl": "${BUILD_URL}",
|
||||
"reportUrl": "${BUILD_URL}allure",
|
||||
"buildOrder": ${BUILD_NUMBER}
|
||||
}
|
||||
EOF
|
||||
set -e
|
||||
exit "${TEST_RC}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'artifacts/target/allure-results']]
|
||||
} catch (Exception ex) {
|
||||
echo "Allure publisher unavailable: ${ex.message}"
|
||||
}
|
||||
}
|
||||
junit allowEmptyResults: true, testResults: 'artifacts/target/surefire-reports/*.xml'
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/target/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
pipeline {
|
||||
agent { label 'maven' }
|
||||
options {
|
||||
timestamps()
|
||||
ansiColor('xterm')
|
||||
}
|
||||
stages {
|
||||
stage('Prepare Metadata') {
|
||||
steps {
|
||||
script {
|
||||
wrap([$class: 'BuildUser']) {
|
||||
env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system'
|
||||
env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system'
|
||||
}
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} api-rest"
|
||||
currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; repo=${params.API_REST_REPO_URL}; ref=${params.API_REST_REPO_REF}"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run API REST Tests') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./sources ./artifacts
|
||||
mkdir -p ./artifacts
|
||||
|
||||
git clone "${API_REST_REPO_URL}" ./sources
|
||||
TARGET_REF="${API_REST_REPO_REF}"
|
||||
if git -C ./sources show-ref --verify --quiet "refs/remotes/origin/${TARGET_REF}"; then
|
||||
git -C ./sources checkout -B "${TARGET_REF}" "origin/${TARGET_REF}"
|
||||
else
|
||||
DEFAULT_REF="$(git -C ./sources symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')"
|
||||
echo "Requested ref '${TARGET_REF}' not found, fallback to default '${DEFAULT_REF}'"
|
||||
TARGET_REF="${DEFAULT_REF}"
|
||||
git -C ./sources checkout -B "${TARGET_REF}" "origin/${TARGET_REF}"
|
||||
fi
|
||||
GIT_SHA="$(git -C ./sources rev-parse --short HEAD)"
|
||||
|
||||
CID="$(docker create localhost:5005/otus/test-api:1.0.0 bash -lc "set -e; cd /workspace; mvn -Dmaven.compiler.release=${API_REST_JAVA_RELEASE} -Dmaven.compiler.source=${API_REST_JAVA_RELEASE} -Dmaven.compiler.target=${API_REST_JAVA_RELEASE} -Dallure.results.directory=target/allure-results ${API_REST_MAVEN_GOAL}")"
|
||||
cleanup_container() {
|
||||
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup_container EXIT INT TERM
|
||||
tar -C ./sources -cf - . | docker cp - "${CID}:/workspace"
|
||||
set +e
|
||||
docker start -a "${CID}"
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/workspace/target" "./artifacts" || true
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
if ! ls ./artifacts/target/allure-results/* >/dev/null 2>&1; then
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
STATUS="passed"
|
||||
if [ "${TEST_RC}" -ne 0 ]; then
|
||||
STATUS="failed"
|
||||
fi
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "api-rest-summary-${BUILD_NUMBER}",
|
||||
"name": "API REST pipeline summary",
|
||||
"fullName": "pipeline.ApiRestSummary",
|
||||
"status": "${STATUS}",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "ApiRestSummary"},
|
||||
{"name": "testMethod", "value": "run"}
|
||||
],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "repo=${API_REST_REPO_URL}"
|
||||
echo "ref=${TARGET_REF}"
|
||||
echo "sha=${GIT_SHA}"
|
||||
echo "maven_goal=${API_REST_MAVEN_GOAL}"
|
||||
echo "java_release=${API_REST_JAVA_RELEASE}"
|
||||
} > ./artifacts/run-info.txt
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "repo.url=${API_REST_REPO_URL}"
|
||||
echo "repo.ref=${TARGET_REF}"
|
||||
echo "repo.sha=${GIT_SHA}"
|
||||
echo "maven.goal=${API_REST_MAVEN_GOAL}"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
cat > ./artifacts/target/allure-results/executor.json <<EOF
|
||||
{
|
||||
"name": "Jenkins",
|
||||
"type": "jenkins",
|
||||
"url": "${JENKINS_URL}",
|
||||
"buildName": "${JOB_NAME} #${BUILD_NUMBER}",
|
||||
"buildUrl": "${BUILD_URL}",
|
||||
"reportUrl": "${BUILD_URL}allure",
|
||||
"buildOrder": ${BUILD_NUMBER}
|
||||
}
|
||||
EOF
|
||||
trap - EXIT INT TERM
|
||||
docker rm -f "${CID}" || true
|
||||
exit "${TEST_RC}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'artifacts/target/allure-results']]
|
||||
} catch (Exception ex) {
|
||||
echo "Allure publisher unavailable: ${ex.message}"
|
||||
}
|
||||
}
|
||||
junit allowEmptyResults: true, testResults: 'artifacts/target/surefire-reports/*.xml'
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/run-info.txt,artifacts/target/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
pipeline {
|
||||
agent { label 'maven' }
|
||||
options {
|
||||
timestamps()
|
||||
ansiColor('xterm')
|
||||
}
|
||||
stages {
|
||||
stage('Prepare Metadata') {
|
||||
steps {
|
||||
script {
|
||||
wrap([$class: 'BuildUser']) {
|
||||
env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system'
|
||||
env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system'
|
||||
}
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} maven-extra"
|
||||
currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; repo=${params.EXTRA_REPO_URL}; ref=${params.EXTRA_REPO_REF}"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run Maven Extra Tests') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./sources ./artifacts
|
||||
mkdir -p ./artifacts
|
||||
|
||||
if [ "${EXTRA_REPO_URL#/}" != "${EXTRA_REPO_URL}" ] && [ ! -d "${EXTRA_REPO_URL}" ]; then
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "maven-extra-skipped-${BUILD_NUMBER}",
|
||||
"name": "Maven extra pipeline summary",
|
||||
"fullName": "pipeline.MavenExtraSummary",
|
||||
"status": "skipped",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "MavenExtraSummary"},
|
||||
{"name": "testMethod", "value": "run"}
|
||||
],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "repo.url=${EXTRA_REPO_URL}"
|
||||
echo "repo.ref=${EXTRA_REPO_REF}"
|
||||
echo "status=SKIPPED_MISSING_REPO"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
cat > ./artifacts/target/allure-results/executor.json <<EOF
|
||||
{
|
||||
"name": "Jenkins",
|
||||
"type": "jenkins",
|
||||
"url": "${JENKINS_URL}",
|
||||
"buildName": "${JOB_NAME} #${BUILD_NUMBER}",
|
||||
"buildUrl": "${BUILD_URL}",
|
||||
"reportUrl": "${BUILD_URL}allure",
|
||||
"buildOrder": ${BUILD_NUMBER}
|
||||
}
|
||||
EOF
|
||||
{
|
||||
echo "repo=${EXTRA_REPO_URL}"
|
||||
echo "ref=${EXTRA_REPO_REF}"
|
||||
echo "status=SKIPPED_MISSING_REPO"
|
||||
} > ./artifacts/run-info.txt
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git clone "${EXTRA_REPO_URL}" ./sources
|
||||
git -C ./sources checkout "${EXTRA_REPO_REF}"
|
||||
GIT_SHA="$(git -C ./sources rev-parse --short HEAD)"
|
||||
|
||||
CID="$(docker create localhost:5005/otus/test-api:1.0.0 bash -lc "set -e; cd /workspace; mvn -Dallure.results.directory=target/allure-results ${EXTRA_MAVEN_GOAL}")"
|
||||
cleanup_container() {
|
||||
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup_container EXIT INT TERM
|
||||
tar -C ./sources -cf - . | docker cp - "${CID}:/workspace"
|
||||
set +e
|
||||
docker start -a "${CID}"
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/workspace/target" "./artifacts" || true
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
if ! ls ./artifacts/target/allure-results/* >/dev/null 2>&1; then
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
STATUS="passed"
|
||||
if [ "${TEST_RC}" -ne 0 ]; then
|
||||
STATUS="failed"
|
||||
fi
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "maven-extra-summary-${BUILD_NUMBER}",
|
||||
"name": "Maven extra pipeline summary",
|
||||
"fullName": "pipeline.MavenExtraSummary",
|
||||
"status": "${STATUS}",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "MavenExtraSummary"},
|
||||
{"name": "testMethod", "value": "run"}
|
||||
],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "repo=${EXTRA_REPO_URL}"
|
||||
echo "ref=${EXTRA_REPO_REF}"
|
||||
echo "sha=${GIT_SHA}"
|
||||
echo "maven_goal=${EXTRA_MAVEN_GOAL}"
|
||||
} > ./artifacts/run-info.txt
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "repo.url=${EXTRA_REPO_URL}"
|
||||
echo "repo.ref=${EXTRA_REPO_REF}"
|
||||
echo "repo.sha=${GIT_SHA}"
|
||||
echo "maven.goal=${EXTRA_MAVEN_GOAL}"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
cat > ./artifacts/target/allure-results/executor.json <<EOF
|
||||
{
|
||||
"name": "Jenkins",
|
||||
"type": "jenkins",
|
||||
"url": "${JENKINS_URL}",
|
||||
"buildName": "${JOB_NAME} #${BUILD_NUMBER}",
|
||||
"buildUrl": "${BUILD_URL}",
|
||||
"reportUrl": "${BUILD_URL}allure",
|
||||
"buildOrder": ${BUILD_NUMBER}
|
||||
}
|
||||
EOF
|
||||
trap - EXIT INT TERM
|
||||
docker rm -f "${CID}" || true
|
||||
exit "${TEST_RC}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'artifacts/target/allure-results']]
|
||||
} catch (Exception ex) {
|
||||
echo "Allure publisher unavailable: ${ex.message}"
|
||||
}
|
||||
}
|
||||
junit allowEmptyResults: true, testResults: 'artifacts/target/surefire-reports/*.xml'
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/run-info.txt,artifacts/target/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
pipeline {
|
||||
agent { label 'maven' }
|
||||
options {
|
||||
timestamps()
|
||||
ansiColor('xterm')
|
||||
}
|
||||
stages {
|
||||
stage('Prepare Metadata') {
|
||||
steps {
|
||||
script {
|
||||
wrap([$class: 'BuildUser']) {
|
||||
env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system'
|
||||
env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system'
|
||||
}
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} playwright/${params.PLAYWRIGHT_BROWSER}"
|
||||
currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; repo=${params.PLAYWRIGHT_REPO_URL}; ref=${params.PLAYWRIGHT_REPO_REF}"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run Playwright Tests') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./sources ./artifacts
|
||||
mkdir -p ./artifacts
|
||||
|
||||
git clone "${PLAYWRIGHT_REPO_URL}" ./sources
|
||||
TARGET_REF="${PLAYWRIGHT_REPO_REF}"
|
||||
if git -C ./sources show-ref --verify --quiet "refs/remotes/origin/${TARGET_REF}"; then
|
||||
git -C ./sources checkout -B "${TARGET_REF}" "origin/${TARGET_REF}"
|
||||
else
|
||||
DEFAULT_REF="$(git -C ./sources symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')"
|
||||
echo "Requested ref '${TARGET_REF}' not found, fallback to default '${DEFAULT_REF}'"
|
||||
TARGET_REF="${DEFAULT_REF}"
|
||||
git -C ./sources checkout -B "${TARGET_REF}" "origin/${TARGET_REF}"
|
||||
fi
|
||||
GIT_SHA="$(git -C ./sources rev-parse --short HEAD)"
|
||||
TEST_IMAGE="${PLAYWRIGHT_DOCKER_IMAGE}"
|
||||
docker pull "${TEST_IMAGE}" || true
|
||||
|
||||
CID="$(docker create \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
"${TEST_IMAGE}" \
|
||||
bash -lc "set -e; cd /tmp; mvn -Dheadless=${PLAYWRIGHT_HEADLESS} -Dbrowser=${PLAYWRIGHT_BROWSER} -DbaseUrl=${PLAYWRIGHT_BASE_URL} test")"
|
||||
cleanup_container() {
|
||||
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup_container EXIT INT TERM
|
||||
tar -C ./sources -cf - . | docker cp - "${CID}:/tmp"
|
||||
set +e
|
||||
docker start -a "${CID}"
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/tmp/target" "./artifacts/target" || true
|
||||
docker cp "${CID}:/tmp/traces" "./artifacts/traces" || true
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
ATTACHMENT_COUNT=0
|
||||
ATTACHMENTS_MANIFEST="./artifacts/target/allure-results/.external-attachments.txt"
|
||||
: > "${ATTACHMENTS_MANIFEST}"
|
||||
ATTACH_FILES="$(find ./artifacts -type f \\( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.mp4' -o -iname '*.webm' -o -iname '*.zip' \\) 2>/dev/null || true)"
|
||||
if [ -n "${ATTACH_FILES}" ]; then
|
||||
while IFS= read -r attachment_file; do
|
||||
if [ -z "${attachment_file}" ] || [ ! -f "${attachment_file}" ]; then
|
||||
continue
|
||||
fi
|
||||
ATTACHMENT_COUNT=$((ATTACHMENT_COUNT + 1))
|
||||
ext="$(echo "${attachment_file}" | awk -F. '{print tolower($NF)}')"
|
||||
mime="application/octet-stream"
|
||||
case "${ext}" in
|
||||
png) mime="image/png" ;;
|
||||
jpg|jpeg) mime="image/jpeg" ;;
|
||||
mp4) mime="video/mp4" ;;
|
||||
webm) mime="video/webm" ;;
|
||||
zip) mime="application/zip" ;;
|
||||
esac
|
||||
source_name="external-attachment-${ATTACHMENT_COUNT}.${ext}"
|
||||
cp "${attachment_file}" "./artifacts/target/allure-results/${source_name}" || continue
|
||||
safe_name="$(basename "${attachment_file}" | sed 's/"/\\"/g')"
|
||||
printf '%s|%s|%s\n' "${safe_name}" "${mime}" "${source_name}" >> "${ATTACHMENTS_MANIFEST}"
|
||||
done <<EOF
|
||||
${ATTACH_FILES}
|
||||
EOF
|
||||
fi
|
||||
if [ "${ATTACHMENT_COUNT}" -gt 0 ]; then
|
||||
ATTACHMENTS_JSON="$(awk -F'|' 'BEGIN{first=1} {if(!first){printf(",")} first=0; gsub(/"/,"\\\"", $1); printf("{\\\"name\\\":\\\"%s\\\",\\\"type\\\":\\\"%s\\\",\\\"source\\\":\\\"%s\\\"}", $1, $2, $3)}' "${ATTACHMENTS_MANIFEST}")"
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "external-artifacts-${BUILD_NUMBER}",
|
||||
"name": "Collected artifacts",
|
||||
"fullName": "pipeline.CollectedArtifacts",
|
||||
"status": "passed",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline Artifacts"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "CollectedArtifacts"},
|
||||
{"name": "testMethod", "value": "attach"}
|
||||
],
|
||||
"attachments": [${ATTACHMENTS_JSON}],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "repo=${PLAYWRIGHT_REPO_URL}"
|
||||
echo "ref=${TARGET_REF}"
|
||||
echo "sha=${GIT_SHA}"
|
||||
echo "browser=${PLAYWRIGHT_BROWSER}"
|
||||
echo "headless=${PLAYWRIGHT_HEADLESS}"
|
||||
echo "base_url=${PLAYWRIGHT_BASE_URL}"
|
||||
echo "docker_image=${TEST_IMAGE}"
|
||||
} > ./artifacts/run-info.txt
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "repo.url=${PLAYWRIGHT_REPO_URL}"
|
||||
echo "repo.ref=${TARGET_REF}"
|
||||
echo "repo.sha=${GIT_SHA}"
|
||||
echo "browser=${PLAYWRIGHT_BROWSER}"
|
||||
echo "headless=${PLAYWRIGHT_HEADLESS}"
|
||||
echo "base.url=${PLAYWRIGHT_BASE_URL}"
|
||||
echo "docker.image=${TEST_IMAGE}"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
cat > ./artifacts/target/allure-results/executor.json <<EOF
|
||||
{
|
||||
"name": "Jenkins",
|
||||
"type": "jenkins",
|
||||
"url": "${JENKINS_URL}",
|
||||
"buildName": "${JOB_NAME} #${BUILD_NUMBER}",
|
||||
"buildUrl": "${BUILD_URL}",
|
||||
"reportUrl": "${BUILD_URL}allure",
|
||||
"buildOrder": ${BUILD_NUMBER}
|
||||
}
|
||||
EOF
|
||||
trap - EXIT INT TERM
|
||||
docker rm -f "${CID}" || true
|
||||
exit "${TEST_RC}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'artifacts/target/allure-results']]
|
||||
} catch (Exception ex) {
|
||||
echo "Allure publisher unavailable: ${ex.message}"
|
||||
}
|
||||
}
|
||||
junit allowEmptyResults: true, testResults: 'artifacts/target/surefire-reports/*.xml'
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/run-info.txt,artifacts/target/**,artifacts/traces/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ pipeline {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./project
|
||||
git clone "${MOBILE_REPO_URL}" ./project
|
||||
git -C ./project checkout "${MOBILE_REPO_REF}"
|
||||
@@ -68,8 +69,8 @@ pipeline {
|
||||
dir('project') {
|
||||
sh '''
|
||||
set -eux
|
||||
HW8_ROOT_PATH="${HW8_ROOT:-/workspace/hw8}"
|
||||
COMPOSE_FILE="${HW8_ROOT_PATH}/config/compose/mobile-ci.compose.yml"
|
||||
PROJECT_ROOT_PATH="${OTUS_WORKSPACE_ROOT:-/workspace/projectwork}"
|
||||
COMPOSE_FILE="${PROJECT_ROOT_PATH}/config/compose/mobile-ci.compose.yml"
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
compose_cmd() { PROJECT_DIR="$PWD" docker compose -f "${COMPOSE_FILE}" "$@"; }
|
||||
elif docker-compose version >/dev/null 2>&1; then
|
||||
@@ -96,6 +97,42 @@ pipeline {
|
||||
|
||||
compose_cmd down -v --remove-orphans || true
|
||||
compose_cmd up -d ${SERVICES}
|
||||
WIREMOCK_CID="$(compose_cmd ps -q wiremock)"
|
||||
if [ -z "${WIREMOCK_CID}" ]; then
|
||||
echo "Wiremock container is not found"
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "./wiremock/mappings/wishlist-apk.json" ] || [ ! -f "./wiremock/__files/wishlist.apk" ]; then
|
||||
echo "Missing wiremock APK assets in repository checkout: ./wiremock/mappings/wishlist-apk.json or ./wiremock/__files/wishlist.apk"
|
||||
exit 1
|
||||
fi
|
||||
docker exec "${WIREMOCK_CID}" sh -lc 'rm -rf /home/wiremock/mappings /home/wiremock/__files && mkdir -p /home/wiremock/mappings /home/wiremock/__files'
|
||||
tar -C ./wiremock -cf - . | docker exec -i "${WIREMOCK_CID}" sh -lc 'tar -C /home/wiremock -xf -'
|
||||
docker restart "${WIREMOCK_CID}"
|
||||
for i in $(seq 1 30); do
|
||||
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}starting{{end}}' "${WIREMOCK_CID}" || true)"
|
||||
if [ "${status}" = "healthy" ]; then
|
||||
break
|
||||
fi
|
||||
if [ "${i}" -eq 30 ]; then
|
||||
echo "Wiremock is not healthy after reload"
|
||||
docker logs "${WIREMOCK_CID}" || true
|
||||
exit 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if echo "${APP_URL}" | grep -Eq 'https?://wiremock:8080'; then
|
||||
APK_PATH="$(echo "${APP_URL}" | sed -E 's#https?://[^/]+##')"
|
||||
if [ -z "${APK_PATH}" ]; then
|
||||
APK_PATH="/wishlist.apk"
|
||||
fi
|
||||
if ! docker exec "${WIREMOCK_CID}" sh -lc "wget -qO- \"http://127.0.0.1:8080${APK_PATH}\" >/dev/null"; then
|
||||
echo "Wiremock can't serve APK path ${APK_PATH}"
|
||||
docker exec "${WIREMOCK_CID}" sh -lc 'ls -la /home/wiremock /home/wiremock/mappings /home/wiremock/__files' || true
|
||||
docker logs "${WIREMOCK_CID}" || true
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
EMULATORS=""
|
||||
SELECTED=0
|
||||
for service in android-emulator-1 android-emulator-2; do
|
||||
@@ -205,6 +242,39 @@ pipeline {
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
if echo "${APP_URL}" | grep -Eq 'https?://wiremock:8080'; then
|
||||
APK_PATH="$(echo "${APP_URL}" | sed -E 's#https?://[^/]+##')"
|
||||
if [ -z "${APK_PATH}" ]; then
|
||||
APK_PATH="/wishlist.apk"
|
||||
fi
|
||||
OLD_IFS="${IFS}"
|
||||
IFS=','
|
||||
for target in ${MOBILE_EMULATORS_VALUE}; do
|
||||
service_name="$(echo "${target}" | cut -d'|' -f1)"
|
||||
if [ -z "${service_name}" ]; then
|
||||
continue
|
||||
fi
|
||||
emu_cid="$(docker ps -q --filter "name=mobileci-${service_name}" | head -n1 || true)"
|
||||
if [ -z "${emu_cid}" ]; then
|
||||
echo "Emulator container not found for service ${service_name}"
|
||||
exit 1
|
||||
fi
|
||||
ready="false"
|
||||
for i in $(seq 1 30); do
|
||||
if docker exec "${emu_cid}" sh -lc "wget -qO- \"http://wiremock:8080${APK_PATH}\" >/dev/null"; then
|
||||
ready="true"
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [ "${ready}" != "true" ]; then
|
||||
echo "APK URL is not reachable from ${service_name}: ${APP_URL}"
|
||||
docker logs --tail 200 "${emu_cid}" || true
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
IFS="${OLD_IFS}"
|
||||
fi
|
||||
|
||||
run_single_class() {
|
||||
class_name="$1"
|
||||
@@ -410,8 +480,8 @@ EOF
|
||||
dir('project') {
|
||||
sh '''
|
||||
set +e
|
||||
HW8_ROOT_PATH="${HW8_ROOT:-/workspace/hw8}"
|
||||
COMPOSE_FILE="${HW8_ROOT_PATH}/config/compose/mobile-ci.compose.yml"
|
||||
PROJECT_ROOT_PATH="${OTUS_WORKSPACE_ROOT:-/workspace/projectwork}"
|
||||
COMPOSE_FILE="${PROJECT_ROOT_PATH}/config/compose/mobile-ci.compose.yml"
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
compose_cmd() { PROJECT_DIR="$PWD" docker compose -f "${COMPOSE_FILE}" "$@"; }
|
||||
elif docker-compose version >/dev/null 2>&1; then
|
||||
|
||||
@@ -8,12 +8,24 @@ pipeline {
|
||||
stage('Prepare Metadata') {
|
||||
steps {
|
||||
script {
|
||||
def bddRefsRaw = (params.BDD_REFS ?: '').trim()
|
||||
def bddRefs = bddRefsRaw
|
||||
? bddRefsRaw.split(',').collect { it.trim() }.findAll { it }.unique()
|
||||
: []
|
||||
if (bddRefs.isEmpty() && (params.BDD_REPO_REF ?: '').trim()) {
|
||||
bddRefs = [params.BDD_REPO_REF.trim()]
|
||||
}
|
||||
if (bddRefs.isEmpty()) {
|
||||
bddRefs = ['main', 'homework_2']
|
||||
}
|
||||
env.BDD_REFS_EFFECTIVE = bddRefs.join(',')
|
||||
|
||||
wrap([$class: 'BuildUser']) {
|
||||
env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system'
|
||||
env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system'
|
||||
}
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} runner"
|
||||
currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; qaRef=${params.QA_REPO_REF}; mobileRef=${params.MOBILE_REPO_REF}"
|
||||
currentBuild.description = "by=${env.RUN_TRIGGER_NAME}; bddRefs=${env.BDD_REFS_EFFECTIVE}; qaRef=${params.QA_REPO_REF}; mobileRef=${params.MOBILE_REPO_REF}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +33,33 @@ pipeline {
|
||||
steps {
|
||||
script {
|
||||
def fanout = [:]
|
||||
def bddRefs = env.BDD_REFS_EFFECTIVE
|
||||
.split(',')
|
||||
.collect { it.trim() }
|
||||
.findAll { it }
|
||||
if (bddRefs.isEmpty()) {
|
||||
bddRefs = ['main', 'homework_2']
|
||||
}
|
||||
|
||||
bddRefs.each { ref ->
|
||||
def refValue = ref
|
||||
def refKey = ref.replaceAll(/[^A-Za-z0-9_.-]/, '_')
|
||||
def runFile = "downstream-web-bdd-${refKey}.txt"
|
||||
fanout["web-bdd-${refKey}"] = {
|
||||
def run = build job: 'qa-web-bdd-tests',
|
||||
wait: true,
|
||||
propagate: false,
|
||||
parameters: [
|
||||
string(name: 'BDD_REPO_URL', value: params.BDD_REPO_URL),
|
||||
string(name: 'BDD_REPO_REF', value: refValue),
|
||||
string(name: 'BROWSER', value: params.BROWSER),
|
||||
string(name: 'BASE_URL', value: params.BASE_URL),
|
||||
string(name: 'SELENOID_URL', value: params.SELENOID_URL),
|
||||
string(name: 'HEADLESS', value: params.HEADLESS)
|
||||
]
|
||||
writeFile file: runFile, text: "${run.number}|${run.result}\n"
|
||||
}
|
||||
}
|
||||
|
||||
fanout['selenium'] = {
|
||||
def run = build job: 'qa-selenium-tests',
|
||||
@@ -73,6 +112,50 @@ pipeline {
|
||||
writeFile file: 'downstream-api.txt', text: "${run.number}|${run.result}\n"
|
||||
}
|
||||
|
||||
fanout['api-contract'] = {
|
||||
def run = build job: 'qa-api-contract-tests',
|
||||
wait: true,
|
||||
propagate: false
|
||||
writeFile file: 'downstream-api-contract.txt', text: "${run.number}|${run.result}\n"
|
||||
}
|
||||
|
||||
fanout['api-rest'] = {
|
||||
def run = build job: 'qa-api-rest-tests',
|
||||
wait: true,
|
||||
propagate: false,
|
||||
parameters: [
|
||||
string(name: 'API_REST_REPO_URL', value: params.API_REST_REPO_URL),
|
||||
string(name: 'API_REST_REPO_REF', value: params.API_REST_REPO_REF)
|
||||
]
|
||||
writeFile file: 'downstream-api-rest.txt', text: "${run.number}|${run.result}\n"
|
||||
}
|
||||
|
||||
fanout['extra'] = {
|
||||
def run = build job: 'qa-maven-extra-tests',
|
||||
wait: true,
|
||||
propagate: false,
|
||||
parameters: [
|
||||
string(name: 'EXTRA_REPO_URL', value: params.EXTRA_REPO_URL),
|
||||
string(name: 'EXTRA_REPO_REF', value: params.EXTRA_REPO_REF)
|
||||
]
|
||||
writeFile file: 'downstream-extra.txt', text: "${run.number}|${run.result}\n"
|
||||
}
|
||||
|
||||
fanout['playwright'] = {
|
||||
def run = build job: 'qa-playwright-tests',
|
||||
wait: true,
|
||||
propagate: false,
|
||||
parameters: [
|
||||
string(name: 'PLAYWRIGHT_REPO_URL', value: params.PLAYWRIGHT_REPO_URL),
|
||||
string(name: 'PLAYWRIGHT_REPO_REF', value: params.PLAYWRIGHT_REPO_REF),
|
||||
string(name: 'PLAYWRIGHT_BROWSER', value: params.PLAYWRIGHT_BROWSER),
|
||||
string(name: 'PLAYWRIGHT_HEADLESS', value: params.PLAYWRIGHT_HEADLESS),
|
||||
string(name: 'PLAYWRIGHT_BASE_URL', value: params.BASE_URL),
|
||||
string(name: 'PLAYWRIGHT_DOCKER_IMAGE', value: params.PLAYWRIGHT_DOCKER_IMAGE)
|
||||
]
|
||||
writeFile file: 'downstream-playwright.txt', text: "${run.number}|${run.result}\n"
|
||||
}
|
||||
|
||||
parallel fanout
|
||||
|
||||
def parseRun = { String fileName ->
|
||||
@@ -86,36 +169,97 @@ pipeline {
|
||||
]
|
||||
}
|
||||
|
||||
def bddRuns = [:]
|
||||
bddRefs.each { ref ->
|
||||
def refKey = ref.replaceAll(/[^A-Za-z0-9_.-]/, '_')
|
||||
bddRuns[ref] = parseRun("downstream-web-bdd-${refKey}.txt")
|
||||
}
|
||||
def seleniumRun = parseRun('downstream-selenium.txt')
|
||||
def mobileRun = parseRun('downstream-mobile.txt')
|
||||
def apiRun = parseRun('downstream-api.txt')
|
||||
def apiContractRun = parseRun('downstream-api-contract.txt')
|
||||
def apiRestRun = parseRun('downstream-api-rest.txt')
|
||||
def extraRun = parseRun('downstream-extra.txt')
|
||||
def playwrightRun = parseRun('downstream-playwright.txt')
|
||||
def lines = []
|
||||
lines << "job=${env.JOB_NAME}"
|
||||
lines << "build=${env.BUILD_NUMBER}"
|
||||
lines << "trigger_user=${env.RUN_TRIGGER_USER}"
|
||||
lines << "trigger_name=${env.RUN_TRIGGER_NAME}"
|
||||
lines << "bdd_repo_url=${params.BDD_REPO_URL}"
|
||||
lines << "bdd_refs=${bddRefs.join(',')}"
|
||||
lines << "qa_repo_url=${params.QA_REPO_URL}"
|
||||
lines << "qa_repo_ref=${params.QA_REPO_REF}"
|
||||
lines << "mobile_repo_url=${params.MOBILE_REPO_URL}"
|
||||
lines << "mobile_repo_ref=${params.MOBILE_REPO_REF}"
|
||||
lines << "api_rest_repo_url=${params.API_REST_REPO_URL}"
|
||||
lines << "api_rest_repo_ref=${params.API_REST_REPO_REF}"
|
||||
lines << "extra_repo_url=${params.EXTRA_REPO_URL}"
|
||||
lines << "extra_repo_ref=${params.EXTRA_REPO_REF}"
|
||||
lines << "playwright_repo_url=${params.PLAYWRIGHT_REPO_URL}"
|
||||
lines << "playwright_repo_ref=${params.PLAYWRIGHT_REPO_REF}"
|
||||
lines << "playwright_docker_image=${params.PLAYWRIGHT_DOCKER_IMAGE}"
|
||||
bddRefs.each { ref ->
|
||||
def refKey = ref.replaceAll(/[^A-Za-z0-9_.-]/, '_')
|
||||
def run = bddRuns[ref]
|
||||
lines << "web_bdd_${refKey}_build=${run.number}"
|
||||
lines << "web_bdd_${refKey}_result=${run.result}"
|
||||
lines << "web_bdd_${refKey}_link=/job/qa-web-bdd-tests/${run.number}/"
|
||||
lines << "web_bdd_${refKey}_link_abs=${env.JENKINS_URL}job/qa-web-bdd-tests/${run.number}/"
|
||||
}
|
||||
|
||||
def primaryBddRef = bddRefs[0]
|
||||
def primaryBddRun = bddRuns[primaryBddRef]
|
||||
lines << "web_bdd_build=${primaryBddRun.number}"
|
||||
lines << "web_bdd_result=${primaryBddRun.result}"
|
||||
lines << "selenium_build=${seleniumRun.number}"
|
||||
lines << "selenium_result=${seleniumRun.result}"
|
||||
lines << "mobile_build=${mobileRun.number}"
|
||||
lines << "mobile_result=${mobileRun.result}"
|
||||
lines << "api_build=${apiRun.number}"
|
||||
lines << "api_result=${apiRun.result}"
|
||||
lines << "api_contract_build=${apiContractRun.number}"
|
||||
lines << "api_contract_result=${apiContractRun.result}"
|
||||
lines << "api_rest_build=${apiRestRun.number}"
|
||||
lines << "api_rest_result=${apiRestRun.result}"
|
||||
lines << "extra_build=${extraRun.number}"
|
||||
lines << "extra_result=${extraRun.result}"
|
||||
lines << "playwright_build=${playwrightRun.number}"
|
||||
lines << "playwright_result=${playwrightRun.result}"
|
||||
lines << "web_bdd_link=/job/qa-web-bdd-tests/${primaryBddRun.number}/"
|
||||
lines << "selenium_link=/job/qa-selenium-tests/${seleniumRun.number}/"
|
||||
lines << "mobile_link=/job/qa-mobile-appium-tests/${mobileRun.number}/"
|
||||
lines << "api_link=/job/qa-api-citrus-tests/${apiRun.number}/"
|
||||
lines << "api_contract_link=/job/qa-api-contract-tests/${apiContractRun.number}/"
|
||||
lines << "api_rest_link=/job/qa-api-rest-tests/${apiRestRun.number}/"
|
||||
lines << "extra_link=/job/qa-maven-extra-tests/${extraRun.number}/"
|
||||
lines << "playwright_link=/job/qa-playwright-tests/${playwrightRun.number}/"
|
||||
lines << "web_bdd_link_abs=${env.JENKINS_URL}job/qa-web-bdd-tests/${primaryBddRun.number}/"
|
||||
lines << "selenium_link_abs=${env.JENKINS_URL}job/qa-selenium-tests/${seleniumRun.number}/"
|
||||
lines << "mobile_link_abs=${env.JENKINS_URL}job/qa-mobile-appium-tests/${mobileRun.number}/"
|
||||
lines << "api_link_abs=${env.JENKINS_URL}job/qa-api-citrus-tests/${apiRun.number}/"
|
||||
lines << "api_contract_link_abs=${env.JENKINS_URL}job/qa-api-contract-tests/${apiContractRun.number}/"
|
||||
lines << "api_rest_link_abs=${env.JENKINS_URL}job/qa-api-rest-tests/${apiRestRun.number}/"
|
||||
lines << "extra_link_abs=${env.JENKINS_URL}job/qa-maven-extra-tests/${extraRun.number}/"
|
||||
lines << "playwright_link_abs=${env.JENKINS_URL}job/qa-playwright-tests/${playwrightRun.number}/"
|
||||
writeFile file: 'runner-summary.txt', text: lines.join('\n') + '\n'
|
||||
archiveArtifacts allowEmptyArchive: false, artifacts: 'runner-summary.txt'
|
||||
|
||||
def failed = [seleniumRun, mobileRun, apiRun].any { it.result != 'SUCCESS' }
|
||||
if (failed) {
|
||||
error("One or more downstream QA jobs failed. See runner-summary.txt for build links and statuses.")
|
||||
def criticalRuns = [mobileRun, apiRun, apiContractRun, apiRestRun, extraRun]
|
||||
def optionalRuns = []
|
||||
optionalRuns << seleniumRun
|
||||
optionalRuns.addAll(bddRuns.values())
|
||||
optionalRuns << playwrightRun
|
||||
|
||||
def criticalFailed = criticalRuns.any { it.result != 'SUCCESS' }
|
||||
def optionalFailed = optionalRuns.any { it.result != 'SUCCESS' }
|
||||
|
||||
if (criticalFailed) {
|
||||
error("One or more critical downstream QA jobs failed. See runner-summary.txt for build links and statuses.")
|
||||
}
|
||||
if (optionalFailed) {
|
||||
currentBuild.result = 'UNSTABLE'
|
||||
echo "Optional downstream jobs have failures (web-bdd refs/playwright). See runner-summary.txt for details."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,15 @@ pipeline {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./sources
|
||||
git clone "${QA_REPO_URL}" ./sources
|
||||
git -C ./sources checkout "${QA_REPO_REF}"
|
||||
GIT_SHA="$(git -C ./sources rev-parse --short HEAD)"
|
||||
REMOTE_FACTORY="./sources/src/main/java/ru/kovbasa/driver/RemoteDriverFactory.java"
|
||||
if [ -f "${REMOTE_FACTORY}" ] && ! grep -q "selenoid.video.enabled" "${REMOTE_FACTORY}"; then
|
||||
sed -i 's/"enableVideo", false/"enableVideo", Boolean.parseBoolean(System.getProperty("selenoid.video.enabled", "false"))/' "${REMOTE_FACTORY}"
|
||||
fi
|
||||
if ! grep -q "<artifactId>allure-junit5</artifactId>" ./sources/pom.xml; then
|
||||
awk -v ver="${ALLURE_ADAPTER_VERSION}" '
|
||||
index($0, "</dependencies>") && !done {
|
||||
@@ -64,11 +69,34 @@ pipeline {
|
||||
if [ "${BROWSER}" = "chrome" ]; then
|
||||
EXTRA_ARGS="-Dchrome.binary=/usr/bin/chromium"
|
||||
fi
|
||||
VIDEO_ARGS=""
|
||||
SELENOID_BASE=""
|
||||
SELENOID_VIDEO_BEFORE="./artifacts/selenoid-videos-before.txt"
|
||||
SELENOID_VIDEO_AFTER="./artifacts/selenoid-videos-after.txt"
|
||||
SELENOID_VIDEO_NEW="./artifacts/selenoid-videos-new.txt"
|
||||
: > "${SELENOID_VIDEO_BEFORE}"
|
||||
: > "${SELENOID_VIDEO_AFTER}"
|
||||
: > "${SELENOID_VIDEO_NEW}"
|
||||
if [ "${EXECUTION_MODE}" = "selenoid" ]; then
|
||||
case "${BROWSER}" in
|
||||
chrome) docker pull selenoid/vnc:chrome_128.0 || true ;;
|
||||
firefox) docker pull selenoid/vnc:firefox_125.0 || true ;;
|
||||
esac
|
||||
docker pull selenoid/video-recorder:latest-release || docker pull selenoid/video-recorder:latest || true
|
||||
VIDEO_ARGS="-Dselenoid.video.enabled=true"
|
||||
if [ -n "${SELENOID_URL}" ]; then
|
||||
SELENOID_BASE="${SELENOID_URL%/wd/hub}"
|
||||
(curl -fsS "${SELENOID_BASE}/video/" || true) \
|
||||
| tr '"' '\n' \
|
||||
| grep -E '\\.mp4$' \
|
||||
| sort -u > "${SELENOID_VIDEO_BEFORE}" || true
|
||||
fi
|
||||
fi
|
||||
|
||||
CID="$(docker create \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
localhost:5005/otus/test-selenium:1.0.0 \
|
||||
bash -lc "set -e; cd /workspace; mvn -Dallure.version=${ALLURE_ADAPTER_VERSION} -Dexecution.mode=${EXECUTION_MODE} -Dbrowser=${BROWSER} -Dbrowser.version= -Dselenoid.url=${SELENOID_URL} -Dbase.url=${BASE_URL} -Dselenide.headless=${HEADLESS} -Dallure.results.directory=target/allure-results ${EXTRA_ARGS} test")"
|
||||
bash -lc "set -e; cd /workspace; mvn -Dallure.version=${ALLURE_ADAPTER_VERSION} -Dexecution.mode=${EXECUTION_MODE} -Dbrowser=${BROWSER} -Dbrowser.version= -Dselenoid.url=${SELENOID_URL} -Dbase.url=${BASE_URL} -Dselenide.headless=${HEADLESS} -Dallure.results.directory=target/allure-results ${VIDEO_ARGS} ${EXTRA_ARGS} test")"
|
||||
cleanup_container() {
|
||||
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
@@ -79,6 +107,83 @@ pipeline {
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/workspace/target" "./artifacts" || true
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
if [ "${EXECUTION_MODE}" = "selenoid" ] && [ -n "${SELENOID_URL}" ]; then
|
||||
(curl -fsS "${SELENOID_BASE}/video/" || true) \
|
||||
| tr '"' '\n' \
|
||||
| grep -E '\\.mp4$' \
|
||||
| sort -u > "${SELENOID_VIDEO_AFTER}" || true
|
||||
comm -13 "${SELENOID_VIDEO_BEFORE}" "${SELENOID_VIDEO_AFTER}" > "${SELENOID_VIDEO_NEW}" || true
|
||||
if [ ! -s "${SELENOID_VIDEO_NEW}" ]; then
|
||||
cp "${SELENOID_VIDEO_AFTER}" "${SELENOID_VIDEO_NEW}" || true
|
||||
fi
|
||||
while IFS= read -r video_file; do
|
||||
[ -n "${video_file}" ] || continue
|
||||
downloaded=0
|
||||
for i in $(seq 1 20); do
|
||||
if curl -fsS "${SELENOID_BASE}/video/${video_file}" -o "./artifacts/target/${video_file}"; then
|
||||
downloaded=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [ "${downloaded}" -ne 1 ]; then
|
||||
echo "Failed to fetch Selenoid video: ${video_file}"
|
||||
fi
|
||||
done < "${SELENOID_VIDEO_NEW}"
|
||||
fi
|
||||
|
||||
ATTACHMENT_COUNT=0
|
||||
ATTACHMENTS_MANIFEST="./artifacts/target/allure-results/.external-attachments.txt"
|
||||
: > "${ATTACHMENTS_MANIFEST}"
|
||||
ATTACH_FILES="$(find ./artifacts -type f \\( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.mp4' -o -iname '*.webm' \\) 2>/dev/null || true)"
|
||||
if [ -n "${ATTACH_FILES}" ]; then
|
||||
while IFS= read -r attachment_file; do
|
||||
if [ -z "${attachment_file}" ] || [ ! -f "${attachment_file}" ]; then
|
||||
continue
|
||||
fi
|
||||
ATTACHMENT_COUNT=$((ATTACHMENT_COUNT + 1))
|
||||
ext="$(echo "${attachment_file}" | awk -F. '{print tolower($NF)}')"
|
||||
mime="application/octet-stream"
|
||||
case "${ext}" in
|
||||
png) mime="image/png" ;;
|
||||
jpg|jpeg) mime="image/jpeg" ;;
|
||||
mp4) mime="video/mp4" ;;
|
||||
webm) mime="video/webm" ;;
|
||||
esac
|
||||
source_name="external-attachment-${ATTACHMENT_COUNT}.${ext}"
|
||||
cp "${attachment_file}" "./artifacts/target/allure-results/${source_name}" || continue
|
||||
safe_name="$(basename "${attachment_file}" | sed 's/"/\\"/g')"
|
||||
printf '%s|%s|%s\n' "${safe_name}" "${mime}" "${source_name}" >> "${ATTACHMENTS_MANIFEST}"
|
||||
done <<EOF
|
||||
${ATTACH_FILES}
|
||||
EOF
|
||||
fi
|
||||
if [ "${ATTACHMENT_COUNT}" -gt 0 ]; then
|
||||
ATTACHMENTS_JSON="$(awk -F'|' 'BEGIN{first=1} {if(!first){printf(",")} first=0; gsub(/"/,"\\\"", $1); printf("{\\\"name\\\":\\\"%s\\\",\\\"type\\\":\\\"%s\\\",\\\"source\\\":\\\"%s\\\"}", $1, $2, $3)}' "${ATTACHMENTS_MANIFEST}")"
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "external-artifacts-${BUILD_NUMBER}",
|
||||
"name": "Collected artifacts",
|
||||
"fullName": "pipeline.CollectedArtifacts",
|
||||
"status": "passed",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline Artifacts"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "CollectedArtifacts"},
|
||||
{"name": "testMethod", "value": "attach"}
|
||||
],
|
||||
"attachments": [${ATTACHMENTS_JSON}],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
@@ -91,6 +196,7 @@ pipeline {
|
||||
echo "headless=${HEADLESS}"
|
||||
echo "execution.mode=${EXECUTION_MODE}"
|
||||
echo "base.url=${BASE_URL}"
|
||||
echo "video.selection=all-new-from-selenoid"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
cat > ./artifacts/target/allure-results/executor.json <<EOF
|
||||
{
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
pipeline {
|
||||
agent { label 'maven' }
|
||||
options {
|
||||
timestamps()
|
||||
ansiColor('xterm')
|
||||
}
|
||||
stages {
|
||||
stage('Prepare Metadata') {
|
||||
steps {
|
||||
script {
|
||||
wrap([$class: 'BuildUser']) {
|
||||
env.RUN_TRIGGER_USER = env.BUILD_USER_ID ?: env.BUILD_USER ?: 'system'
|
||||
env.RUN_TRIGGER_NAME = env.BUILD_USER ?: env.BUILD_USER_ID ?: 'system'
|
||||
}
|
||||
currentBuild.displayName = "#${env.BUILD_NUMBER} bdd/${params.BDD_REPO_REF}"
|
||||
currentBuild.description =
|
||||
"by=${env.RUN_TRIGGER_NAME}; repo=${params.BDD_REPO_URL}; ref=${params.BDD_REPO_REF}; browser=${params.BROWSER}"
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Run WEB BDD Tests In Docker') {
|
||||
steps {
|
||||
sh '''
|
||||
set -eux
|
||||
git config --global --add safe.directory '*' || true
|
||||
rm -rf ./sources
|
||||
git clone "${BDD_REPO_URL}" ./sources
|
||||
TARGET_REF="${BDD_REPO_REF}"
|
||||
if git -C ./sources show-ref --verify --quiet "refs/remotes/origin/${TARGET_REF}"; then
|
||||
git -C ./sources checkout -B "${TARGET_REF}" "origin/${TARGET_REF}"
|
||||
else
|
||||
DEFAULT_REF="$(git -C ./sources symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')"
|
||||
echo "Requested ref '${TARGET_REF}' not found, fallback to default '${DEFAULT_REF}'"
|
||||
TARGET_REF="${DEFAULT_REF}"
|
||||
git -C ./sources checkout -B "${TARGET_REF}" "origin/${TARGET_REF}"
|
||||
fi
|
||||
GIT_SHA="$(git -C ./sources rev-parse --short HEAD)"
|
||||
CHROME_FACTORY="./sources/src/main/java/ru/kovbasa/driver/ChromeDriverFactory.java"
|
||||
if [ -f "${CHROME_FACTORY}" ]; then
|
||||
cat > "${CHROME_FACTORY}" <<'EOF'
|
||||
package ru.kovbasa.driver;
|
||||
|
||||
import org.openqa.selenium.WebDriver;
|
||||
import org.openqa.selenium.chrome.ChromeDriver;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ChromeDriverFactory implements DriverFactory {
|
||||
|
||||
@Override
|
||||
public WebDriver createDriver() {
|
||||
final ChromeOptions options = new ChromeOptions();
|
||||
options.addArguments("--start-maximized");
|
||||
options.addArguments("--disable-notifications");
|
||||
final boolean headless = Boolean.parseBoolean(System.getProperty("selenide.headless", "false"));
|
||||
if (headless) {
|
||||
options.addArguments("--headless=new");
|
||||
}
|
||||
options.addArguments("--no-sandbox");
|
||||
options.addArguments("--disable-dev-shm-usage");
|
||||
options.addArguments("--disable-gpu");
|
||||
options.addArguments("--window-size=1920,1080");
|
||||
options.addArguments("--remote-allow-origins=*");
|
||||
|
||||
final String remoteUrl = System.getProperty("selenoid.url", "").trim();
|
||||
if (!remoteUrl.isEmpty()) {
|
||||
try {
|
||||
options.setCapability("selenoid:options", Map.of(
|
||||
"name", "bdd-ui-tests",
|
||||
"enableVNC", true,
|
||||
"enableVideo", Boolean.parseBoolean(System.getProperty("selenoid.video.enabled", "false")),
|
||||
"env", List.of("TZ=UTC")
|
||||
));
|
||||
final URL url = URI.create(remoteUrl).toURL();
|
||||
return new RemoteWebDriver(url, options);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to create RemoteWebDriver for selenoid.url=" + remoteUrl, ex);
|
||||
}
|
||||
}
|
||||
return new ChromeDriver(options);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
rm -rf ./artifacts
|
||||
mkdir -p ./artifacts
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "trigger_user=${RUN_TRIGGER_USER}"
|
||||
echo "trigger_name=${RUN_TRIGGER_NAME}"
|
||||
echo "bdd_repo_url=${BDD_REPO_URL}"
|
||||
echo "bdd_repo_ref=${TARGET_REF}"
|
||||
echo "bdd_repo_sha=${GIT_SHA}"
|
||||
echo "browser=${BROWSER}"
|
||||
echo "headless=${HEADLESS}"
|
||||
echo "base_url=${BASE_URL}"
|
||||
echo "selenoid_url=${SELENOID_URL}"
|
||||
echo "timestamp_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
} > ./artifacts/run-info.txt
|
||||
|
||||
EXTRA_ARGS=""
|
||||
if [ "${BROWSER}" = "chrome" ]; then
|
||||
EXTRA_ARGS="-Dchrome.binary=/usr/bin/chromium"
|
||||
fi
|
||||
SELENOID_ARGS=""
|
||||
SELENOID_BASE=""
|
||||
SELENOID_VIDEO_BEFORE="./artifacts/selenoid-videos-before.txt"
|
||||
SELENOID_VIDEO_AFTER="./artifacts/selenoid-videos-after.txt"
|
||||
SELENOID_VIDEO_NEW="./artifacts/selenoid-videos-new.txt"
|
||||
: > "${SELENOID_VIDEO_BEFORE}"
|
||||
: > "${SELENOID_VIDEO_AFTER}"
|
||||
: > "${SELENOID_VIDEO_NEW}"
|
||||
if [ -n "${SELENOID_URL}" ]; then
|
||||
case "${BROWSER}" in
|
||||
chrome) docker pull selenoid/vnc:chrome_128.0 || true ;;
|
||||
firefox) docker pull selenoid/vnc:firefox_125.0 || true ;;
|
||||
esac
|
||||
docker pull selenoid/video-recorder:latest-release || docker pull selenoid/video-recorder:latest || true
|
||||
SELENOID_ARGS="-Dselenoid.video.enabled=true"
|
||||
SELENOID_BASE="${SELENOID_URL%/wd/hub}"
|
||||
(curl -fsS "${SELENOID_BASE}/video/" || true) \
|
||||
| tr '"' '\n' \
|
||||
| grep -E '\\.mp4$' \
|
||||
| sort -u > "${SELENOID_VIDEO_BEFORE}" || true
|
||||
fi
|
||||
|
||||
CID="$(docker create \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
localhost:5005/otus/test-selenium:1.0.0 \
|
||||
bash -lc "set -e; cd /workspace; mvn -Dallure.version=${ALLURE_ADAPTER_VERSION} -Dbrowser=${BROWSER} -Dbase.url=${BASE_URL} -Dselenoid.url=${SELENOID_URL} -Dselenide.headless=${HEADLESS} -Dallure.results.directory=target/allure-results ${SELENOID_ARGS} ${EXTRA_ARGS} test")"
|
||||
cleanup_container() {
|
||||
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup_container EXIT INT TERM
|
||||
tar -C ./sources -cf - . | docker cp - "${CID}:/workspace"
|
||||
set +e
|
||||
docker start -a "${CID}"
|
||||
TEST_RC=$?
|
||||
docker cp "${CID}:/workspace/target" "./artifacts" || true
|
||||
mkdir -p ./artifacts/target/allure-results
|
||||
if [ -n "${SELENOID_URL}" ]; then
|
||||
(curl -fsS "${SELENOID_BASE}/video/" || true) \
|
||||
| tr '"' '\n' \
|
||||
| grep -E '\\.mp4$' \
|
||||
| sort -u > "${SELENOID_VIDEO_AFTER}" || true
|
||||
comm -13 "${SELENOID_VIDEO_BEFORE}" "${SELENOID_VIDEO_AFTER}" > "${SELENOID_VIDEO_NEW}" || true
|
||||
if [ ! -s "${SELENOID_VIDEO_NEW}" ]; then
|
||||
cp "${SELENOID_VIDEO_AFTER}" "${SELENOID_VIDEO_NEW}" || true
|
||||
fi
|
||||
while IFS= read -r video_file; do
|
||||
[ -n "${video_file}" ] || continue
|
||||
downloaded=0
|
||||
for i in $(seq 1 20); do
|
||||
if curl -fsS "${SELENOID_BASE}/video/${video_file}" -o "./artifacts/target/${video_file}"; then
|
||||
downloaded=1
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
if [ "${downloaded}" -ne 1 ]; then
|
||||
echo "Failed to fetch Selenoid video: ${video_file}"
|
||||
fi
|
||||
done < "${SELENOID_VIDEO_NEW}"
|
||||
fi
|
||||
|
||||
ATTACHMENT_COUNT=0
|
||||
ATTACHMENTS_MANIFEST="./artifacts/target/allure-results/.external-attachments.txt"
|
||||
: > "${ATTACHMENTS_MANIFEST}"
|
||||
ATTACH_FILES="$(find ./artifacts -type f \\( -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.mp4' -o -iname '*.webm' \\) 2>/dev/null || true)"
|
||||
if [ -n "${ATTACH_FILES}" ]; then
|
||||
while IFS= read -r attachment_file; do
|
||||
if [ -z "${attachment_file}" ] || [ ! -f "${attachment_file}" ]; then
|
||||
continue
|
||||
fi
|
||||
ATTACHMENT_COUNT=$((ATTACHMENT_COUNT + 1))
|
||||
ext="$(echo "${attachment_file}" | awk -F. '{print tolower($NF)}')"
|
||||
mime="application/octet-stream"
|
||||
case "${ext}" in
|
||||
png) mime="image/png" ;;
|
||||
jpg|jpeg) mime="image/jpeg" ;;
|
||||
mp4) mime="video/mp4" ;;
|
||||
webm) mime="video/webm" ;;
|
||||
esac
|
||||
source_name="external-attachment-${ATTACHMENT_COUNT}.${ext}"
|
||||
cp "${attachment_file}" "./artifacts/target/allure-results/${source_name}" || continue
|
||||
safe_name="$(basename "${attachment_file}" | sed 's/"/\\"/g')"
|
||||
printf '%s|%s|%s\n' "${safe_name}" "${mime}" "${source_name}" >> "${ATTACHMENTS_MANIFEST}"
|
||||
done <<EOF
|
||||
${ATTACH_FILES}
|
||||
EOF
|
||||
fi
|
||||
if [ "${ATTACHMENT_COUNT}" -gt 0 ]; then
|
||||
ATTACHMENTS_JSON="$(awk -F'|' 'BEGIN{first=1} {if(!first){printf(",")} first=0; gsub(/"/,"\\\"", $1); printf("{\\\"name\\\":\\\"%s\\\",\\\"type\\\":\\\"%s\\\",\\\"source\\\":\\\"%s\\\"}", $1, $2, $3)}' "${ATTACHMENTS_MANIFEST}")"
|
||||
EXTRA_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
||||
TS_MS="$(( $(date +%s) * 1000 ))"
|
||||
cat > "./artifacts/target/allure-results/${EXTRA_UUID}-result.json" <<EOF
|
||||
{
|
||||
"uuid": "${EXTRA_UUID}",
|
||||
"historyId": "external-artifacts-${BUILD_NUMBER}",
|
||||
"name": "Collected artifacts",
|
||||
"fullName": "pipeline.CollectedArtifacts",
|
||||
"status": "passed",
|
||||
"stage": "finished",
|
||||
"start": ${TS_MS},
|
||||
"stop": ${TS_MS},
|
||||
"labels": [
|
||||
{"name": "suite", "value": "Pipeline Artifacts"},
|
||||
{"name": "package", "value": "pipeline"},
|
||||
{"name": "testClass", "value": "CollectedArtifacts"},
|
||||
{"name": "testMethod", "value": "attach"}
|
||||
],
|
||||
"attachments": [${ATTACHMENTS_JSON}],
|
||||
"steps": [],
|
||||
"parameters": []
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
{
|
||||
echo "job=${JOB_NAME}"
|
||||
echo "build=${BUILD_NUMBER}"
|
||||
echo "trigger.user=${RUN_TRIGGER_USER}"
|
||||
echo "trigger.name=${RUN_TRIGGER_NAME}"
|
||||
echo "repo.url=${BDD_REPO_URL}"
|
||||
echo "repo.ref=${TARGET_REF}"
|
||||
echo "repo.sha=${GIT_SHA}"
|
||||
echo "browser=${BROWSER}"
|
||||
echo "headless=${HEADLESS}"
|
||||
echo "base.url=${BASE_URL}"
|
||||
echo "selenoid.url=${SELENOID_URL}"
|
||||
echo "video.selection=all-new-from-selenoid"
|
||||
} > ./artifacts/target/allure-results/environment.properties
|
||||
trap - EXIT INT TERM
|
||||
docker rm -f "${CID}" || true
|
||||
exit "${TEST_RC}"
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
always {
|
||||
script {
|
||||
try {
|
||||
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'artifacts/target/allure-results']]
|
||||
} catch (Exception ex) {
|
||||
echo "Allure publisher unavailable: ${ex.message}"
|
||||
}
|
||||
}
|
||||
junit allowEmptyResults: true, testResults: 'artifacts/target/surefire-reports/*.xml'
|
||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/run-info.txt,artifacts/target/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,6 @@
|
||||
parameters:
|
||||
- string:
|
||||
name: JJB_PATH
|
||||
default: /workspace/hw8/config/jobs
|
||||
default: /workspace/projectwork/config/jobs
|
||||
description: "Путь до JJB-конфигов"
|
||||
dsl: !include-raw-verbatim: ../scripts/jobs-uploader.groovy
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- job:
|
||||
name: qa-api-contract-tests
|
||||
description: "Контрактные API тесты (WireMock + JsonSchemaValidation)."
|
||||
project-type: pipeline
|
||||
concurrent: true
|
||||
sandbox: true
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 50
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-api-contract-tests.groovy
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- job:
|
||||
name: qa-api-rest-tests
|
||||
description: "API REST тесты (RestAssured + JsonSchema + quality gates)."
|
||||
project-type: pipeline
|
||||
concurrent: true
|
||||
sandbox: true
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 50
|
||||
parameters:
|
||||
- string:
|
||||
name: API_REST_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/hw3.git
|
||||
- string:
|
||||
name: API_REST_REPO_REF
|
||||
default: main
|
||||
- string:
|
||||
name: API_REST_MAVEN_GOAL
|
||||
default: verify
|
||||
- string:
|
||||
name: API_REST_JAVA_RELEASE
|
||||
default: "21"
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-hw3-api-tests.groovy
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
- job:
|
||||
name: qa-maven-extra-tests
|
||||
description: "Дополнительные Maven тесты (универсальная job)."
|
||||
project-type: pipeline
|
||||
concurrent: true
|
||||
sandbox: true
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 50
|
||||
parameters:
|
||||
- string:
|
||||
name: EXTRA_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/hw3.git
|
||||
- string:
|
||||
name: EXTRA_REPO_REF
|
||||
default: homework_5
|
||||
- string:
|
||||
name: EXTRA_MAVEN_GOAL
|
||||
default: test
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-hw5-tests.groovy
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
- job:
|
||||
name: qa-playwright-tests
|
||||
description: "Playwright UI тесты."
|
||||
project-type: pipeline
|
||||
concurrent: true
|
||||
sandbox: true
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 50
|
||||
parameters:
|
||||
- string:
|
||||
name: PLAYWRIGHT_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/homework_6.git
|
||||
- string:
|
||||
name: PLAYWRIGHT_REPO_REF
|
||||
default: master
|
||||
- choice:
|
||||
name: PLAYWRIGHT_BROWSER
|
||||
choices:
|
||||
- chromium
|
||||
- firefox
|
||||
- webkit
|
||||
- choice:
|
||||
name: PLAYWRIGHT_HEADLESS
|
||||
choices:
|
||||
- "true"
|
||||
- "false"
|
||||
- string:
|
||||
name: PLAYWRIGHT_BASE_URL
|
||||
default: https://otus.ru
|
||||
- string:
|
||||
name: PLAYWRIGHT_DOCKER_IMAGE
|
||||
default: mcr.microsoft.com/playwright/java:v1.58.0-jammy
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-hw6-playwright-tests.groovy
|
||||
@@ -5,10 +5,22 @@
|
||||
project-type: pipeline
|
||||
concurrent: true
|
||||
sandbox: true
|
||||
triggers:
|
||||
- timed: "15 1 * * *"
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 50
|
||||
parameters:
|
||||
- string:
|
||||
name: BDD_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/homework_1.git
|
||||
- string:
|
||||
name: BDD_REFS
|
||||
default: main,homework_2
|
||||
- string:
|
||||
name: BDD_REPO_REF
|
||||
default: homework_2
|
||||
description: "Legacy fallback if BDD_REFS is empty"
|
||||
- string:
|
||||
name: QA_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/homework_4.git
|
||||
@@ -41,15 +53,15 @@
|
||||
default: https://otus.ru
|
||||
- string:
|
||||
name: EXECUTION_MODE
|
||||
default: local
|
||||
default: selenoid
|
||||
- string:
|
||||
name: SELENOID_URL
|
||||
default: http://host.docker.internal:4444/wd/hub
|
||||
- choice:
|
||||
name: HEADLESS
|
||||
choices:
|
||||
- "true"
|
||||
- "false"
|
||||
- "true"
|
||||
- string:
|
||||
name: APP_URL
|
||||
default: http://wiremock:8080/wishlist.apk
|
||||
@@ -83,4 +95,36 @@
|
||||
- string:
|
||||
name: RESERVATION_OWNER
|
||||
default: user4us
|
||||
- string:
|
||||
name: API_REST_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/hw3.git
|
||||
- string:
|
||||
name: API_REST_REPO_REF
|
||||
default: main
|
||||
- string:
|
||||
name: EXTRA_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/hw3.git
|
||||
- string:
|
||||
name: EXTRA_REPO_REF
|
||||
default: homework_5
|
||||
- string:
|
||||
name: PLAYWRIGHT_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/homework_6.git
|
||||
- string:
|
||||
name: PLAYWRIGHT_REPO_REF
|
||||
default: master
|
||||
- choice:
|
||||
name: PLAYWRIGHT_BROWSER
|
||||
choices:
|
||||
- chromium
|
||||
- firefox
|
||||
- webkit
|
||||
- choice:
|
||||
name: PLAYWRIGHT_HEADLESS
|
||||
choices:
|
||||
- "true"
|
||||
- "false"
|
||||
- string:
|
||||
name: PLAYWRIGHT_DOCKER_IMAGE
|
||||
default: mcr.microsoft.com/playwright/java:v1.58.0-jammy
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-runner.groovy
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
description: "Базовый URL тестируемого сайта"
|
||||
- string:
|
||||
name: EXECUTION_MODE
|
||||
default: local
|
||||
default: selenoid
|
||||
description: "Режим запуска (local|selenoid)"
|
||||
- string:
|
||||
name: SELENOID_URL
|
||||
@@ -42,7 +42,7 @@
|
||||
- choice:
|
||||
name: HEADLESS
|
||||
choices:
|
||||
- "true"
|
||||
- "false"
|
||||
- "true"
|
||||
description: "Headless режим"
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-selenium-tests.groovy
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
- job:
|
||||
name: qa-web-bdd-tests
|
||||
description: "WEB BDD/Cucumber тесты (homework_1, branch homework_2)."
|
||||
project-type: pipeline
|
||||
concurrent: true
|
||||
sandbox: true
|
||||
properties:
|
||||
- build-discarder:
|
||||
num-to-keep: 50
|
||||
parameters:
|
||||
- string:
|
||||
name: BDD_REPO_URL
|
||||
default: https://git.kovbasa.ru/otus-autotests/homework_1.git
|
||||
description: "Git URL репозитория с BDD/Cucumber web тестами"
|
||||
- string:
|
||||
name: BDD_REPO_REF
|
||||
default: homework_2
|
||||
description: "Git branch/tag/commit для checkout"
|
||||
- string:
|
||||
name: ALLURE_ADAPTER_VERSION
|
||||
default: 2.29.1
|
||||
description: "Версия allure-junit5 адаптера"
|
||||
- choice:
|
||||
name: BROWSER
|
||||
choices:
|
||||
- chrome
|
||||
- firefox
|
||||
- string:
|
||||
name: BASE_URL
|
||||
default: https://otus.ru
|
||||
- string:
|
||||
name: SELENOID_URL
|
||||
default: http://host.docker.internal:4444/wd/hub
|
||||
- choice:
|
||||
name: HEADLESS
|
||||
choices:
|
||||
- "false"
|
||||
- "true"
|
||||
dsl: !include-raw-verbatim: ../scripts/qa-web-bdd-tests.groovy
|
||||
@@ -41,9 +41,14 @@
|
||||
filter-executors: true
|
||||
filter-queue: true
|
||||
job-name:
|
||||
- qa-web-bdd-tests
|
||||
- qa-selenium-tests
|
||||
- qa-api-citrus-tests
|
||||
- qa-api-contract-tests
|
||||
- qa-mobile-appium-tests
|
||||
- qa-api-rest-tests
|
||||
- qa-maven-extra-tests
|
||||
- qa-playwright-tests
|
||||
columns:
|
||||
- status
|
||||
- weather
|
||||
|
||||
Reference in New Issue
Block a user