Повністю автоматизовані релізи для Rust-проектів

12.09.2025 4 хвилин Автор: Lady Liberty

У статті детально описується, як налаштувати автоматизовані релізи Rust-проєктів із використанням сучасних інструментів. Пояснюється, для чого застосовується git-cliff при створенні changelog, як release-plz допомагає керувати версіями й виконувати публікацію на crates.io, а також яку роль відіграє cargo-dist у збірці бінарників для різних операційних систем. Окремо розглянуто інтеграцію Dependabot і Mergify, що дозволяє своєчасно оновлювати залежності й уникати рутинної роботи. Матеріал показує, як поєднання цих інструментів робить процес випуску нових версій швидким, надійним та зручним навіть для великих проєктів.

Як зробити випуск Rust-програми повністю автоматичним

Уявіть собі розробника на ім’я nuhro . Він написав свою першу програму на Rust, і вона щасливо працює на його машині. Він також надіслав бінарний файл/exe кільком довірливим друзям, і всім їм сподобалася програма після того, як вони її сліпо виконали. Гарні вібрації!

Потім одного дня він вирішив поділитися цією невеликою програмою зі світом (також відомою як Reddit). Для цього він зрозумів, що йому потрібно випустити програму. Інакше як же користувачі інтернету (які можуть використовувати різні ОС/платформи) встановлять/запустять програму, правда?

І так, це було у 2019 році, коли було опубліковано перший проєкт на Rust та автоматизував процес релізу за допомогою запозиченого робочого процесу GitHub Actions від ripgrepo_O. Повертаючись до сьогодення, якщо ви хочете випустити проект Rust, цілком ймовірно, що ви зіткнетеся з такими інструментами для випуску Rust:

  • cargo-releaseвсе про випуск іржавого ящика.

  • cargo-smart-release: автоматично випускати складні робочі простори cargo з генерацією журналу змін.

  • cargo-unleashІнструменти автоматизації випуску для масивних монорепозиторіїв.

Ми можемо перерахувати тут інші інструменти, але, якщо коротко, все, що ми хочемо зробити, це:

  1. Переконайтеся, що проєкт готовий до випуску . Це може включати оновлену версію/журнал змін/залежності.

  2. Позначте новий реліз.

  3. Опублікуйте нову версію на різних платформах (crates.io, GitHub тощо)

Потужний еліксир

Тут ми збираємося змішати найпотужніші інструменти, щоб створити відьмину суміш — найкращий спосіб публікації контейнерів Rust. Дехто може назвати це токсичним варивом, яке може отруїти когось через надмірну автоматизацію. Дехто може назвати це квитком в один кінець до пекла автоматизації. Дехто може…

Отже, ось список інструментів, які ми будемо використовувати:

  • git-cliff Генератор журналу змін з широкими можливостями налаштування. Автоматизує створення журналу змін.

  • release-plz   Опублікувати контейнери Rust з CI з вимогою щодо випуску. Обробляє оновлення залежностей, керування версіями та випуск crates.io.

  • cargo-dist   Упаковка для застосунків Rust, що підлягає транспортуванню. Створює релізи та упаковки GitHub для різних платформ.

  • Dependabot Автоматизовані оновлення залежностей, вбудовані в GitHub. Оновлює залежності дій Rust та GitHub.

  • Mergify Автоматизований інструмент CI/CD для оптимізації. Автоматично об’єднує Dependabotзапити на зняття.

З мінімальними налаштуваннями ми можемо зробити ці інструменти безперешкодно інтегрованими один з одним, і зрештою, все, що нам потрібно зробити, це просто натиснути кнопку, щоб запустити новий реліз. Іншими словами, найкраще в цьому процесі релізу те, що майже все обробляється в незалежній інтерфейсі, тобто в діях GitHub, тому ми навіть пальцем не ворухнемо.

А тепер, давайте візьмемося за роботу!

Налаштування

git-cliff– один з моїх великих проєктів, який широко використовується в екосистемі Rust для автоматизації створення журналу змін.

git-cliff — це інструмент командного рядка (написаний на Rust ), який надає високо настроюваний спосіб створення журналів змін з історії git. Він підтримує використання користувацьких регулярних виразів для зміни журналів змін, які здебільшого базуються на звичайних коммітах . Завдяки шаблонізатору, натхненному Jinja2/Django, за допомогою одного файлу конфігурації можна застосовувати широкий спектр форматів для журналу змін.Більше інформації та прикладів можна знайти в репозиторії GitHub .

Для нашого формату журналу змін ми можемо просто використовувати конфігурацію за замовчуванням, яка виводить щось на кшталт наступного:

# Changelog

All notable changes to this project will be documented in this file.

## [unreleased]

### Documentation

- Add link to emacs package support git-cliff (#307)

Для цієї конфігурації спочатку нам слід встановити git-cliff:

cargo install git-cliff --locked

Тоді ми можемо ініціалізувати його наступним чином:

git cliff --init

Це створить cliff.tomlу поточному каталозі, і ви можете закомітувати його в кореневому каталозі вашого репозиторію, щоб release-plzотримати його.

Якщо ви хочете створити складніший журнал змін, ви можете переглянути приклади або скористатися конфігурацією , якаgit-cliff відповідає наступному:

$ git cliff

# Changelog

All notable changes to this project will be documented in this file.

## [unreleased]

### 📚 Documentation

- _(readme)_ Add link to emacs package support git-cliff ([#307](https://github.com/orhun/git-cliff/issues/307)) - ([fa471c7](https://github.com/orhun/git-cliff/commit/fa471c7178dce184ca6fe5bbb24b9c2db96d68ce))

Чудово, тепер ми готові налаштувати власне інструмент для зняття важеля!

release-plz 

release-plz створює повністю автоматизований конвеєр релізів, що дозволяє вам легко випускати зміни частіше, без побоюючись помилок чи інших незначних помилок, які ви можете зробити під час випуску з терміналу.

На відміну від git-cliff, нам насправді не потрібно встановлювати його release-plzяк інструмент командного рядка. Це тому, що release-plzвін запускається з CI (GitHub Actions), і ми нічого локально не робимо для релізів. Однак нам все ще потрібно налаштувати деякі речі щодо того, як ми хочемо релізувати наш проект.

Для налаштування release-plzстворіть release-plz.tomlз наступним вмістом у кореневому каталозі вашого репозиторію:

[workspace]
# path of the git-cliff configuration
changelog_config = "cliff.toml"

# enable changelog updates
changelog_update = true

# update dependencies with `cargo update`
dependencies_update = true

# create tags for the releases
git_tag_enable = true

# disable GitHub releases
git_release_enable = false

# labels for the release PR
pr_labels = ["release"]

# disallow updating repositories with uncommitted changes
allow_dirty = false

# disallow packaging with uncommitted changes
publish_allow_dirty = false

# disable running `cargo-semver-checks`
semver_check = false

Важливими опціями тут є:

  • changelog_config: має вказувати на git-cliffконфігурацію, яку ми створили в попередньому розділі.

  • dependencies_update: встановлення цього значення trueозначає, що ви хочете запускати cargo updateперед кожним релізом. Увімкнення цього параметра для оновлення транзитивних залежностей не завадить, оскільки ми вже будемо оновлювати основні залежності за допомогою Dependabot у наступних кроках.

  • git_tag_enable: встановіть це значення, trueщоб створити тег для нових релізів.

  • git_release_enable: переконайтеся, що ви встановили це значення, falseоскільки ми не хочемо release-plzстворювати реліз GitHub для нас, оскільки ця частина буде оброблена cargo-dist.

Щодо наступного кроку, нам слід створити фактичну автоматизацію релізу, яка відповідає за виконання release-plzкожного коміту, що надсилається до репозиторію, та створення “release PR”.

https://github.com/orhun/daktilo/pull/51

Після об’єднання релізного PR, release-plzвиявляє, що є зміна версії, Cargo.tomlі запускає cargo publishреліз нової версії на crates.io .

А, тож нам, мабуть, потрібно налаштувати секрети для токена публікації тощо, чи не так? Так! І кілька дозволів також. Ви можете переглянути release-plzдокументацію для отримання детальнішої інформації, але ось короткий виклад:

1. Змініть «Дозволи робочого процесу», щоб дозволити GitHub Actions створювати та затверджувати запити на зняття (pull requests) у Репозиторій > Налаштування > Дії > Загальні.

2. Створіть новий PAT (персональний токен доступу) (дрібнозернистий або класичний) з правильними правами доступу до репозиторію та додайте його до репозиторію як секретний ключ з назвою RELEASE_PLZ_TOKEN.

3. На crates.io згенеруйте новий токен з publish-newправами publish-updateдоступу та додайте його до репозиторію як секрет з назвою CARGO_REGISTRY_TOKEN.

4. Ми нарешті можемо створити наш робочий процес дій GitHub у.github/workflows/release-plz.yml

name: Continuous Deployment

on:
  push:
    branches:
      - main

jobs:
  release-plz:
    name: Release-plz
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.RELEASE_PLZ_TOKEN }}

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Run release-plz
        uses: MarcoIeni/[email protected]
        env:
          GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

Щоб розбити це на частини:

  • Ми клонуємо всю історію git за допомогою fetch-depth: 0, що необхідно для визначення наступної версії та створення журналу змін.

  • Ми встановлюємо стабільну версію Rust (чому б і ні?!)

  • Як останній крок, ми біжимо release-plz, чорт забирай!

З : Вау, вау, вау, гей, гей, гей, що там з GITHUB_TOKEN: ${{ secrets.RELEASE_PLZ_TOKEN }}? Просто використовуйте ${{ secrets.GITHUB_TOKEN }}bro, який визначено як `за замовчуванням` для кожного репозиторію.

В : Гарне питання! Якщо ви використовуєте GITHUB_TOKEN, це означає, що ви збираєтеся автентифікуватися від імені GitHub Actions.

З : То що ж тут не так?

A : Ну, робочі процеси, що запускаються , on: push: tags не працюватимуть, якщо тег створено GitHub Actions. Тому ми використовуємо персональний токен доступу, який є способом сказати: «Шановні GitHub Actions,  запускаю, release-plz,далі створюю тег, тому, будь ласка, запустіть подальші робочі процеси». Крім того, таким чином нам не потрібно вказувати дозволи робочого процесу за допомогою permissions:ключа. Ви можете дізнатися більше про це тут .

Тепер ми просто готові працювати над нашим проєктом як завжди, і release-plzстворимо PR-запит на реліз для наших змін, і ми зможемо об’єднати його для створення нового релізу, коли будемо готові!

З : Що робити, якщо хочу запустити/тестувати release-plzлокально?

A : Можете, якщо хочете! Запит на оновлення – це просто вивід release-plz updateкоманди, тож ви можете запустити його локально та побачити, що змінилося з точки зору журналу змін тощо. Також, якщо ви хочете створити запит на оновлення з командного рядка, ви можете просто виконати release-plz release-pr!

cargo-dist

cargo-dist спрощує розповсюдження бінарних файлів, розбиваючи процес на кілька кроків, таких як планування, збірка, публікація та оголошення. Це просто інструмент для створення цієї чудової сторінки випуску разом із бінарними артефактами та інсталяторами:

https://github.com/orhun/daktilo/releases/tag/v0.4.0

Подібно до release-plzcargo-distце інструмент, який ми не будемо використовувати локально, а запускатимемо його з CI. Однак його потрібно встановити, щоб налаштувати CI (сам для себе) вперше.

Отже, почнемо з встановлення cargo-dist:

cargo install cargo-dist --locked

Ви також можете скористатися cargo-dist-installerдля встановлення cargo-dist(тобто бінарним інсталятором), якщо бажаєте:

cargo_dist_version="0.3.1"

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v{version}/cargo-dist-installer.sh | sh

Також у нас буде інсталятор, як показано вище, після того, як він cargo distзробить свою справу.

cargo dist init

Вау, що трапилося?

Наведена вище команда генерує .github/workflows/release.ymlфайл для обробки релізів GitHub. Також вона додає наступний розділ до вашого проєкту Cargo.toml:

# The profile that 'cargo dist' will build with
[profile.dist]
inherits = "release"
lto = "thin"

# Config for 'cargo dist'
[workspace.metadata.dist]
# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.3.1"

# CI backends to support
ci = ["github"]

# The installers to generate for each app
installers = ["shell", "powershell"]

# Target platforms to build apps for (Rust target-triple syntax)
targets = [
  "x86_64-unknown-linux-gnu",
  "aarch64-apple-darwin",
  "x86_64-apple-darwin",
  "x86_64-pc-windows-msvc",
]

# Publish jobs to run in CI
pr-run-mode = "upload"

Ви можете оновити цю конфігурацію, щоб легко налаштувати cargo-distта змінити параметри випуску.

З : А як щодо нових випусків/важливих змін cargo-dist? Чи потрібно мені видалити конфігурацію та почати все спочатку?

A : Ви можете просто запустити cargo dist initще раз, щоб повторно згенерувати конфігурацію випуску!

З : Круто! Чи cargo-distперевіряє оновлення файлу робочого процесу? Що робити, якщо треба внести зміни до згенерованого робочого процесу дій GitHub (наприклад, встановити залежності Linux)?

A : Просто встановіть такі значення в конфігурації, щоб уникнути попередження про «застарілий» статус:

[workspace.metadata.dist]

# Ignore out-of-date contents
allow-dirty = ["ci"]

З : Як можливо провести тестування cargo-distна місці?

Легко :

$ cargo dist plan

announcing v0.1.0
  automated-rust-releases 0.1.0
    automated-rust-releases-installer.sh
    automated-rust-releases-installer.ps1
    automated-rust-releases-aarch64-apple-darwin.tar.xz
      [bin] automated-rust-releases
      [misc] LICENSE-MIT
      [checksum] automated-rust-releases-aarch64-apple-darwin.tar.xz.sha256
    automated-rust-releases-x86_64-apple-darwin.tar.xz
      [bin] automated-rust-releases
      [misc] LICENSE-MIT
      [checksum] automated-rust-releases-x86_64-apple-darwin.tar.xz.sha256
    automated-rust-releases-x86_64-pc-windows-msvc.zip
      [bin] automated-rust-releases.exe
      [misc] LICENSE-MIT
      [checksum] automated-rust-releases-x86_64-pc-windows-msvc.zip.sha256
    automated-rust-releases-x86_64-unknown-linux-gnu.tar.xz
      [bin] automated-rust-releases
      [misc] LICENSE-MIT
      [checksum] automated-rust-releases-x86_64-unknown-linux-gnu.tar.xz.sha256

І запустіть, cargo dist buildякщо ви дійсно хочете зібрати артефакти. Також, якщо ви встановите pr-run-modeце uploadв конфігурації, артефакти будуть зібрані та завантажені як artifacts.zipпов’язаний коміт на GitHub.

Тепер ми готові створювати артефакти та випускати їх на GitHub! Залишилося кілька кроків, перш ніж ми створимо реліз та продемонструємо все.

Dependabot

Dependabot— це інструмент, вбудований у GitHub, для автоматичного оновлення залежностей проєкту шляхом створення пул-реквестів.

Ми можемо просто налаштувати це, створивши .github/dependabot.yml:

version: 2
updates:
  # Maintain dependencies for Cargo
  - package-ecosystem: cargo
    directory: "/"
    schedule:
      interval: daily
    open-pull-requests-limit: 10

  # Maintain dependencies for GitHub Actions
  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: daily
    open-pull-requests-limit: 10

Тепер Dependabotпочнеться створення пул-реквестів для залежностей Rust/GitHub Actions, коли вони застаріють:

https://github.com/orhun/systeroid/pull/141

Mergify

Mergify— це оптимізатор конвеєра CI/CD, який керує вашими пул-реквестами, автоматизує робочий процес GitHub та оптимізує ваші витрати на CI.

Ми будемо використовувати Mergifyдля:

  1. Автоматично об’єднувати Dependabotзапити на зняття.

  2. Оновіть до останньої mainгілки для пул-реквестів.

Давайте створимо конфігурацію в .github/mergify.yml:

pull_request_rules:
  - name: Automatic merge for Dependabot pull requests
    conditions:
      - author=dependabot[bot]
    actions:
      merge:
        method: squash

  - name: Automatic update to the main branch for pull requests
    conditions:
      - -conflict # skip PRs with conflicts
      - -draft # skip GH draft PRs
      - -author=dependabot[bot] # skip dependabot PRs
    actions:
      update:

Нарешті, нам потрібно створити обліковий запис на https://mergify.com та додати наш репозиторій GitHub до панелі інструментів:

Тепер Mergifyпочнеться обробка відкритих пул-реквестів:

Запити Dependabotна зняття тепер об’єднуються автоматично!

Давайте створювати релізи

Весь код/конфігурація доступна тут: https://github.com/orhun/automated-rust-releases

release-plzвже створили для нас PR-запит:

https://github.com/orhun/automated-rust-releases/pull/1

cargo-distперевірки також проходять. Давайте натиснемо кнопку об’єднання та створимо реліз:

Після злиття, release-plzотримує нову версію з Cargo.tomlта надсилає її до crates.io:

release-plzтакож створює новий тег, який запускає cargo-dist:

cargo-distзбирає артефакти та завантажує їх до релізу GitHub:

https://github.com/orhun/automated-rust-releases/releases/tag/v0.1.1

Як бачите, cargo-distтакож аналізує журнал змін (який генерується git-cliff) та додає його до релізу. (Зверніть увагу, що вам насправді потрібно створити файл CHANGELOG.md у кореневому каталозі вашого репозиторію, щоб це працювало.)

Давайте спробуємо інсталятор на практиці:

$ curl --proto '=https' --tlsv1.2 -LsSf https://github.com/orhun/automated-rust-releases/releases/download/v0.1.1/automated-rust-releases-installer.sh | sh

downloading automated-rust-releases 0.1.1 x86_64-unknown-linux-gnu
installing to /home/orhun/.cargo/bin
  automated-rust-releases
everything's installed!

А коли ми його запускаємо:

$ automated-rust-releases

Hello, world!

Висновок

Для останнього проєкту daktilo (CLI-програма, що перетворює клавіатуру на друкарську машинку) була застосована ця автоматизація випуску, яка продемонструвала гарні результати. Наразі очікуються наступні покращення:

  • git-cliff: краща інтеграція з GitHub (тобто додавання імен учасників до журналів змін * )

  • release-plz: встановлення версії у звіті про виклик випуску за допомогою команди *

  • cargo-dist: крос-компіляція *

Дайте мені знати, якщо у вас є пропозиції та ідеї! Щасливого випуску!

Підписатися
Сповістити про
0 Коментарі
Найстаріші
Найновіше Найбільше голосів
Знайшли помилку?
Якщо ви знайшли помилку, зробіть скріншот і надішліть його боту.