Implement fully automated Jenkins HW8 setup with Ansible, JCasC and JJB
This commit is contained in:
@@ -4,3 +4,5 @@ build/
|
|||||||
*.iml
|
*.iml
|
||||||
.DS_Store
|
.DS_Store
|
||||||
jenkins_home/
|
jenkins_home/
|
||||||
|
compose/.env
|
||||||
|
lib/
|
||||||
|
|||||||
-21
@@ -1,21 +0,0 @@
|
|||||||
FROM jenkins/jenkins:lts-jdk21
|
|
||||||
|
|
||||||
USER root
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
ca-certificates \
|
|
||||||
curl \
|
|
||||||
git \
|
|
||||||
maven \
|
|
||||||
docker.io \
|
|
||||||
docker-compose \
|
|
||||||
chromium \
|
|
||||||
firefox-esr \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
|
|
||||||
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
|
|
||||||
|
|
||||||
COPY init.groovy.d/ /usr/share/jenkins/ref/init.groovy.d/
|
|
||||||
COPY jobs/ /usr/share/jenkins/ref/job-xml/
|
|
||||||
|
|
||||||
USER jenkins
|
|
||||||
@@ -1,59 +1,140 @@
|
|||||||
# OTUS Homework 8: Jenkins Jobs
|
# OTUS HW8: Jenkins + Ansible + JJB + Docker Slaves
|
||||||
|
|
||||||
Проект поднимает Jenkins и отдельный Docker daemon (`docker:dind`) в Docker и автоматически создает 2 job:
|
Полностью автоматизированный проект для ДЗ8:
|
||||||
- `selenium-tests` для Selenium/Selenide тестов по Otus с выбором браузера;
|
- Jenkins в Docker;
|
||||||
- `mobile-appium-tests` для Appium тестов мобильного приложения с Allure-отчетом.
|
- reverse proxy (Nginx);
|
||||||
|
- локальный Docker Registry;
|
||||||
|
- docker-slaves (агенты Jenkins);
|
||||||
|
- Jenkins Job Builder (JJB) для автоматического создания/обновления job;
|
||||||
|
- Ansible playbook для раскатки всего с нуля;
|
||||||
|
- 6 готовых job, включая runner с параллельным запуском тестов;
|
||||||
|
- Allure-отчеты для Selenium и Appium job.
|
||||||
|
|
||||||
## Что входит в проект
|
Проект использует локальные домашки из корня:
|
||||||
- `Dockerfile`, `docker-compose.yml` — Jenkins с предустановленными инструментами и отдельным Docker daemon для job.
|
- `C:/Users/spawn/IdeaProjects/otus-autotests/homework_4` (Selenium + Citrus);
|
||||||
- `init.groovy.d/` — автосоздание пользователя `admin/admin`, job и Allure CLI.
|
- `C:/Users/spawn/IdeaProjects/otus-autotests/hw7` (Appium).
|
||||||
- `jobs/selenium-tests.xml` — job для `https://git.kovbasa.ru/otus-autotests/homework_4.git`.
|
|
||||||
- `jobs/mobile-appium-tests.xml` — job для `https://git.kovbasa.ru/otus-autotests/homework_7.git`.
|
|
||||||
|
|
||||||
## Требования
|
## Что развертывается
|
||||||
- Docker и Docker Compose.
|
|
||||||
- Доступ Jenkins-контейнера в интернет для клонирования репозиториев и загрузки Maven dependencies.
|
|
||||||
- Для Appium job:
|
|
||||||
- доступ к БД `jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist`;
|
|
||||||
- хост, на котором Docker может поднять `budtmo/docker-android`.
|
|
||||||
|
|
||||||
## Запуск
|
### Сервисы (Docker Compose)
|
||||||
1. Поднять Jenkins:
|
- `registry` (`localhost:5005`) — хранение образов тестов и слейвов.
|
||||||
|
- `jenkins` (`localhost:8081`) — Jenkins controller (JCasC).
|
||||||
|
- `nginx` (`localhost:8088`) — внешний вход в Jenkins.
|
||||||
|
- `agent-maven` — swarm-agent с label `maven docker`.
|
||||||
|
- `agent-jjb` — swarm-agent с label `jjb docker`.
|
||||||
|
- `jobs_uploader` — one-shot контейнер, который накатывает job через `jenkins-jobs update`.
|
||||||
|
|
||||||
|
### Автоматизация (Ansible)
|
||||||
|
Playbook:
|
||||||
|
1. Поднимает `registry + jenkins + nginx`.
|
||||||
|
2. Ждет готовности Jenkins.
|
||||||
|
3. Собирает и пушит в registry все нужные образы:
|
||||||
|
- `localhost:5005/otus/slave-maven:1.0.0`
|
||||||
|
- `localhost:5005/otus/slave-jjb:1.0.0`
|
||||||
|
- `localhost:5005/otus/test-selenium:1.0.0`
|
||||||
|
- `localhost:5005/otus/test-api:1.0.0`
|
||||||
|
- `localhost:5005/otus/test-mobile:1.0.0`
|
||||||
|
4. Поднимает слейвы.
|
||||||
|
5. Накатывает все job через JJB.
|
||||||
|
|
||||||
|
По умолчанию UI через nginx опубликован на `8088`, чтобы не конфликтовать с Selenoid UI из `homework_4` (часто занимает `8080`).
|
||||||
|
|
||||||
|
## 6 Jenkins job
|
||||||
|
|
||||||
|
1. `jobs-uploader` — обновление всех job из `hw8/config/jobs`.
|
||||||
|
2. `infra-health-check` — проверка инфраструктуры и образов.
|
||||||
|
3. `qa-selenium-tests` — Selenium/Selenide (browser parameter + Allure).
|
||||||
|
4. `qa-mobile-appium-tests` — Appium (APP_URL для автоскачивания APK + Allure).
|
||||||
|
5. `qa-api-citrus-tests` — API (citrus-tests).
|
||||||
|
6. `qa-runner` — запускает `qa-selenium-tests`, `qa-mobile-appium-tests`, `qa-api-citrus-tests` параллельно.
|
||||||
|
|
||||||
|
Есть вьюхи:
|
||||||
|
- `DevOps`;
|
||||||
|
- `QA`.
|
||||||
|
|
||||||
|
## Важные требования окружения
|
||||||
|
- Docker + Docker Compose V2.
|
||||||
|
- Ansible.
|
||||||
|
- Linux/WSL2 host для mobile-части (желательно с `/dev/kvm` для эмуляторов `budtmo/docker-android`).
|
||||||
|
- Доступ к БД для `hw7` (`DB_URL/DB_USER/DB_PASSWORD`).
|
||||||
|
|
||||||
|
## Быстрый старт с нуля
|
||||||
|
|
||||||
|
### 1. Перейти в `hw8`
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d --build
|
cd C:/Users/spawn/IdeaProjects/otus-autotests/hw8
|
||||||
```
|
```
|
||||||
2. Открыть `http://localhost:8081`.
|
|
||||||
3. Войти под `admin` / `admin`.
|
|
||||||
4. Убедиться, что автоматически созданы job:
|
|
||||||
- `selenium-tests`
|
|
||||||
- `mobile-appium-tests`
|
|
||||||
|
|
||||||
Jenkins job запускают Docker не через сокет хоста, а через отдельный сервис `docker`. Это нужно, чтобы mobile job корректно работала с файлами из Jenkins workspace.
|
### 2. Подготовить `.env` для compose
|
||||||
|
```bash
|
||||||
|
cp compose/.env.example compose/.env
|
||||||
|
```
|
||||||
|
Обязательно заполните в `compose/.env`:
|
||||||
|
- `JENKINS_ADMIN_ID`, `JENKINS_ADMIN_PASSWORD`;
|
||||||
|
- `MOBILE_DB_PASSWORD` (если не хотите передавать `DB_PASSWORD` руками в параметрах job);
|
||||||
|
- при необходимости `NGINX_PORT` и `JENKINS_URL_PUBLIC`.
|
||||||
|
|
||||||
## Запуск job
|
### 3. Запустить полный деплой playbook
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.ini ansible/playbooks/site.yml
|
||||||
|
```
|
||||||
|
|
||||||
### selenium-tests
|
Если запускаете из Windows PowerShell без установленного Ansible:
|
||||||
Запускать через **Build with Parameters**. Рабочие значения по умолчанию:
|
```powershell
|
||||||
- `REPO_URL=https://git.kovbasa.ru/otus-autotests/homework_4.git`
|
wsl bash -lc "cd /mnt/c/Users/spawn/IdeaProjects/otus-autotests/hw8 && ansible-playbook -i ansible/inventory/hosts.ini ansible/playbooks/site.yml"
|
||||||
- `BRANCH=master`
|
```
|
||||||
- `BROWSER=chrome`
|
|
||||||
- `EXECUTION_MODE=local`
|
|
||||||
- `HEADLESS=true`
|
|
||||||
|
|
||||||
### mobile-appium-tests
|
### 4. Открыть Jenkins
|
||||||
Запускать через **Build with Parameters**. Рабочие значения по умолчанию:
|
- через nginx: `http://localhost:8088`
|
||||||
- `REPO_URL=https://git.kovbasa.ru/otus-autotests/homework_7.git`
|
- напрямую: `http://localhost:8081`
|
||||||
- `BRANCH=master`
|
|
||||||
- `DB_URL=jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist`
|
|
||||||
- `DB_USER=student`
|
|
||||||
- `APPIUM_URL=http://docker:4723`
|
|
||||||
|
|
||||||
Обязательно заполнить:
|
Логин/пароль: из `compose/.env` (`JENKINS_ADMIN_ID`, `JENKINS_ADMIN_PASSWORD`).
|
||||||
- `DB_PASSWORD`
|
|
||||||
|
|
||||||
Опционально:
|
## Как запускать тесты
|
||||||
- `APP_URL` — если задан, APK будет скачан автоматически; если пусто, используется APK из репозитория `homework_7`.
|
|
||||||
|
|
||||||
## Результат
|
### Рекомендуемый запуск
|
||||||
- Allure-отчет публикуется в каждой job после завершения билда.
|
Запустить `qa-runner` через **Build with Parameters**.
|
||||||
- XML job лежат в каталоге `jobs/` и могут быть выданы как результат домашнего задания.
|
|
||||||
|
### Selenium отдельно
|
||||||
|
Запустить `qa-selenium-tests`:
|
||||||
|
- `BROWSER`: `chrome|firefox`;
|
||||||
|
- `HEADLESS`: `true|false`.
|
||||||
|
|
||||||
|
### Appium отдельно
|
||||||
|
Запустить `qa-mobile-appium-tests`:
|
||||||
|
- `DB_PASSWORD` (по умолчанию `student`, если пусто — берется из env `MOBILE_DB_PASSWORD` Jenkins controller);
|
||||||
|
- `APP_URL` по умолчанию: `http://wiremock:8080/wishlist.apk`.
|
||||||
|
- дефолтные пароли тест-аккаунтов из `hw7`: `user1us/user2us/user3us` (соответствуют логинам).
|
||||||
|
|
||||||
|
`APP_URL` передается в capability `app`, поэтому APK скачивается автоматически Appium-сервером эмулятора.
|
||||||
|
|
||||||
|
## Отчеты
|
||||||
|
- `qa-selenium-tests` публикует Allure + JUnit.
|
||||||
|
- `qa-mobile-appium-tests` публикует Allure + JUnit.
|
||||||
|
- артефакты `target/**` архивируются в Jenkins.
|
||||||
|
|
||||||
|
## Структура проекта
|
||||||
|
|
||||||
|
- `ansible/` — playbook раскатки/удаления.
|
||||||
|
- `compose/` — инфраструктура Jenkins (compose, Dockerfile, JCasC, nginx, jobs_uploader).
|
||||||
|
- `compose/images/` — Dockerfile для слейвов и test-runner образов.
|
||||||
|
- `config/jobs/` — JJB YAML + Groovy pipeline scripts + views.
|
||||||
|
|
||||||
|
## Повторное обновление job
|
||||||
|
|
||||||
|
1. Через Jenkins job `jobs-uploader`, либо
|
||||||
|
2. Ручным one-shot:
|
||||||
|
```bash
|
||||||
|
docker compose -f compose/docker-compose.yml --env-file compose/.env run --rm jobs_uploader
|
||||||
|
```
|
||||||
|
|
||||||
|
## Полное удаление стенда
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.ini ansible/playbooks/down.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Чистый повторный запуск (reset + deploy)
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.ini ansible/playbooks/down.yml
|
||||||
|
ansible-playbook -i ansible/inventory/hosts.ini ansible/playbooks/site.yml
|
||||||
|
```
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[defaults]
|
||||||
|
inventory = ./inventory/hosts.ini
|
||||||
|
host_key_checking = False
|
||||||
|
stdout_callback = yaml
|
||||||
|
timeout = 30
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[local]
|
||||||
|
localhost ansible_connection=local
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- name: Destroy OTUS HW8 Jenkins infrastructure
|
||||||
|
hosts: local
|
||||||
|
gather_facts: false
|
||||||
|
vars:
|
||||||
|
hw8_root: "{{ playbook_dir }}/../.."
|
||||||
|
compose_dir: "{{ hw8_root }}/compose"
|
||||||
|
compose_file: "{{ compose_dir }}/docker-compose.yml"
|
||||||
|
compose_env_file: "{{ compose_dir }}/.env"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Stop and remove compose stack
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: docker compose -f {{ compose_file }} --env-file {{ compose_env_file }} down -v --remove-orphans
|
||||||
|
args:
|
||||||
|
chdir: "{{ compose_dir }}"
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
- name: Deploy OTUS HW8 Jenkins infrastructure
|
||||||
|
hosts: local
|
||||||
|
gather_facts: true
|
||||||
|
vars:
|
||||||
|
hw8_root: "{{ playbook_dir }}/../.."
|
||||||
|
compose_dir: "{{ hw8_root }}/compose"
|
||||||
|
compose_file: "{{ compose_dir }}/docker-compose.yml"
|
||||||
|
compose_env_file: "{{ compose_dir }}/.env"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
- name: Check Docker CLI availability
|
||||||
|
ansible.builtin.command: docker --version
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Check Docker Compose availability
|
||||||
|
ansible.builtin.command: docker compose version
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Create compose env file from template if missing
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: "{{ compose_dir }}/.env.example"
|
||||||
|
dest: "{{ compose_env_file }}"
|
||||||
|
force: true
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Ensure image build script is executable
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ compose_dir }}/scripts/build_and_push_images.sh"
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Pre-pull Jenkins base image (retry on Docker Hub timeouts)
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: docker pull jenkins/jenkins:2.541.3-lts-jdk21
|
||||||
|
register: pull_jenkins_base
|
||||||
|
retries: 6
|
||||||
|
delay: 20
|
||||||
|
until: pull_jenkins_base.rc == 0
|
||||||
|
|
||||||
|
- name: Build and start registry, jenkins and nginx
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: docker compose -f {{ compose_file }} --env-file {{ compose_env_file }} up -d --build registry jenkins nginx
|
||||||
|
args:
|
||||||
|
chdir: "{{ compose_dir }}"
|
||||||
|
register: compose_bootstrap
|
||||||
|
retries: 4
|
||||||
|
delay: 20
|
||||||
|
until: compose_bootstrap.rc == 0
|
||||||
|
|
||||||
|
- name: Wait for Jenkins to become available
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://127.0.0.1:8081/login"
|
||||||
|
status_code: 200
|
||||||
|
register: jenkins_ready
|
||||||
|
retries: 60
|
||||||
|
delay: 5
|
||||||
|
until: jenkins_ready.status == 200
|
||||||
|
|
||||||
|
- name: Build and push slave/test images to local registry
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: ./scripts/build_and_push_images.sh localhost:5005 1.0.0
|
||||||
|
args:
|
||||||
|
chdir: "{{ compose_dir }}"
|
||||||
|
register: build_and_push_result
|
||||||
|
retries: 3
|
||||||
|
delay: 20
|
||||||
|
until: build_and_push_result.rc == 0
|
||||||
|
|
||||||
|
- name: Start swarm agents
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: docker compose -f {{ compose_file }} --env-file {{ compose_env_file }} up -d agent-maven agent-jjb
|
||||||
|
args:
|
||||||
|
chdir: "{{ compose_dir }}"
|
||||||
|
|
||||||
|
- name: Build jobs_uploader image with latest changes
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: docker compose -f {{ compose_file }} --env-file {{ compose_env_file }} build jobs_uploader
|
||||||
|
args:
|
||||||
|
chdir: "{{ compose_dir }}"
|
||||||
|
|
||||||
|
- name: Remove stale jobs_uploader run containers
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
ids="$(docker ps -aq --filter "name=jobs_uploader-run" || true)"
|
||||||
|
if [ -n "${ids}" ]; then
|
||||||
|
docker rm -f ${ids}
|
||||||
|
fi
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
|
||||||
|
- name: Upload Jenkins jobs via JJB container
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: timeout 900 docker compose -f {{ compose_file }} --env-file {{ compose_env_file }} run --rm jobs_uploader
|
||||||
|
args:
|
||||||
|
chdir: "{{ compose_dir }}"
|
||||||
|
|
||||||
|
- name: Show endpoint details
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg:
|
||||||
|
- "Jenkins UI: http://localhost:8088 (via nginx) or http://localhost:8081"
|
||||||
|
- "Registry: http://localhost:5005/v2/"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
JENKINS_ADMIN_ID=admin
|
||||||
|
JENKINS_ADMIN_PASSWORD=admin
|
||||||
|
JENKINS_URL_PUBLIC=http://localhost:8088/
|
||||||
|
NGINX_PORT=8088
|
||||||
|
MOBILE_DB_PASSWORD=student
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
services:
|
||||||
|
registry:
|
||||||
|
image: registry:2.8.3
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
REGISTRY_HTTP_ADDR: 0.0.0.0:5000
|
||||||
|
ports:
|
||||||
|
- "5005:5000"
|
||||||
|
volumes:
|
||||||
|
- registry_data:/var/lib/registry
|
||||||
|
networks:
|
||||||
|
- jenkins_net
|
||||||
|
|
||||||
|
jenkins:
|
||||||
|
build:
|
||||||
|
context: ./jenkins
|
||||||
|
image: local-jenkins:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
user: root
|
||||||
|
environment:
|
||||||
|
CASC_JENKINS_CONFIG: /var/jenkins_home/casc_configs/jenkins.yaml
|
||||||
|
JAVA_OPTS: >-
|
||||||
|
-Djenkins.install.runSetupWizard=false
|
||||||
|
-Dhudson.model.DownloadService.noSignatureCheck=true
|
||||||
|
JENKINS_ADMIN_ID: ${JENKINS_ADMIN_ID}
|
||||||
|
JENKINS_ADMIN_PASSWORD: ${JENKINS_ADMIN_PASSWORD}
|
||||||
|
JENKINS_URL_PUBLIC: ${JENKINS_URL_PUBLIC}
|
||||||
|
MOBILE_DB_PASSWORD: ${MOBILE_DB_PASSWORD:-}
|
||||||
|
ports:
|
||||||
|
- "8081:8080"
|
||||||
|
- "50000:50000"
|
||||||
|
volumes:
|
||||||
|
- jenkins_home:/var/jenkins_home
|
||||||
|
- ./jenkins/casc:/var/jenkins_home/casc_configs:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ../..:/workspace/otus-autotests:ro
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl -fsS http://127.0.0.1:8080/login >/dev/null"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 30
|
||||||
|
networks:
|
||||||
|
- jenkins_net
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:1.28.0
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
jenkins:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "${NGINX_PORT:-8088}:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||||
|
networks:
|
||||||
|
- jenkins_net
|
||||||
|
|
||||||
|
jobs_uploader:
|
||||||
|
build:
|
||||||
|
context: ./jobs_uploader
|
||||||
|
image: local-jobs-uploader:latest
|
||||||
|
restart: "no"
|
||||||
|
depends_on:
|
||||||
|
jenkins:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
JENKINS_HOSTNAME: http://jenkins:8080
|
||||||
|
JENKINS_USERNAME: ${JENKINS_ADMIN_ID}
|
||||||
|
JENKINS_PASSWORD: ${JENKINS_ADMIN_PASSWORD}
|
||||||
|
JJB_PATH: /workspace/otus-autotests/hw8/config/jobs
|
||||||
|
volumes:
|
||||||
|
- ../..:/workspace/otus-autotests:ro
|
||||||
|
networks:
|
||||||
|
- jenkins_net
|
||||||
|
|
||||||
|
agent-maven:
|
||||||
|
image: localhost:5005/otus/slave-maven:1.0.0
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
jenkins:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
JENKINS_URL: http://jenkins:8080
|
||||||
|
JENKINS_USER: ${JENKINS_ADMIN_ID}
|
||||||
|
JENKINS_PASSWORD: ${JENKINS_ADMIN_PASSWORD}
|
||||||
|
JENKINS_AGENT_NAME: maven-agent
|
||||||
|
JENKINS_AGENT_WORKDIR: /home/jenkins/agent
|
||||||
|
JENKINS_WEB_SOCKET: "true"
|
||||||
|
JENKINS_LABELS: maven docker
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ../..:/workspace/otus-autotests:ro
|
||||||
|
networks:
|
||||||
|
- jenkins_net
|
||||||
|
|
||||||
|
agent-jjb:
|
||||||
|
image: localhost:5005/otus/slave-jjb:1.0.0
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
jenkins:
|
||||||
|
condition: service_healthy
|
||||||
|
environment:
|
||||||
|
JENKINS_URL: http://jenkins:8080
|
||||||
|
JENKINS_USER: ${JENKINS_ADMIN_ID}
|
||||||
|
JENKINS_PASSWORD: ${JENKINS_ADMIN_PASSWORD}
|
||||||
|
JENKINS_AGENT_NAME: jjb-agent
|
||||||
|
JENKINS_AGENT_WORKDIR: /home/jenkins/agent
|
||||||
|
JENKINS_WEB_SOCKET: "true"
|
||||||
|
JENKINS_LABELS: jjb docker
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ../..:/workspace/otus-autotests:ro
|
||||||
|
networks:
|
||||||
|
- jenkins_net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
jenkins_net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
jenkins_home:
|
||||||
|
registry_data:
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
FROM eclipse-temurin:21-jre
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
docker.io \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
&& pip3 install --break-system-packages --no-cache-dir "setuptools<81" "jenkins-job-builder>=6.4.3" \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN curl -fsSL -o /usr/local/bin/swarm-client.jar \
|
||||||
|
https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/3.51/swarm-client-3.51.jar
|
||||||
|
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
: "${JENKINS_URL:?JENKINS_URL is required}"
|
||||||
|
: "${JENKINS_USER:?JENKINS_USER is required}"
|
||||||
|
: "${JENKINS_PASSWORD:?JENKINS_PASSWORD is required}"
|
||||||
|
: "${JENKINS_AGENT_NAME:?JENKINS_AGENT_NAME is required}"
|
||||||
|
|
||||||
|
exec java -jar /usr/local/bin/swarm-client.jar \
|
||||||
|
-master "${JENKINS_URL}" \
|
||||||
|
-username "${JENKINS_USER}" \
|
||||||
|
-password "${JENKINS_PASSWORD}" \
|
||||||
|
-name "${JENKINS_AGENT_NAME}" \
|
||||||
|
-labels "${JENKINS_LABELS:-jjb docker}" \
|
||||||
|
-executors 1 \
|
||||||
|
-mode exclusive \
|
||||||
|
-disableSslVerification
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
FROM eclipse-temurin:21-jre
|
||||||
|
|
||||||
|
ARG DOCKER_COMPOSE_VERSION=2.36.2
|
||||||
|
ARG ALLURE_VERSION=2.29.0
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
docker.io \
|
||||||
|
unzip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN curl -fsSL -o /usr/local/bin/swarm-client.jar \
|
||||||
|
https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/3.51/swarm-client-3.51.jar
|
||||||
|
|
||||||
|
RUN mkdir -p /usr/local/lib/docker/cli-plugins \
|
||||||
|
&& curl -fsSL -o /usr/local/lib/docker/cli-plugins/docker-compose \
|
||||||
|
"https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64" \
|
||||||
|
&& chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
|
||||||
|
|
||||||
|
RUN curl -fsSL -o /tmp/allure.zip \
|
||||||
|
"https://github.com/allure-framework/allure2/releases/download/${ALLURE_VERSION}/allure-${ALLURE_VERSION}.zip" \
|
||||||
|
&& unzip -q /tmp/allure.zip -d /opt \
|
||||||
|
&& ln -s "/opt/allure-${ALLURE_VERSION}" /opt/allure \
|
||||||
|
&& ln -s /opt/allure/bin/allure /usr/local/bin/allure \
|
||||||
|
&& rm -f /tmp/allure.zip
|
||||||
|
|
||||||
|
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||||
|
RUN chmod +x /usr/local/bin/entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
: "${JENKINS_URL:?JENKINS_URL is required}"
|
||||||
|
: "${JENKINS_USER:?JENKINS_USER is required}"
|
||||||
|
: "${JENKINS_PASSWORD:?JENKINS_PASSWORD is required}"
|
||||||
|
: "${JENKINS_AGENT_NAME:?JENKINS_AGENT_NAME is required}"
|
||||||
|
|
||||||
|
exec java -jar /usr/local/bin/swarm-client.jar \
|
||||||
|
-master "${JENKINS_URL}" \
|
||||||
|
-username "${JENKINS_USER}" \
|
||||||
|
-password "${JENKINS_PASSWORD}" \
|
||||||
|
-name "${JENKINS_AGENT_NAME}" \
|
||||||
|
-labels "${JENKINS_LABELS:-maven docker}" \
|
||||||
|
-executors 1 \
|
||||||
|
-mode exclusive \
|
||||||
|
-disableSslVerification
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM maven:3.9.11-eclipse-temurin-21
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM maven:3.9.11-eclipse-temurin-21
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
FROM maven:3.9.11-eclipse-temurin-21
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
chromium \
|
||||||
|
chromium-driver \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
FROM jenkins/jenkins:2.541.3-lts-jdk21
|
||||||
|
|
||||||
|
USER root
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
docker.io \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
unzip \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ARG ALLURE_VERSION=2.29.0
|
||||||
|
RUN curl -fsSL -o /tmp/allure.zip \
|
||||||
|
"https://github.com/allure-framework/allure2/releases/download/${ALLURE_VERSION}/allure-${ALLURE_VERSION}.zip" \
|
||||||
|
&& unzip -q /tmp/allure.zip -d /opt \
|
||||||
|
&& ln -s "/opt/allure-${ALLURE_VERSION}" /opt/allure \
|
||||||
|
&& ln -s /opt/allure/bin/allure /usr/local/bin/allure \
|
||||||
|
&& rm -f /tmp/allure.zip
|
||||||
|
|
||||||
|
COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
|
||||||
|
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/plugins.txt
|
||||||
|
|
||||||
|
USER jenkins
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
credentials:
|
||||||
|
system:
|
||||||
|
domainCredentials:
|
||||||
|
- credentials:
|
||||||
|
- usernamePassword:
|
||||||
|
id: "jenkins-admin-userpass"
|
||||||
|
scope: GLOBAL
|
||||||
|
username: "${JENKINS_ADMIN_ID}"
|
||||||
|
password: "${JENKINS_ADMIN_PASSWORD}"
|
||||||
|
|
||||||
|
jenkins:
|
||||||
|
systemMessage: "Jenkins is configured by code (Docker + JCasC + JJB + Ansible)."
|
||||||
|
numExecutors: 0
|
||||||
|
mode: EXCLUSIVE
|
||||||
|
securityRealm:
|
||||||
|
local:
|
||||||
|
allowsSignup: false
|
||||||
|
users:
|
||||||
|
- id: "${JENKINS_ADMIN_ID}"
|
||||||
|
password: "${JENKINS_ADMIN_PASSWORD}"
|
||||||
|
authorizationStrategy:
|
||||||
|
loggedInUsersCanDoAnything:
|
||||||
|
allowAnonymousRead: false
|
||||||
|
crumbIssuer:
|
||||||
|
standard:
|
||||||
|
excludeClientIPFromCrumb: false
|
||||||
|
globalNodeProperties:
|
||||||
|
- envVars:
|
||||||
|
env:
|
||||||
|
- key: JENKINS_URL_INTERNAL
|
||||||
|
value: "http://jenkins:8080"
|
||||||
|
- key: OTUS_WORKSPACE_ROOT
|
||||||
|
value: "/workspace/otus-autotests"
|
||||||
|
- key: MOBILE_DB_PASSWORD
|
||||||
|
value: "${MOBILE_DB_PASSWORD}"
|
||||||
|
|
||||||
|
unclassified:
|
||||||
|
location:
|
||||||
|
url: "${JENKINS_URL_PUBLIC}"
|
||||||
|
|
||||||
|
tool:
|
||||||
|
allure:
|
||||||
|
installations:
|
||||||
|
- name: "allure"
|
||||||
|
home: "/opt/allure"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
workflow-aggregator
|
||||||
|
git
|
||||||
|
credentials
|
||||||
|
credentials-binding
|
||||||
|
matrix-project
|
||||||
|
docker-workflow
|
||||||
|
allure-jenkins-plugin
|
||||||
|
configuration-as-code
|
||||||
|
swarm
|
||||||
|
ansicolor
|
||||||
|
timestamper
|
||||||
|
build-user-vars-plugin
|
||||||
|
pipeline-utility-steps
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3.12-slim
|
||||||
|
|
||||||
|
WORKDIR /opt/jobs_uploader
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& pip install --no-cache-dir jinja2 "setuptools<81" "jenkins-job-builder>=6.4.3"
|
||||||
|
|
||||||
|
COPY entrypoint.sh /opt/jobs_uploader/entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /opt/jobs_uploader/entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/opt/jobs_uploader/entrypoint.sh"]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
export JENKINS_HOSTNAME="${JENKINS_HOSTNAME:?JENKINS_HOSTNAME is required}"
|
||||||
|
export JENKINS_USERNAME="${JENKINS_USERNAME:?JENKINS_USERNAME is required}"
|
||||||
|
export JENKINS_PASSWORD="${JENKINS_PASSWORD:?JENKINS_PASSWORD is required}"
|
||||||
|
export JJB_PATH="${JJB_PATH:?JJB_PATH is required}"
|
||||||
|
|
||||||
|
cat > /tmp/jenkins-job-builder.ini <<EOF
|
||||||
|
[job_builder]
|
||||||
|
ignore_cache=True
|
||||||
|
keep_descriptions=True
|
||||||
|
recursive=True
|
||||||
|
|
||||||
|
[jenkins]
|
||||||
|
url=${JENKINS_HOSTNAME}
|
||||||
|
user=${JENKINS_USERNAME}
|
||||||
|
password=${JENKINS_PASSWORD}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "Waiting for Jenkins at ${JENKINS_HOSTNAME}..."
|
||||||
|
until curl -fsS "${JENKINS_HOSTNAME}/login" >/dev/null; do
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Uploading jobs from ${JJB_PATH}..."
|
||||||
|
jenkins-jobs --conf /tmp/jenkins-job-builder.ini --flush-cache update "${JJB_PATH}"
|
||||||
|
|
||||||
|
echo "Jobs upload completed."
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://jenkins:8080;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-Port $server_port;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REGISTRY="${1:-localhost:5005}"
|
||||||
|
TAG="${2:-1.0.0}"
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
RETRIES="${RETRIES:-6}"
|
||||||
|
RETRY_DELAY_SEC="${RETRY_DELAY_SEC:-15}"
|
||||||
|
|
||||||
|
retry_cmd() {
|
||||||
|
local attempt=1
|
||||||
|
local max_attempts="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
until "$@"; do
|
||||||
|
if [ "${attempt}" -ge "${max_attempts}" ]; then
|
||||||
|
echo "Command failed after ${attempt} attempts: $*"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
echo "Attempt ${attempt}/${max_attempts} failed: $*"
|
||||||
|
attempt=$((attempt + 1))
|
||||||
|
sleep "${RETRY_DELAY_SEC}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
prepull_base_images() {
|
||||||
|
local images=(
|
||||||
|
"eclipse-temurin:21-jre"
|
||||||
|
"maven:3.9.11-eclipse-temurin-21"
|
||||||
|
"python:3.12-slim"
|
||||||
|
)
|
||||||
|
for image in "${images[@]}"; do
|
||||||
|
echo "Pulling base image ${image}"
|
||||||
|
retry_cmd "${RETRIES}" docker pull "${image}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
build_and_push() {
|
||||||
|
local image_name="$1"
|
||||||
|
local context_dir="$2"
|
||||||
|
local full_image="${REGISTRY}/${image_name}:${TAG}"
|
||||||
|
echo "Building ${full_image}"
|
||||||
|
retry_cmd "${RETRIES}" docker build -t "${full_image}" "${context_dir}"
|
||||||
|
echo "Pushing ${full_image}"
|
||||||
|
retry_cmd "${RETRIES}" docker push "${full_image}"
|
||||||
|
}
|
||||||
|
|
||||||
|
prepull_base_images
|
||||||
|
build_and_push "otus/slave-maven" "${ROOT_DIR}/images/slave-maven"
|
||||||
|
build_and_push "otus/slave-jjb" "${ROOT_DIR}/images/slave-jjb"
|
||||||
|
build_and_push "otus/test-selenium" "${ROOT_DIR}/images/test-selenium"
|
||||||
|
build_and_push "otus/test-api" "${ROOT_DIR}/images/test-api"
|
||||||
|
build_and_push "otus/test-mobile" "${ROOT_DIR}/images/test-mobile"
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
services:
|
||||||
|
wiremock:
|
||||||
|
image: wiremock/wiremock:3.9.1
|
||||||
|
volumes:
|
||||||
|
- ${PROJECT_DIR}/wiremock:/home/wiremock
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/__admin/health | grep -q 'healthy'"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 12
|
||||||
|
|
||||||
|
android-emulator-1:
|
||||||
|
image: budtmo/docker-android:emulator_13.0
|
||||||
|
depends_on:
|
||||||
|
- wiremock
|
||||||
|
devices:
|
||||||
|
- /dev/kvm:/dev/kvm
|
||||||
|
environment:
|
||||||
|
- DEVICE=Pixel_5
|
||||||
|
- APPIUM=true
|
||||||
|
- WEB_VNC=true
|
||||||
|
- ENABLE_VNC=true
|
||||||
|
- AUTO_GRANT_PERMISSIONS=true
|
||||||
|
- EMULATOR_PARAMS=-no-window -no-audio -gpu swiftshader_indirect -no-snapshot -no-boot-anim
|
||||||
|
shm_size: 2gb
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "[ \"$(cat /home/androidusr/device_status 2>/dev/null)\" = \"READY\" ]"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 40
|
||||||
|
start_period: 30s
|
||||||
|
|
||||||
|
android-emulator-2:
|
||||||
|
image: budtmo/docker-android:emulator_12.0
|
||||||
|
depends_on:
|
||||||
|
- wiremock
|
||||||
|
devices:
|
||||||
|
- /dev/kvm:/dev/kvm
|
||||||
|
environment:
|
||||||
|
- DEVICE=Pixel_4
|
||||||
|
- APPIUM=true
|
||||||
|
- WEB_VNC=true
|
||||||
|
- ENABLE_VNC=true
|
||||||
|
- AUTO_GRANT_PERMISSIONS=true
|
||||||
|
- EMULATOR_PARAMS=-no-window -no-audio -gpu swiftshader_indirect -no-snapshot -no-boot-anim
|
||||||
|
shm_size: 2gb
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "[ \"$(cat /home/androidusr/device_status 2>/dev/null)\" = \"READY\" ]"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 40
|
||||||
|
start_period: 30s
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- defaults:
|
||||||
|
name: global
|
||||||
|
project_folder: /workspace/otus-autotests
|
||||||
|
test_image_tag: "1.0.0"
|
||||||
|
build_keep: 40
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- property:
|
||||||
|
name: hw8-build-policy
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 40
|
||||||
|
- disable-concurrent-builds
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'jjb' }
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Check Docker & Registry') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
docker version
|
||||||
|
curl -fsS http://registry:5000/v2/_catalog
|
||||||
|
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
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check Sources') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
test -f /workspace/otus-autotests/homework_4/pom.xml
|
||||||
|
test -f /workspace/otus-autotests/hw7/pom.xml
|
||||||
|
test -f /workspace/otus-autotests/hw8/config/jobs/global.yaml
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'jjb' }
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Validate Input') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
test -d "${JJB_PATH}"
|
||||||
|
test -f "${JJB_PATH}/global.yaml"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy Jobs') {
|
||||||
|
steps {
|
||||||
|
withCredentials([usernamePassword(
|
||||||
|
credentialsId: 'jenkins-admin-userpass',
|
||||||
|
usernameVariable: 'J_USER',
|
||||||
|
passwordVariable: 'J_PASS'
|
||||||
|
)]) {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
cat > /tmp/jenkins-job-builder.ini <<EOF
|
||||||
|
[job_builder]
|
||||||
|
ignore_cache=True
|
||||||
|
keep_descriptions=True
|
||||||
|
recursive=True
|
||||||
|
|
||||||
|
[jenkins]
|
||||||
|
url=${JENKINS_URL_INTERNAL}
|
||||||
|
user=${J_USER}
|
||||||
|
password=${J_PASS}
|
||||||
|
EOF
|
||||||
|
jenkins-jobs --conf /tmp/jenkins-job-builder.ini --flush-cache update "${JJB_PATH}"
|
||||||
|
rm -f /tmp/jenkins-job-builder.ini
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'maven' }
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Run API Tests In Docker') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
rm -rf ./artifacts
|
||||||
|
mkdir -p ./artifacts
|
||||||
|
|
||||||
|
CID="$(docker create localhost:5005/otus/test-api:1.0.0 bash -lc "set -e; cd /workspace; mvn -f citrus-tests/pom.xml test")"
|
||||||
|
cleanup_container() {
|
||||||
|
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup_container EXIT INT TERM
|
||||||
|
tar -C /workspace/otus-autotests/homework_4 -cf - . | docker cp - "${CID}:/workspace"
|
||||||
|
set +e
|
||||||
|
docker start -a "${CID}"
|
||||||
|
TEST_RC=$?
|
||||||
|
docker cp "${CID}:/workspace/citrus-tests/target" "./artifacts/citrus-target" || true
|
||||||
|
trap - EXIT INT TERM
|
||||||
|
docker rm -f "${CID}" || true
|
||||||
|
exit "${TEST_RC}"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
junit allowEmptyResults: true, testResults: 'artifacts/citrus-target/surefire-reports/*.xml'
|
||||||
|
archiveArtifacts allowEmptyArchive: true, artifacts: 'artifacts/citrus-target/**'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'maven' }
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Prepare Workspace') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
rm -rf ./project
|
||||||
|
mkdir -p ./project
|
||||||
|
cp -a /workspace/otus-autotests/hw7/. ./project/
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Start Mobile Environment') {
|
||||||
|
steps {
|
||||||
|
dir('project') {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
COMPOSE_FILE="/workspace/otus-autotests/hw8/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
|
||||||
|
compose_cmd() { PROJECT_DIR="$PWD" docker-compose -f "${COMPOSE_FILE}" "$@"; }
|
||||||
|
else
|
||||||
|
echo "Neither docker compose plugin nor docker-compose binary is available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
export COMPOSE_PROJECT_NAME=mobileci
|
||||||
|
compose_cmd down -v --remove-orphans || true
|
||||||
|
compose_cmd up -d wiremock android-emulator-1 android-emulator-2
|
||||||
|
|
||||||
|
EMULATORS=""
|
||||||
|
for service in android-emulator-1 android-emulator-2; do
|
||||||
|
cid="$(compose_cmd ps -q "$service")"
|
||||||
|
if [ -z "${cid}" ]; then
|
||||||
|
echo "Container for ${service} not found, skipping"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
healthy="false"
|
||||||
|
for i in $(seq 1 80); do
|
||||||
|
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}starting{{end}}' "${cid}" || true)"
|
||||||
|
if [ "${status}" = "healthy" ]; then
|
||||||
|
healthy="true"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${healthy}" = "true" ]; then
|
||||||
|
if [ -n "${EMULATORS}" ]; then
|
||||||
|
EMULATORS="${EMULATORS},"
|
||||||
|
fi
|
||||||
|
EMULATORS="${EMULATORS}${service}|http://${service}:4723|Android Emulator|${APP_URL}"
|
||||||
|
else
|
||||||
|
echo "Service ${service} is not healthy in time, excluding from test run"
|
||||||
|
docker logs "${cid}" || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${EMULATORS}" ]; then
|
||||||
|
echo "No healthy emulators available"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s' "${EMULATORS}" > .mobile_emulators.txt
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Run Appium Tests In Docker') {
|
||||||
|
steps {
|
||||||
|
dir('project') {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
rm -rf ./target
|
||||||
|
MOBILE_EMULATORS_VALUE="$(cat ./.mobile_emulators.txt)"
|
||||||
|
DB_PASSWORD_EFFECTIVE="${DB_PASSWORD:-${MOBILE_DB_PASSWORD:-}}"
|
||||||
|
if [ -z "${DB_PASSWORD_EFFECTIVE}" ]; then
|
||||||
|
echo "DB password is not set. Provide DB_PASSWORD job parameter or MOBILE_DB_PASSWORD env variable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
CID="$(docker create \
|
||||||
|
--network mobileci_default \
|
||||||
|
-e DB_URL="${DB_URL:-}" \
|
||||||
|
-e DB_USER="${DB_USER:-}" \
|
||||||
|
-e DB_PASSWORD="${DB_PASSWORD_EFFECTIVE}" \
|
||||||
|
-e APP_URL="${APP_URL:-}" \
|
||||||
|
-e MOBILE_EMULATORS="${MOBILE_EMULATORS_VALUE}" \
|
||||||
|
-e WISHLISTS_USERNAME="${WISHLISTS_USERNAME:-}" \
|
||||||
|
-e WISHLISTS_PASSWORD="${WISHLISTS_PASSWORD:-}" \
|
||||||
|
-e GIFTS_USERNAME="${GIFTS_USERNAME:-}" \
|
||||||
|
-e GIFTS_PASSWORD="${GIFTS_PASSWORD:-}" \
|
||||||
|
-e RESERVATION_USERNAME="${RESERVATION_USERNAME:-}" \
|
||||||
|
-e RESERVATION_PASSWORD="${RESERVATION_PASSWORD:-}" \
|
||||||
|
-e RESERVATION_OWNER="${RESERVATION_OWNER:-}" \
|
||||||
|
localhost:5005/otus/test-mobile:1.0.0 \
|
||||||
|
bash -lc "set -e; cd /workspace; timeout 1800s mvn -Dallure.results.directory=target/allure-results test")"
|
||||||
|
cleanup_container() {
|
||||||
|
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup_container EXIT INT TERM
|
||||||
|
tar -C "$PWD" -cf - . | docker cp - "${CID}:/workspace"
|
||||||
|
set +e
|
||||||
|
docker start -a "${CID}"
|
||||||
|
TEST_RC=$?
|
||||||
|
docker cp "${CID}:/workspace/target" "./target" || true
|
||||||
|
trap - EXIT INT TERM
|
||||||
|
docker rm -f "${CID}" || true
|
||||||
|
exit "${TEST_RC}"
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
dir('project') {
|
||||||
|
sh '''
|
||||||
|
set +e
|
||||||
|
COMPOSE_FILE="/workspace/otus-autotests/hw8/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
|
||||||
|
compose_cmd() { PROJECT_DIR="$PWD" docker-compose -f "${COMPOSE_FILE}" "$@"; }
|
||||||
|
else
|
||||||
|
echo "compose command is not available for cleanup"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
export COMPOSE_PROJECT_NAME=mobileci
|
||||||
|
compose_cmd down -v --remove-orphans || true
|
||||||
|
'''
|
||||||
|
}
|
||||||
|
script {
|
||||||
|
try {
|
||||||
|
allure commandline: 'allure', includeProperties: false, jdk: '', results: [[path: 'project/target/allure-results']]
|
||||||
|
} catch (Exception ex) {
|
||||||
|
echo "Allure publisher unavailable: ${ex.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
junit allowEmptyResults: true, testResults: 'project/target/surefire-reports/*.xml'
|
||||||
|
archiveArtifacts allowEmptyArchive: true, artifacts: 'project/target/**'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'jjb' }
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Run Jobs In Parallel') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
def fanout = [:]
|
||||||
|
|
||||||
|
fanout['selenium'] = {
|
||||||
|
build job: 'qa-selenium-tests',
|
||||||
|
wait: true,
|
||||||
|
propagate: true,
|
||||||
|
parameters: [
|
||||||
|
string(name: 'BROWSER', value: params.BROWSER),
|
||||||
|
string(name: 'BASE_URL', value: params.BASE_URL),
|
||||||
|
string(name: 'EXECUTION_MODE', value: params.EXECUTION_MODE),
|
||||||
|
string(name: 'SELENOID_URL', value: params.SELENOID_URL),
|
||||||
|
string(name: 'HEADLESS', value: params.HEADLESS)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fanout['mobile'] = {
|
||||||
|
build job: 'qa-mobile-appium-tests',
|
||||||
|
wait: true,
|
||||||
|
propagate: true,
|
||||||
|
parameters: [
|
||||||
|
string(name: 'APP_URL', value: params.APP_URL),
|
||||||
|
string(name: 'DB_URL', value: params.DB_URL),
|
||||||
|
string(name: 'DB_USER', value: params.DB_USER),
|
||||||
|
string(name: 'DB_PASSWORD', value: params.DB_PASSWORD),
|
||||||
|
string(name: 'WISHLISTS_USERNAME', value: params.WISHLISTS_USERNAME),
|
||||||
|
string(name: 'WISHLISTS_PASSWORD', value: params.WISHLISTS_PASSWORD),
|
||||||
|
string(name: 'GIFTS_USERNAME', value: params.GIFTS_USERNAME),
|
||||||
|
string(name: 'GIFTS_PASSWORD', value: params.GIFTS_PASSWORD),
|
||||||
|
string(name: 'RESERVATION_USERNAME', value: params.RESERVATION_USERNAME),
|
||||||
|
string(name: 'RESERVATION_PASSWORD', value: params.RESERVATION_PASSWORD),
|
||||||
|
string(name: 'RESERVATION_OWNER', value: params.RESERVATION_OWNER)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fanout['api'] = {
|
||||||
|
build job: 'qa-api-citrus-tests', wait: true, propagate: true
|
||||||
|
}
|
||||||
|
|
||||||
|
parallel fanout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
pipeline {
|
||||||
|
agent { label 'maven' }
|
||||||
|
options {
|
||||||
|
timestamps()
|
||||||
|
ansiColor('xterm')
|
||||||
|
}
|
||||||
|
stages {
|
||||||
|
stage('Run Selenium Tests In Docker') {
|
||||||
|
steps {
|
||||||
|
sh '''
|
||||||
|
set -eux
|
||||||
|
rm -rf ./artifacts
|
||||||
|
mkdir -p ./artifacts
|
||||||
|
|
||||||
|
EXTRA_ARGS=""
|
||||||
|
if [ "${BROWSER}" = "chrome" ]; then
|
||||||
|
EXTRA_ARGS="-Dchrome.binary=/usr/bin/chromium"
|
||||||
|
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 -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")"
|
||||||
|
cleanup_container() {
|
||||||
|
docker rm -f "${CID}" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup_container EXIT INT TERM
|
||||||
|
tar -C /workspace/otus-autotests/homework_4 -cf - . | docker cp - "${CID}:/workspace"
|
||||||
|
set +e
|
||||||
|
docker start -a "${CID}"
|
||||||
|
TEST_RC=$?
|
||||||
|
docker cp "${CID}:/workspace/target" "./artifacts/target" || true
|
||||||
|
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/target/**'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- job:
|
||||||
|
name: infra-health-check
|
||||||
|
description: "Проверка инфраструктуры Jenkins/Registry/Agent образов."
|
||||||
|
project-type: pipeline
|
||||||
|
concurrent: false
|
||||||
|
sandbox: true
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 30
|
||||||
|
dsl: !include-raw-verbatim: ../scripts/infra-health-check.groovy
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
- job:
|
||||||
|
name: jobs-uploader
|
||||||
|
description: "Обновляет Jenkins jobs из JJB YAML (config/jobs)."
|
||||||
|
project-type: pipeline
|
||||||
|
concurrent: false
|
||||||
|
sandbox: true
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 30
|
||||||
|
parameters:
|
||||||
|
- string:
|
||||||
|
name: JJB_PATH
|
||||||
|
default: /workspace/otus-autotests/hw8/config/jobs
|
||||||
|
description: "Путь до JJB-конфигов"
|
||||||
|
dsl: !include-raw-verbatim: ../scripts/jobs-uploader.groovy
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- job:
|
||||||
|
name: qa-api-citrus-tests
|
||||||
|
description: "API тесты модуля citrus-tests (homework_4)."
|
||||||
|
project-type: pipeline
|
||||||
|
concurrent: true
|
||||||
|
sandbox: true
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 50
|
||||||
|
dsl: !include-raw-verbatim: ../scripts/qa-api-citrus-tests.groovy
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
- job:
|
||||||
|
name: qa-mobile-appium-tests
|
||||||
|
description: "Appium mobile тесты (hw7) с автоскачиванием APK через APP_URL."
|
||||||
|
project-type: pipeline
|
||||||
|
concurrent: true
|
||||||
|
sandbox: true
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 30
|
||||||
|
parameters:
|
||||||
|
- string:
|
||||||
|
name: APP_URL
|
||||||
|
default: http://wiremock:8080/wishlist.apk
|
||||||
|
description: "URL APK для автоскачивания (capability app)"
|
||||||
|
- string:
|
||||||
|
name: DB_URL
|
||||||
|
default: jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist
|
||||||
|
description: "JDBC URL"
|
||||||
|
- string:
|
||||||
|
name: DB_USER
|
||||||
|
default: student
|
||||||
|
description: "DB user"
|
||||||
|
- string:
|
||||||
|
name: DB_PASSWORD
|
||||||
|
default: "student"
|
||||||
|
description: "DB password"
|
||||||
|
- string:
|
||||||
|
name: WISHLISTS_USERNAME
|
||||||
|
default: user1us
|
||||||
|
- string:
|
||||||
|
name: WISHLISTS_PASSWORD
|
||||||
|
default: user1us
|
||||||
|
- string:
|
||||||
|
name: GIFTS_USERNAME
|
||||||
|
default: user2us
|
||||||
|
- string:
|
||||||
|
name: GIFTS_PASSWORD
|
||||||
|
default: user2us
|
||||||
|
- string:
|
||||||
|
name: RESERVATION_USERNAME
|
||||||
|
default: user3us
|
||||||
|
- string:
|
||||||
|
name: RESERVATION_PASSWORD
|
||||||
|
default: user3us
|
||||||
|
- string:
|
||||||
|
name: RESERVATION_OWNER
|
||||||
|
default: user4us
|
||||||
|
dsl: !include-raw-verbatim: ../scripts/qa-mobile-appium-tests.groovy
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
- job:
|
||||||
|
name: qa-runner
|
||||||
|
description: "Оркестратор: запускает тестовые job параллельно."
|
||||||
|
project-type: pipeline
|
||||||
|
concurrent: true
|
||||||
|
sandbox: true
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 50
|
||||||
|
parameters:
|
||||||
|
- choice:
|
||||||
|
name: BROWSER
|
||||||
|
choices:
|
||||||
|
- chrome
|
||||||
|
- firefox
|
||||||
|
- string:
|
||||||
|
name: BASE_URL
|
||||||
|
default: https://otus.ru
|
||||||
|
- string:
|
||||||
|
name: EXECUTION_MODE
|
||||||
|
default: selenoid
|
||||||
|
- string:
|
||||||
|
name: SELENOID_URL
|
||||||
|
default: http://host.docker.internal:4444/wd/hub
|
||||||
|
- choice:
|
||||||
|
name: HEADLESS
|
||||||
|
choices:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
|
- string:
|
||||||
|
name: APP_URL
|
||||||
|
default: http://wiremock:8080/wishlist.apk
|
||||||
|
- string:
|
||||||
|
name: DB_URL
|
||||||
|
default: jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist
|
||||||
|
- string:
|
||||||
|
name: DB_USER
|
||||||
|
default: student
|
||||||
|
- string:
|
||||||
|
name: DB_PASSWORD
|
||||||
|
default: "student"
|
||||||
|
- string:
|
||||||
|
name: WISHLISTS_USERNAME
|
||||||
|
default: user1us
|
||||||
|
- string:
|
||||||
|
name: WISHLISTS_PASSWORD
|
||||||
|
default: user1us
|
||||||
|
- string:
|
||||||
|
name: GIFTS_USERNAME
|
||||||
|
default: user2us
|
||||||
|
- string:
|
||||||
|
name: GIFTS_PASSWORD
|
||||||
|
default: user2us
|
||||||
|
- string:
|
||||||
|
name: RESERVATION_USERNAME
|
||||||
|
default: user3us
|
||||||
|
- string:
|
||||||
|
name: RESERVATION_PASSWORD
|
||||||
|
default: user3us
|
||||||
|
- string:
|
||||||
|
name: RESERVATION_OWNER
|
||||||
|
default: user4us
|
||||||
|
dsl: !include-raw-verbatim: ../scripts/qa-runner.groovy
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
- job:
|
||||||
|
name: qa-selenium-tests
|
||||||
|
description: "Selenium/Selenide тесты (homework_4) с выбором браузера."
|
||||||
|
project-type: pipeline
|
||||||
|
concurrent: true
|
||||||
|
sandbox: true
|
||||||
|
properties:
|
||||||
|
- build-discarder:
|
||||||
|
num-to-keep: 50
|
||||||
|
parameters:
|
||||||
|
- choice:
|
||||||
|
name: BROWSER
|
||||||
|
choices:
|
||||||
|
- chrome
|
||||||
|
- firefox
|
||||||
|
description: "Браузер для запуска UI-тестов"
|
||||||
|
- string:
|
||||||
|
name: BASE_URL
|
||||||
|
default: https://otus.ru
|
||||||
|
description: "Базовый URL тестируемого сайта"
|
||||||
|
- string:
|
||||||
|
name: EXECUTION_MODE
|
||||||
|
default: selenoid
|
||||||
|
description: "Режим запуска (local|selenoid)"
|
||||||
|
- string:
|
||||||
|
name: SELENOID_URL
|
||||||
|
default: http://host.docker.internal:4444/wd/hub
|
||||||
|
description: "URL Selenoid (используется при EXECUTION_MODE=selenoid)"
|
||||||
|
- choice:
|
||||||
|
name: HEADLESS
|
||||||
|
choices:
|
||||||
|
- "true"
|
||||||
|
- "false"
|
||||||
|
description: "Headless режим"
|
||||||
|
dsl: !include-raw-verbatim: ../scripts/qa-selenium-tests.groovy
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
- view:
|
||||||
|
name: DevOps
|
||||||
|
view-type: list
|
||||||
|
description: "Инфраструктурные job"
|
||||||
|
filter-executors: true
|
||||||
|
filter-queue: true
|
||||||
|
job-name:
|
||||||
|
- jobs-uploader
|
||||||
|
- infra-health-check
|
||||||
|
columns:
|
||||||
|
- status
|
||||||
|
- weather
|
||||||
|
- job
|
||||||
|
- last-success
|
||||||
|
- last-failure
|
||||||
|
- last-duration
|
||||||
|
- build-button
|
||||||
|
|
||||||
|
- view:
|
||||||
|
name: QA
|
||||||
|
view-type: list
|
||||||
|
description: "Тестовые job и раннер"
|
||||||
|
filter-executors: true
|
||||||
|
filter-queue: true
|
||||||
|
job-name:
|
||||||
|
- qa-runner
|
||||||
|
- qa-selenium-tests
|
||||||
|
- qa-api-citrus-tests
|
||||||
|
- qa-mobile-appium-tests
|
||||||
|
columns:
|
||||||
|
- status
|
||||||
|
- weather
|
||||||
|
- job
|
||||||
|
- last-success
|
||||||
|
- last-failure
|
||||||
|
- last-duration
|
||||||
|
- build-button
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
services:
|
|
||||||
docker:
|
|
||||||
image: docker:27-dind
|
|
||||||
privileged: true
|
|
||||||
environment:
|
|
||||||
- DOCKER_TLS_CERTDIR=
|
|
||||||
command:
|
|
||||||
- --host=tcp://0.0.0.0:2375
|
|
||||||
- --tls=false
|
|
||||||
volumes:
|
|
||||||
- ./jenkins_home:/var/jenkins_home
|
|
||||||
|
|
||||||
jenkins:
|
|
||||||
build: .
|
|
||||||
depends_on:
|
|
||||||
- docker
|
|
||||||
ports:
|
|
||||||
- "8081:8080"
|
|
||||||
- "50000:50000"
|
|
||||||
environment:
|
|
||||||
- JAVA_OPTS=-Djenkins.install.runSetupWizard=false
|
|
||||||
- DOCKER_HOST=tcp://docker:2375
|
|
||||||
user: root
|
|
||||||
volumes:
|
|
||||||
- ./jenkins_home:/var/jenkins_home
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import jenkins.model.Jenkins
|
|
||||||
import hudson.security.HudsonPrivateSecurityRealm
|
|
||||||
import hudson.security.FullControlOnceLoggedInAuthorizationStrategy
|
|
||||||
|
|
||||||
def jenkins = Jenkins.instance
|
|
||||||
def realm = new HudsonPrivateSecurityRealm(false)
|
|
||||||
if (realm.getAllUsers().isEmpty()) {
|
|
||||||
realm.createAccount("admin", "admin")
|
|
||||||
}
|
|
||||||
jenkins.setSecurityRealm(realm)
|
|
||||||
|
|
||||||
def strategy = new FullControlOnceLoggedInAuthorizationStrategy()
|
|
||||||
strategy.setAllowAnonymousRead(true)
|
|
||||||
jenkins.setAuthorizationStrategy(strategy)
|
|
||||||
|
|
||||||
jenkins.save()
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import jenkins.model.Jenkins
|
|
||||||
|
|
||||||
def jenkins = Jenkins.instance
|
|
||||||
def xmlDir = new File(jenkins.root, "job-xml")
|
|
||||||
if (!xmlDir.exists()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlDir.eachFileMatch(~/.*\.xml/) { file ->
|
|
||||||
def jobName = file.name.replaceFirst(/\.xml$/, "")
|
|
||||||
def existing = jenkins.getItem(jobName)
|
|
||||||
if (existing != null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
def fis = new FileInputStream(file)
|
|
||||||
try {
|
|
||||||
jenkins.createProjectFromXML(jobName, fis)
|
|
||||||
println "Created job: ${jobName}"
|
|
||||||
} finally {
|
|
||||||
fis.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jenkins.save()
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import jenkins.model.Jenkins
|
|
||||||
import ru.yandex.qatools.allure.jenkins.tools.AllureCommandlineInstallation
|
|
||||||
import ru.yandex.qatools.allure.jenkins.tools.AllureCommandlineInstaller
|
|
||||||
import hudson.tools.InstallSourceProperty
|
|
||||||
|
|
||||||
def jenkins = Jenkins.instance
|
|
||||||
def desc = jenkins.getDescriptorByType(AllureCommandlineInstallation.DescriptorImpl)
|
|
||||||
def existing = desc.getInstallations()
|
|
||||||
|
|
||||||
def alreadyConfigured = existing.any { it.name == "allure" }
|
|
||||||
if (!alreadyConfigured) {
|
|
||||||
def installer = new AllureCommandlineInstaller("2.29.0")
|
|
||||||
def prop = new InstallSourceProperty([installer])
|
|
||||||
def installation = new AllureCommandlineInstallation("allure", "", [prop])
|
|
||||||
desc.setInstallations(installation)
|
|
||||||
desc.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
<?xml version='1.1' encoding='UTF-8'?>
|
|
||||||
<flow-definition plugin="workflow-job">
|
|
||||||
<description>Appium mobile tests with APK download and Allure report.</description>
|
|
||||||
<keepDependencies>false</keepDependencies>
|
|
||||||
<properties>
|
|
||||||
<hudson.model.ParametersDefinitionProperty>
|
|
||||||
<parameterDefinitions>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>REPO_URL</name>
|
|
||||||
<description>Git repo with Appium tests (hw7)</description>
|
|
||||||
<defaultValue>https://git.kovbasa.ru/otus-autotests/homework_7.git</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>BRANCH</name>
|
|
||||||
<description>Git branch</description>
|
|
||||||
<defaultValue>master</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>APP_URL</name>
|
|
||||||
<description>APK URL (optional). If empty, repo APK will be used.</description>
|
|
||||||
<defaultValue></defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>DB_URL</name>
|
|
||||||
<description>JDBC url</description>
|
|
||||||
<defaultValue>jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>DB_USER</name>
|
|
||||||
<description>DB user</description>
|
|
||||||
<defaultValue>student</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>APPIUM_URL</name>
|
|
||||||
<description>Appium server URL</description>
|
|
||||||
<defaultValue>http://docker:4723</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.PasswordParameterDefinition>
|
|
||||||
<name>DB_PASSWORD</name>
|
|
||||||
<description>DB password</description>
|
|
||||||
</hudson.model.PasswordParameterDefinition>
|
|
||||||
</parameterDefinitions>
|
|
||||||
</hudson.model.ParametersDefinitionProperty>
|
|
||||||
</properties>
|
|
||||||
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps">
|
|
||||||
<script><![CDATA[
|
|
||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
triggers {
|
|
||||||
pollSCM('H/5 * * * *')
|
|
||||||
}
|
|
||||||
parameters {
|
|
||||||
string(name: 'REPO_URL', defaultValue: 'https://git.kovbasa.ru/otus-autotests/homework_7.git', description: 'Git repo with Appium tests (hw7)')
|
|
||||||
string(name: 'BRANCH', defaultValue: 'master', description: 'Git branch')
|
|
||||||
string(name: 'APP_URL', defaultValue: '', description: 'APK URL (optional)')
|
|
||||||
string(name: 'DB_URL', defaultValue: 'jdbc:postgresql://sql.otus.kartushin.su:5432/wishlist', description: 'JDBC url')
|
|
||||||
string(name: 'DB_USER', defaultValue: 'student', description: 'DB user')
|
|
||||||
string(name: 'APPIUM_URL', defaultValue: 'http://docker:4723', description: 'Appium server URL')
|
|
||||||
password(name: 'DB_PASSWORD', defaultValue: '', description: 'DB password')
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('Checkout') {
|
|
||||||
steps {
|
|
||||||
git branch: params.BRANCH, url: params.REPO_URL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Docker Compose') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
if (isUnix()) {
|
|
||||||
def composeCmd = 'docker compose'
|
|
||||||
if (sh(script: 'command -v docker-compose >/dev/null 2>&1', returnStatus: true) == 0) {
|
|
||||||
composeCmd = 'docker-compose'
|
|
||||||
}
|
|
||||||
if (params.APP_URL?.trim()) {
|
|
||||||
sh "APP_URL='${params.APP_URL}' ${composeCmd} up -d"
|
|
||||||
} else {
|
|
||||||
sh "${composeCmd} up -d"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
def composeCmd = 'docker compose'
|
|
||||||
if (bat(script: 'where docker-compose', returnStatus: true) == 0) {
|
|
||||||
composeCmd = 'docker-compose'
|
|
||||||
}
|
|
||||||
if (params.APP_URL?.trim()) {
|
|
||||||
bat "set APP_URL=${params.APP_URL}&& ${composeCmd} up -d"
|
|
||||||
} else {
|
|
||||||
bat "${composeCmd} up -d"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Test') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
def mvn = isUnix() ? 'mvn' : 'mvn.cmd'
|
|
||||||
if (isUnix()) {
|
|
||||||
sh "DB_URL='${params.DB_URL}' DB_USER='${params.DB_USER}' DB_PASSWORD='${params.DB_PASSWORD}' ${mvn} -Dappium.url=${params.APPIUM_URL} -Dallure.results.directory=target/allure-results test"
|
|
||||||
} else {
|
|
||||||
bat "set DB_URL=${params.DB_URL}&& set DB_USER=${params.DB_USER}&& set DB_PASSWORD=${params.DB_PASSWORD}&& ${mvn} -Dappium.url=${params.APPIUM_URL} -Dallure.results.directory=target/allure-results test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
allure includeProperties: false, jdk: '', results: [[path: 'target/allure-results']]
|
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'target/**'
|
|
||||||
script {
|
|
||||||
if (isUnix()) {
|
|
||||||
def composeCmd = 'docker compose'
|
|
||||||
if (sh(script: 'command -v docker-compose >/dev/null 2>&1', returnStatus: true) == 0) {
|
|
||||||
composeCmd = 'docker-compose'
|
|
||||||
}
|
|
||||||
sh "${composeCmd} down -v || true"
|
|
||||||
} else {
|
|
||||||
def composeCmd = 'docker compose'
|
|
||||||
if (bat(script: 'where docker-compose', returnStatus: true) == 0) {
|
|
||||||
composeCmd = 'docker-compose'
|
|
||||||
}
|
|
||||||
bat "${composeCmd} down -v"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]]></script>
|
|
||||||
<sandbox>true</sandbox>
|
|
||||||
</definition>
|
|
||||||
<triggers/>
|
|
||||||
<disabled>false</disabled>
|
|
||||||
</flow-definition>
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
<?xml version='1.1' encoding='UTF-8'?>
|
|
||||||
<flow-definition plugin="workflow-job">
|
|
||||||
<description>Selenium/Selenide tests with browser parameter and Allure report.</description>
|
|
||||||
<keepDependencies>false</keepDependencies>
|
|
||||||
<properties>
|
|
||||||
<hudson.model.ParametersDefinitionProperty>
|
|
||||||
<parameterDefinitions>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>REPO_URL</name>
|
|
||||||
<description>Git repo with Selenium/Selenide tests</description>
|
|
||||||
<defaultValue>https://git.kovbasa.ru/otus-autotests/homework_4.git</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>BRANCH</name>
|
|
||||||
<description>Git branch</description>
|
|
||||||
<defaultValue>master</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.ChoiceParameterDefinition>
|
|
||||||
<name>BROWSER</name>
|
|
||||||
<description>Browser to run tests</description>
|
|
||||||
<choices class="java.util.Arrays$ArrayList">
|
|
||||||
<a class="string-array">
|
|
||||||
<string>chrome</string>
|
|
||||||
<string>firefox</string>
|
|
||||||
</a>
|
|
||||||
</choices>
|
|
||||||
</hudson.model.ChoiceParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>BROWSER_VERSION</name>
|
|
||||||
<description>Browser version (optional)</description>
|
|
||||||
<defaultValue></defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>EXECUTION_MODE</name>
|
|
||||||
<description>local or selenoid</description>
|
|
||||||
<defaultValue>local</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>SELENOID_URL</name>
|
|
||||||
<description>Selenoid URL (used when execution.mode=selenoid)</description>
|
|
||||||
<defaultValue>http://localhost/wd/hub</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.StringParameterDefinition>
|
|
||||||
<name>BASE_URL</name>
|
|
||||||
<description>Base URL for tests</description>
|
|
||||||
<defaultValue>https://otus.ru</defaultValue>
|
|
||||||
<trim>true</trim>
|
|
||||||
</hudson.model.StringParameterDefinition>
|
|
||||||
<hudson.model.ChoiceParameterDefinition>
|
|
||||||
<name>HEADLESS</name>
|
|
||||||
<description>Run browsers in headless mode</description>
|
|
||||||
<choices class="java.util.Arrays$ArrayList">
|
|
||||||
<a class="string-array">
|
|
||||||
<string>true</string>
|
|
||||||
<string>false</string>
|
|
||||||
</a>
|
|
||||||
</choices>
|
|
||||||
</hudson.model.ChoiceParameterDefinition>
|
|
||||||
</parameterDefinitions>
|
|
||||||
</hudson.model.ParametersDefinitionProperty>
|
|
||||||
</properties>
|
|
||||||
<definition class="org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition" plugin="workflow-cps">
|
|
||||||
<script><![CDATA[
|
|
||||||
pipeline {
|
|
||||||
agent any
|
|
||||||
triggers {
|
|
||||||
pollSCM('H/5 * * * *')
|
|
||||||
}
|
|
||||||
parameters {
|
|
||||||
string(name: 'REPO_URL', defaultValue: 'https://git.kovbasa.ru/otus-autotests/homework_4.git', description: 'Git repo with Selenium/Selenide tests')
|
|
||||||
string(name: 'BRANCH', defaultValue: 'master', description: 'Git branch')
|
|
||||||
choice(name: 'BROWSER', choices: ['chrome', 'firefox'], description: 'Browser to run tests')
|
|
||||||
string(name: 'BROWSER_VERSION', defaultValue: '', description: 'Browser version (optional)')
|
|
||||||
string(name: 'EXECUTION_MODE', defaultValue: 'local', description: 'local or selenoid')
|
|
||||||
string(name: 'SELENOID_URL', defaultValue: 'http://localhost/wd/hub', description: 'Selenoid URL')
|
|
||||||
string(name: 'BASE_URL', defaultValue: 'https://otus.ru', description: 'Base URL for tests')
|
|
||||||
choice(name: 'HEADLESS', choices: ['true', 'false'], description: 'Run browsers in headless mode')
|
|
||||||
}
|
|
||||||
stages {
|
|
||||||
stage('Checkout') {
|
|
||||||
steps {
|
|
||||||
git branch: params.BRANCH, url: params.REPO_URL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Test') {
|
|
||||||
steps {
|
|
||||||
script {
|
|
||||||
def mvn = isUnix() ? 'mvn' : 'mvn.cmd'
|
|
||||||
def extra = isUnix() ? " -Dchrome.binary=/usr/bin/chromium" : ""
|
|
||||||
def cmd = "${mvn} -Dexecution.mode=${params.EXECUTION_MODE} -Dbrowser=${params.BROWSER} -Dbrowser.version=${params.BROWSER_VERSION} -Dselenoid.url=${params.SELENOID_URL} -Dbase.url=${params.BASE_URL} -Dselenide.headless=${params.HEADLESS} -Dallure.results.directory=target/allure-results${extra} test"
|
|
||||||
if (isUnix()) {
|
|
||||||
sh cmd
|
|
||||||
} else {
|
|
||||||
bat cmd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
allure includeProperties: false, jdk: '', results: [[path: 'target/allure-results']]
|
|
||||||
archiveArtifacts allowEmptyArchive: true, artifacts: 'target/**'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]]></script>
|
|
||||||
<sandbox>true</sandbox>
|
|
||||||
</definition>
|
|
||||||
<triggers/>
|
|
||||||
<disabled>false</disabled>
|
|
||||||
</flow-definition>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
workflow-aggregator
|
|
||||||
git
|
|
||||||
allure-jenkins-plugin
|
|
||||||
docker-workflow
|
|
||||||
matrix-project
|
|
||||||
Reference in New Issue
Block a user