Содержание
GitHub Actions на macOS может быть дорогим
Советы по экономии денег
По возможности используйте runner на базе Linux
Запуск задания одновременно
Избегайте выполнения рабочего процесса с помощью фильтров
Внимание: обязательные проверки статуса и пропуск рабочих процессов
В этом сообщении блога я делюсь советами о том, как наиболее эффективно использовать GitHub Actions и избежать лишнего времени выполнения и денежных затрат.
Я предполагаю, что вы знакомы с GitHub Actions и уже использовали их, но вот краткий обзор:
GitHub Actions — это встроенный инструментарий CI/CD, который позволяет определять рабочие процессы и задания, запускаемые при определенных событиях GitHub (например, push или pull_request) на вашем репозитории.
Вам необходимо определить тип машины, на которой выполняется задание. Это может быть: самостоятельный runner (раннер, например, локальный Mac Mini у вас дома) или раннер, размещенный на GitHub.
Существует несколько типов раннеров, размещенных на GitHub, но часто вам понадобится образ раннера на основе macOS.Так в чём проблема?
Раннер – программное обеспечение, предназначенное для автоматизации выполнения заданий.
GitHub Actions на macOS может быть дорогим
Хорошей новостью является то, что использование GitHub Actions не связано с публичными репозиториями.
Но будьте осторожны, если вы используете приватный репозиторий!
Для приватных репозиториев каждая учётная запись GitHub получает определенное количество бесплатных минут и хранилища для использования с раннерами, размещёнными на GitHub, в зависимости от продукта, используемого с учетной записью. Любое использование сверх включенных величин контролируется лимитами расходов.
Образы раннера на базе MacOS затратны для GitHub, поэтому GitHub применяет минутный множитель.
Использование 1000 минут macOS потребует 10 000 минут, включенных в вашу учётную запись. После того, как вы израсходуете весь свой бюджет, оплата раннеров для macOS становится дорогой из-за этого множителя.
Использование образа раннера на базе macOS обходится в десять раз дороже, чем на базе Linux.Источник: About billing for GitHub Actions
Советы по экономии денег
По возможности используйте раннер на базе Linux.
Это несложно, но всё же стоит упомянуть.
Если вы хотите проверить pull request, вы можете использовать для этой работы раннер на базе Linux. В ubuntu-latest установлен Swift.
jobs:
SwiftLint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: GitHub Action for SwiftLint
uses: norio-nomura/action-swiftlint@3.2.1
Такие инструменты, как SwiftLint или SwiftFormat, работают в Linux, поскольку они не зависят от специфичных для Apple фреймворков.
К вашему сведению: в январе 2023 года ubuntu-latest ссылается на образ раннера ubuntu-22.04, который поставляется с предустановленной версией Swift 5.7.3.
Запуск задания одновременно
Я не хочу избегать параллельных заданий, потому что здорово, что вы можете определить матричную стратегию для автоматического создания нескольких запусков заданий, основанных на комбинациях переменных.
Вот пример сборки пакета Swift на iOS для разных версий Swift:
Swift 5.7.1, который поставляется с Xcode 14.1;
Swift 5.5, который поставляется с Xcode 13.
jobs:
build-macOS:
name: Xcode ${{ matrix.xcode }}
runs-on: ${{ matrix.runner-image }}
env:
DEVELOPER_DIR: "/Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer"
strategy:
fail-fast: false
matrix:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#xcode
# https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md#xcode
include:
- xcode: "14.1"
runner-image: macOS-12
name: "macOS 12, Xcode 14.1, Swift 5.7.1"
- xcode: "13.0"
runner-image: macOS-11
name: "macOS 11, Xcode 13.0, Swift 5.5.0"
steps:
- uses: actions/checkout@v3
- name: Build (${{ matrix.name }})
run: set -o pipefail && xcodebuild -project -scheme YourPackageName-Package -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 11' clean test | xcpretty
Вернёмся к теме. Я имею в виду, что вы должны избегать одновременного выполнения одной и той же работы, логически принадлежащей одной и той же группе. Поясню на примере:
Я создаю pull request, и запускается рабочий процесс.
Вы заметили, что забыли добавить файл, поэтому выполняете push и добавляете в свой pull request.
Предыдущий рабочий процесс всё ещё выполняется, и запускается новый рабочий процесс – это пример избыточного параллельного рабочего процесса/задания.
Для данного примера было бы лучше, если бы GitHub немедленно отменил выполнение предыдущего рабочего процесса.
Это возможно с помощью jobs.<job_id>.concurrency, чтобы гарантировать, что только одно задание или рабочий процесс, использующий одну и ту же группу параллелизма, будет выполняться одновременно. Группа параллелизма может быть любой строкой или выражением. Пример использования параллелизма для отмены любого незавершённого задания или выполнения:
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
Например, популярный XcodeProj от Tuist использует эту функцию.
# https://github.com/tuist/XcodeProj/blob/8.8.0/.github/workflows/xcodeproj.yml
concurrency:
group: xcodeproj-${{ github.head_ref }}
cancel-in-progress: true
Дополнительную информацию можно найти в документации GitHub Using Concurrency.
Избегайте выполнения рабочего процесса с помощью фильтров
Нужно ли вам создавать исходный код Swift, если вы обновили только файл README?
Нет! Используйте фильтр путей, если вы хотите рассмотреть шаблоны путей к файлам.
Мне нравится определять следующий рабочий процесс в .github/workflows/ci-build.yml:
name: ci
on:
push:
paths:
- ".github/workflows/**"
- "**/*.swift"
pull_request:
paths:
- ".github/workflows/**"
- "**/*.swift"
Изменения, связанные с исходным кодом (а также изменения, связанные с рабочим процессом), запускают рабочий процесс, но такие файлы, как README, не запускают рабочий процесс.
Внимание: обязательные проверки статуса и пропуск рабочих процессов
Возможно, вы настроили проверку состояния, чтобы рабочий процесс был успешным, прежде чем запрос на вытягивание будет объединён.
Но если рабочий процесс пропускается из-за фильтрации путей (или фильтрации ветвей, или сообщения фиксации), то проверки, связанные с этим рабочим процессом, останутся “Pending” (ожидающими), а pull request будет заблокирован от слияния (merging).
Одним из решений может быть Using conditions to control job execution (использование условий для управления выполнением задания), но я считаю, что у GitHub есть лучшее решение.
Давайте создадим дополнительный рабочий процесс, определенный в .github/workflows/ci-build-skiped.yml:
name: ci
on:
push:
paths-ignore:
- ".github/workflows/**"
- "**/*.swift"
pull_request:
paths-ignore:
- ".github/workflows/**"
- "**/*.swift"
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: 'echo "No build required"'
Определив дополнительный рабочий процесс с помощью инструментов:
идентичный рабочий процесс и имя задания;
использование paths-ignore (путей игнорирования) в качестве аналога
Мы можем гарантировать выполнение минимального рабочего процесса/задания и, следовательно, проверку статуса.
Убедитесь, что ключ имени и требуемое имя задания в обоих файлах рабочего процесса совпадают.
Дополнительные сведения см. в документации GitHub Handling skipped but required checks.