Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Модель запуска CI

Единая для всех тестовых workflow: `unit-test.yml`, `qa.yml`, `e2e.yml`, `e2e-client.yml`, `e2e-edt.yml`.

## Триггеры

| Событие | Когда | Зачем |
|---|---|---|
| `push` в `develop` / `master` / `release/**` | после мержа | пост-мердж прогон целевых веток |
| `pull_request_target` | открытие/обновление PR | валидация изменений PR |
| `workflow_dispatch` | вручную | перепрогон по требованию |

Ветки самого репозитория по `push` **не** прогоняются: разработка ведётся через PR, и триггер
`push` на все ветки давал двойной прогон (ветка + её PR).

## Почему `pull_request_target`, а не `pull_request`

Тестам нужны секреты — учётка портала 1С (`ONEC_USERNAME` / `ONEC_PASSWORD`) для установки
платформы и лицензии (`ONEC_LICENCE`, `ONEC_SERVER_LICENCE`). PR из форка при событии
`pull_request` секретов **не получает**, поэтому все джобы с установкой 1С в нём падают в
принципе — а `unit-test.yml`, где секретов не надо, до недавнего времени вообще висел в
`action_required`.

`pull_request_target` выполняется в контексте базовой ветки и секреты видит. Плата за это —
код PR туда не попадает автоматически: по умолчанию checkout берёт базовую ветку, из-за чего
раньше «E2E (сервер)» показывал зелёный, фактически прогоняя `develop`, а не PR.

Поэтому во всех тестовых workflow код PR забирается явно:

```yaml
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.number && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }}
allow-unsafe-pr-checkout: true
```

* `refs/pull/<N>/merge` — merge-коммит PR, то есть ровно то состояние, которое попадёт в
базовую ветку. Именно его и надо тестировать.
* `allow-unsafe-pr-checkout: true` — обязательный опт-ин `actions/checkout` (появился в
v4.4.0 / v7). Без него checkout отказывается брать код форка под `pull_request_target`.

## Подтверждения нет — это сознательное решение

Прогон PR из форка стартует сразу, без чьего-либо аппрува. Надо понимать, что это значит:
**любой автор PR выполняет свой код с секретами репозитория.**

Штатной защиты здесь нет. Встроенная настройка «Require approval for external
contributors» относится только к событию `pull_request` — GitHub Docs прямо пишут, что
workflow, запущенные по `pull_request_target`, «will always run, regardless of approval
settings». А `pull_request` форку секретов не отдаёт, поэтому тесты на нём не поднимаются.
Одновременно получить и кнопку, и секреты нельзя.

Ручные варианты гейта (environment с обязательным ревьюером, метка `ci:approved` с
`types: [labeled]`) пробовались и признаны слишком дорогими в ежедневной работе:
подтверждение требуется на каждый пуш в ветку PR, а окружение выдаёт его на прогон, не на
PR — то есть по клику на каждый workflow.

### Чем это ограничить

Тестам нужны только учётка портала 1С (`ONEC_USERNAME` / `ONEC_PASSWORD`), лицензии
(`ONEC_LICENCE`, `ONEC_SERVER_LICENCE`) и `SONARQUBE_TOKEN` / `SONARQUBE_HOST`. Всё
остальное в PR-прогоне лишнее, но как репозиторные секреты видно любой джобе:

* `OSHUB_TOKEN` — публикация пакета в hub.oscript.io;
* `TRIGGER_DOCS_DEPLOY_TOKEN` — запуск деплоя документации.

Это единственные секреты, утечка которых бьёт не по проекту, а по пользователям пакета.
Их стоит перенести в отдельный environment (например `release`) и указать его в
`release.yml` / `docs-deploy.yaml` — тогда прогон PR до них не дотянется вообще.

## Concurrency

Ключ группы включает `github.event_name`:

```yaml
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
```

Событие в ключе нужно из-за переходного периода. Прогон по `pull_request` читает workflow
**из ветки PR**, а не из базовой, поэтому у PR, открытых до перехода на
`pull_request_target`, старая версия файла с триггером `pull_request` продолжает
запускаться. Без `event_name` в ключе такой прогон попадает в ту же группу и отменяет наш
прогон по `pull_request_target`. Дубли уйдут сами, как только ветки PR подтянут `develop`.

Номер PR в ключе — вместо `github.ref_name`: под `pull_request_target` `ref_name` равен
базовой ветке, и все PR оказывались в одной группе, отменяя прогоны друг друга.

## Правки этих файлов внутри PR не действуют

`pull_request_target` всегда берёт версию workflow из **базовой** ветки. Любые изменения
в `.github/workflows/**`, приехавшие в PR, вступят в силу только после мержа в `develop`.
Проверять их удобно через `workflow_dispatch` на ветке.
24 changes: 20 additions & 4 deletions .github/workflows/e2e-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,29 @@ name: E2E тесты (клиент)
# Файловые/клиентские E2E-сценарии (tests/e2e/client-tests).
# Не требуют серверной инфраструктуры - только клиентскую платформу 1С.
# Прогоняются на матрице ОС × версий платформы × версий OneScript.
#
# Модель запуска (триггеры, забор кода PR, гейт для форков) описана в
# .github/workflows/README.md - она общая для всех тестовых workflow.

on:
push:
pull_request:
branches:
- develop
- master
- 'release/**'
pull_request_target:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
# event входит в ключ намеренно: у PR, открытых до перехода на pull_request_target,
# в ветке лежит старая версия этого workflow с триггером pull_request, а её прогон
# читается из ветки PR. Без event в группе он попадал бы в ту же группу и отменял
# наш прогон по pull_request_target.
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read
checks: write
pull-requests: write

Expand Down Expand Up @@ -49,8 +60,13 @@ jobs:
- name: Актуализация
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.ref }}
# pull_request_target выполняется в контексте базовой ветки, поэтому код PR
# забираем явно - merge-ref'ом, то есть тем, что реально попадёт в базовую ветку.
ref: ${{ github.event.pull_request.number && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }}
# Осознанный опт-ин: без него checkout откажется брать код форка под
# pull_request_target. Вход появился в actions/checkout 4.4.0, на которую
# сейчас указывает тег v4.
allow-unsafe-pr-checkout: true

- name: Установка свойств git
shell: bash
Expand Down
24 changes: 22 additions & 2 deletions .github/workflows/e2e-edt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ name: E2E тесты (EDT)
#
# Запускается вручную (workflow_dispatch) и по push/PR, затрагивающим EDT-код, тесты,
# фикстуры или сам workflow (полная установка EDT+платформы тяжёлая, поэтому не на каждый push).
#
# Модель запуска (триггеры, забор кода PR, гейт для форков) описана в
# .github/workflows/README.md - она общая для всех тестовых workflow.

on:
workflow_dispatch:
Expand All @@ -24,6 +27,10 @@ on:
required: false
default: '2025.2.6'
push:
branches:
- develop
- master
- 'release/**'
paths:
- '.github/workflows/e2e-edt.yml'
- 'tests/e2e/edt-tests/**'
Expand All @@ -38,7 +45,7 @@ on:
- 'src/cli/Cfe_Подкоманды/ПодкомандаCfeConvert.os'
- 'src/cli/Cfe_Подкоманды/ПодкомандаCfeDecompile.os'
- 'src/cli/НаборыОпций/НаборОпцийИсходников.os'
pull_request:
pull_request_target:
paths:
- '.github/workflows/e2e-edt.yml'
- 'tests/e2e/edt-tests/**'
Expand All @@ -55,10 +62,15 @@ on:
- 'src/cli/НаборыОпций/НаборОпцийИсходников.os'

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}
# event входит в ключ намеренно: у PR, открытых до перехода на pull_request_target,
# в ветке лежит старая версия этого workflow с триггером pull_request, а её прогон
# читается из ветки PR. Без event в группе он попадал бы в ту же группу и отменял
# наш прогон по pull_request_target.
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read
checks: write
pull-requests: write

Expand All @@ -79,6 +91,14 @@ jobs:

- name: Актуализация
uses: actions/checkout@v4
with:
# pull_request_target выполняется в контексте базовой ветки, поэтому код PR
# забираем явно - merge-ref'ом, то есть тем, что реально попадёт в базовую ветку.
ref: ${{ github.event.pull_request.number && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }}
# Осознанный опт-ин: без него checkout откажется брать код форка под
# pull_request_target. Вход появился в actions/checkout 4.4.0, на которую
# сейчас указывает тег v4.
allow-unsafe-pr-checkout: true

- name: Установка свойств git
shell: bash
Expand Down
30 changes: 27 additions & 3 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,34 @@ name: E2E тесты (кластер 1С)
#
# Файловые/клиентские E2E-сценарии выведены в отдельный workflow e2e-client.yml,
# где важна матрица ОС и версий платформы.
#
# Модель запуска (триггеры, забор кода PR, гейт для форков) описана в
# .github/workflows/README.md - она общая для всех тестовых workflow.

on:
workflow_dispatch:
push:
pull_request:
branches:
- develop
- master
- 'release/**'
pull_request_target:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
# Ключ - номер PR, а не github.ref_name: под pull_request_target ref_name равен базовой
# ветке, из-за чего все PR попадали в одну группу и отменяли прогоны друг друга.
#
# event входит в ключ намеренно: у PR, открытых до перехода на pull_request_target,
# в ветке лежит старая версия этого workflow с триггером pull_request, а её прогон
# читается из ветки PR. Без event в группе он попадал бы в ту же группу и отменял
# наш прогон по pull_request_target.
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

permissions:
contents: read
checks: write
pull-requests: write

jobs:
cluster-test:
Expand All @@ -46,6 +61,15 @@ jobs:
steps:
- name: Актуализация
uses: actions/checkout@v4
with:
# pull_request_target выполняется в контексте базовой ветки, поэтому код PR
# забираем явно - merge-ref'ом, то есть тем, что реально попадёт в базовую ветку.
# Без этого джоба прогоняла базовую ветку и давала ложно-зелёный результат на PR.
ref: ${{ github.event.pull_request.number && format('refs/pull/{0}/merge', github.event.pull_request.number) || '' }}
# Осознанный опт-ин: без него checkout откажется брать код форка под
# pull_request_target. Вход появился в actions/checkout 4.4.0, на которую
# сейчас указывает тег v4.
allow-unsafe-pr-checkout: true

- name: Запуск PostgreSQL (whitemanprk/postgres-1c:16)
# Образ использует стандартный postgres entrypoint. Кастомизация pg_hba.conf
Expand Down
Loading
Loading