ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

МнС Ρ‡Π°ΡΡ‚Π΅Π½ΡŒΠΊΠΎ приходится ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ для сборки ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² Π½Π° Java. Иногда это опСнсорс, ΠΈΠ½ΠΎΠ³Π΄Π° Π½Π΅Ρ‚. НСдавно я Ρ€Π΅ΡˆΠΈΠ» ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ пСрСнСсти Ρ‡Π°ΡΡ‚ΡŒ своих Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠ΅Π² с Travis-CI ΠΈ TeamCity Π½Π° GitHub Actions, ΠΈ Π²ΠΎΡ‚ Ρ‡Ρ‚ΠΎ ΠΈΠ· этого ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ.

Π§Ρ‚ΠΎ Π±ΡƒΠ΄Π΅ΠΌ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ

Для Π½Π°Ρ‡Π°Π»Π° Π½Π°ΠΌ Π½ΡƒΠΆΠ΅Π½ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, Π΄Π°Π²Π°ΠΉΡ‚Π΅ сдСлаСм нСбольшоС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π½Π° Spring boot / Java 11 / Maven. Π’ Ρ€Π°ΠΌΠΊΠ°Ρ… этой ΡΡ‚Π°Ρ‚ΡŒΠΈ Π»ΠΎΠ³ΠΈΠΊΠ° прилоТСния нас ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ совсСм, Π½Π°ΠΌ Π²Π°ΠΆΠ½Π° инфраструктура Π²ΠΎΠΊΡ€ΡƒΠ³ прилоТСния, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Ρ…Π²Π°Ρ‚ΠΈΡ‚ ΠΏΡ€ΠΎΡΡ‚Π΅Π½ΡŒΠΊΠΎΠ³ΠΎ REST API ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π»Π΅Ρ€Π°.

ΠŸΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ исходники ΠΌΠΎΠΆΠ½ΠΎ Ρ‚ΡƒΡ‚: github.com/antkorwin/github-actions всС этапы построСния pipeline-ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π° ΠΎΡ‚Ρ€Π°ΠΆΠ΅Π½Ρ‹ Π² ΠΏΡƒΠ»Π»-рСквСстах этого ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°.

JIRA ΠΈ ΠΏΠ»Π°Π½ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅

Π‘Ρ‚ΠΎΠΈΡ‚ ΡΠΊΠ°Π·Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ JIRA Π² качСствС Ρ‚Ρ€Π΅ΠΊΠ΅Ρ€Π° Π·Π°Π΄Π°Ρ‡, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ Π΄Π°Π²Π°ΠΉΡ‚Π΅ Π·Π°Π²Π΅Π΄Π΅ΠΌ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΡƒΡŽ Π±ΠΎΡ€Π΄Ρƒ ΠΏΠΎΠ΄ этот ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΈ Π½Π°ΠΊΠΈΠ΄Π°Π΅ΠΌ Ρ‚ΡƒΠ΄Π° ΠΏΠ΅Ρ€Π²Ρ‹Π΅ Π·Π°Π΄Π°Ρ‡ΠΈ:

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Π§ΡƒΡ‚ΡŒ ΠΏΠΎΠ·ΠΆΠ΅ ΠΌΡ‹ Π΅Ρ‰Π΅ вСрнСмся ΠΊ Ρ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ интСрСсного ΠΌΠΎΠ³ΡƒΡ‚ Π΄Π°Ρ‚ΡŒ Π² связкС JIRA ΠΈ GitHub.

АвтоматизируСм сборку ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°

Наш тСстовый ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ собираСтся Ρ‡Π΅Ρ€Π΅Π· maven, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ сборка Π΅Π³ΠΎ довольно простая, всС, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ, это mvn clean package.

Π§Ρ‚ΠΎΠ±Ρ‹ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ это ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ Github Actions, Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ Π±ΡƒΠ΄Π΅Ρ‚ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ Ρ„Π°ΠΉΠ» с описаниСм нашСго workflow, это ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌ yml-Ρ„Π°ΠΉΠ»ΠΎΠΌ, Π½Π΅ ΠΌΠΎΠ³Ρƒ ΡΠΊΠ°Π·Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ ΠΌΠ½Π΅ нравится Β«ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Π½Π° ymlΒ», Π½ΠΎ Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ΄Π΅Π»Π°Ρ‚ΡŒ β€” Π΄Π΅Π»Π°Π΅ΠΌ Π² Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ .github/workflow/ Ρ„Π°ΠΉΠ» build.yml Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄Π΅ΠΌ ΠΎΠΏΠΈΡΡ‹Π²Π°Ρ‚ΡŒ дСйствия ΠΏΡ€ΠΈ сборкС мастСр Π²Π΅Ρ‚ΠΊΠΈ:

name: Build

on:
  pull_request:
    branches:
      - '*'
  push:
    branches:
      - 'master'

jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v1
      - name: set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 1.11
      - name: Maven Package
        run: mvn -B clean package -DskipTests

on β€” это описаниС события, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒΡΡ наш скрипт.

on: pull_request / push β€” Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ этот workflow Π½ΡƒΠΆΠ½ΠΎ Π·Π°ΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΏΡƒΡˆΠ΅ Π² мастСр ΠΈ создании ΠΏΡƒΠ»Π»-рСквСстов.

Π”Π°Π»ΡŒΡˆΠ΅ ΠΈΠ΄Π΅Ρ‚ описаниС Π·Π°Π΄Π°Π½ΠΈΠΉ (jobs) ΠΈ шаги выполнСния (steps) для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Π·Π°Π΄Π°Ρ‡ΠΈ.

runs-on β€” Ρ‚ΡƒΡ‚ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ Ρ†Π΅Π»Π΅Π²ΡƒΡŽ ОБ, Π½Π° ΡƒΠ΄ΠΈΠ²Π»Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ Π΄Π°ΠΆΠ΅ Mac OS, Π½ΠΎ Π½Π° ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹Ρ… рСпозиториях это довольно Π΄ΠΎΡ€ΠΎΠ³ΠΎΠ΅ ΡƒΠ΄ΠΎΠ²ΠΎΠ»ΡŒΡΡ‚Π²ΠΈΠ΅ (Π² сравнСнии с linux).

uses позволяСт ΠΏΠ΅Ρ€Π΅ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΡΠΊΡˆΠ΅Π½Ρ‹, Ρ‚Π°ΠΊ Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ экшСна actions/setup-java ΠΌΡ‹ устанавливаСм ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ для Java 11.

ΠŸΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ with ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ запускаСм дСйствиС, ΠΏΠΎ сути это Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Ρ‚ΡŒΡΡ Π² экшСн.

ΠžΡΡ‚Π°Π΅Ρ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ΠΌΠ°Π²Π΅Π½ΠΎΠΌ сборку ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°: run: mvn -B clean package Ρ„Π»Π°Π³ -B Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½ΡƒΠΆΠ΅Π½ non-interactive mode, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΌΠ°Π²Π΅Π½ Π²Π΄Ρ€ΡƒΠ³ Π½Π΅ Π·Π°Ρ…ΠΎΡ‚Π΅Π» Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Ρƒ нас ΡΠΏΡ€ΠΎΡΠΈΡ‚ΡŒ

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

ΠžΡ‚Π»ΠΈΡ‡Π½ΠΎ! Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΏΡ€ΠΈ ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΊΠΎΠΌΠΌΠΈΡ‚Π΅ Π² мастСр, запускаСтся сборка ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°.

АвтоматизируСм запуск тСстов

Π‘Π±ΠΎΡ€ΠΊΠ° это Ρ…ΠΎΡ€ΠΎΡˆΠΎ, Π½ΠΎ Π² Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Π»Π°Π³ΠΎΠΏΠΎΠ»ΡƒΡ‡Π½ΠΎ ΡΠΎΠ±ΠΈΡ€Π°Ρ‚ΡŒΡΡ, Π½ΠΎ Π½Π΅ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ. ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ шагом Π½ΡƒΠΆΠ½ΠΎ Π·Π°Π½ΡΡ‚ΡŒΡΡ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ ΠΏΡ€ΠΎΠ³ΠΎΠ½Π° тСстов. К Ρ‚ΠΎΠΌΡƒ ΠΆΠ΅, довольно ΡƒΠ΄ΠΎΠ±Π½ΠΎ ΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΏΡ€ΠΎΡ…ΠΎΠ΄Π° тСстов, ΠΊΠΎΠ³Π΄Π° дСлаСшь Ρ€Π΅Π²ΡŒΡŽ PR β€” Ρ‚Ρ‹ Ρ‚ΠΎΡ‡Π½ΠΎ знаСшь, Ρ‡Ρ‚ΠΎ тСсты проходят ΠΈ Π½ΠΈΠΊΡ‚ΠΎ Π½Π΅ Π·Π°Π±Ρ‹Π», ΠΏΠ΅Ρ€Π΅Π΄ Ρ‚Π΅ΠΌ ΠΊΠ°ΠΊ Π΄Π΅Π»Π°Ρ‚ΡŒ merge, ΠΏΡ€ΠΎΠ³Π½Π°Ρ‚ΡŒ свою Π²Π΅Ρ‚ΠΊΡƒ.

Π”Π΅Π»Π°Π΅ΠΌ запуск тСстов ΠΏΡ€ΠΈ создании ΠΏΡƒΠ»Π»-рСквСста ΠΈ merge Π² мастСр, Π° Π·Π°ΠΎΠ΄Π½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ построСниС ΠΎΡ‚Ρ‡Π΅Ρ‚Π° ΠΎ code-coverage.

name: Build

on:
  pull_request:
    branches:
      - '*'
  push:
    branches:
      - 'master'

jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v1
      - name: set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 1.11
      - name: Maven Verify
        run: mvn -B clean verify
      - name: Test Coverage
        uses: codecov/codecov-action@v1
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

Для покрытия тСстов я ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽ codecov Π² связкС с jacoco ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠΌ. Π£ codecov Π΅ΡΡ‚ΡŒ свой экшСн, Π½ΠΎ Π΅ΠΌΡƒ для Ρ€Π°Π±ΠΎΡ‚Ρ‹ с нашим pull-request-ΠΎΠΌ Π½ΡƒΠΆΠ΅Π½ Ρ‚ΠΎΠΊΠ΅Π½:

${{ secrets.CODECOV_TOKEN }} β€” Ρ‚Π°ΠΊΡƒΡŽ ΠΊΠΎΠ½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡŽ ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ Π²ΡΡ‚Ρ€Π΅Ρ‡Π°Ρ‚ΡŒ Π΅Ρ‰Π΅ Π½Π΅ ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·, secrets это ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ хранСния сСкрСтов Π² Π³ΠΈΡ‚Ρ…Π°Π±Π΅, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ‚Π°ΠΌ ΠΏΡ€ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΏΠ°Ρ€ΠΎΠ»ΠΈ/Ρ‚ΠΎΠΊΠ΅Π½Ρ‹/хосты/url-Ρ‹ ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΠ΅ Π΄Π°Π½Π½Ρ‹Π΅, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ Π½Π΅ стоит ΡΠ²Π΅Ρ‚ΠΈΡ‚ΡŒ Π² ΠΊΠΎΠ΄ΠΎΠ²ΠΎΠΉ Π±Π°Π·Π΅ рСпозитория.

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ Π² secrets, ΠΌΠΎΠΆΠ½ΠΎ Π² настройках рСпозитория Π½Π° GitHub:

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ‚ΠΎΠΊΠ΅Π½ ΠΌΠΎΠΆΠ½ΠΎ Π½Π° codecov.io послС Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ‡Π΅Ρ€Π΅Π· GitHub, для добавлСния public ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π½ΡƒΠΆΠ½ΠΎ просто ΠΏΡ€ΠΎΠΉΡ‚ΠΈ ΠΏΠΎ ссылкС Π²ΠΈΠ΄Π°: GitHub user name/[repo name]. ΠŸΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ Ρ‚ΠΎΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ, для этого Π½Π°Π΄ΠΎ Π΄Π°Ρ‚ΡŒ ΠΏΡ€Π°Π²Π° codecov ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡŽ Π² Π³ΠΈΡ‚Ρ…Π°Π±Π΅.

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

ДобавляСм jacoco ΠΏΠ»Π°Π³ΠΈΠ½ Π² POM-Ρ„Π°ΠΉΠ»:

<plugin>
	<groupId>org.jacoco</groupId>
	<artifactId>jacoco-maven-plugin</artifactId>
	<version>0.8.4</version>
	<executions>
		<execution>
			<goals>
				<goal>prepare-agent</goal>
			</goals>
		</execution>
		<!-- attached to Maven test phase -->
		<execution>
			<id>report</id>
			<phase>test</phase>
			<goals>
				<goal>report</goal>
			</goals>
		</execution>
	</executions>
</plugin>
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-surefire-plugin</artifactId>
	<version>2.22.2</version>
	<configuration>
		<reportFormat>plain</reportFormat>
		<includes>
			<include>**/*Test*.java</include>
			<include>**/*IT*.java</include>
		</includes>
	</configuration>
</plugin>

Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π² ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ наш ΠΏΡƒΠ»Π»-рСквСст Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ codecov Π±ΠΎΡ‚ ΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ Π³Ρ€Π°Ρ„ΠΈΠΊ измСнСния покрытия:

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Π”ΠΎΠ±Π°Π²ΠΈΠΌ статичСский Π°Π½Π°Π»ΠΈΠ·Π°Ρ‚ΠΎΡ€

Π’ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π΅ своих oпСнсорс-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² я ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽ sonar cloud для статичСского Π°Π½Π°Π»ΠΈΠ·Π° ΠΊΠΎΠ΄Π°, Π΅Π³ΠΎ довольно Π»Π΅Π³ΠΊΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ ΠΊ travis-ci. Π’Π°ΠΊ Ρ‡Ρ‚ΠΎ это Π»ΠΎΠ³ΠΈΡ‡Π½Ρ‹ΠΉ шаг ΠΏΡ€ΠΈ ΠΌΠΈΠ³Ρ€Π°Ρ†ΠΈΠΈ Π½Π° GitHub Actions, ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚ΠΎΠΆΠ΅ самоС. ΠœΠ°Ρ€ΠΊΠ΅Ρ‚ экшСнов β€” клСвая ΡˆΡ‚ΡƒΠΊΠ°, Π½ΠΎ Π² этот Ρ€Π°Π· ΠΎΠ½ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎΠ΄Π²Π΅Π», ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ я ΠΏΠΎ ΠΏΡ€ΠΈΠ²Ρ‹Ρ‡ΠΊΠ΅ нашСл Π½ΡƒΠΆΠ½Ρ‹ΠΉ экшСн ΠΈ прописал Π΅Π³ΠΎ Π² workflow. А оказалось, Ρ‡Ρ‚ΠΎ sonar Π½Π΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ Ρ‡Π΅Ρ€Π΅Π· дСйствиС для Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ² Π½Π° maven ΠΈΠ»ΠΈ gradle. Об этом ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ написано Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ, Π½ΠΎ ΠΊΡ‚ΠΎ ΠΆΠ΅ Π΅Π΅ Ρ‡ΠΈΡ‚Π°Π΅Ρ‚?!

Π§Π΅Ρ€Π΅Π· дСйствиС нСльзя, поэтому Π±ΡƒΠ΄Π΅ΠΌ Π΄Π΅Π»Π°Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· mvn ΠΏΠ»Π°Π³ΠΈΠ½:

name: SonarCloud

on:
  push:
    branches:
      - master
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  sonarcloud:
    runs-on: ubuntu-16.04
    steps:
      - uses: actions/checkout@v1
      - name: Set up JDK
        uses: actions/setup-java@v1
        with:
          java-version: 1.11
      - name: Analyze with SonarCloud
#       set environment variables:
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
#       run sonar maven plugin:
        run: mvn -B verify sonar:sonar -Dsonar.projectKey=antkorwin_github-actions -Dsonar.organization=antkorwin-github -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=$SONAR_TOKEN -Dsonar.coverage.jacoco.xmlReportPaths=./target/site/jacoco/jacoco.xml

SONAR_TOKEN β€” ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Π² sonarcloud.io ΠΈ Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ Π΅Π³ΠΎ Π² secrets. GITHUB_TOKEN β€” это встроСнный Ρ‚ΠΎΠΊΠ΅Π½, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π³Π΅Π½Π΅Ρ€ΠΈΡ‚ Π³ΠΈΡ‚Ρ…Π°Π±, с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π½Π΅Π³ΠΎ sonarcloud[bot] смоТСт Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π² Π³ΠΈΡ‚Π΅, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡΡ‚Π°Π²Π»ΡΡ‚ΡŒ Π½Π°ΠΌ сообщСния Π² ΠΏΡƒΠ»Π»-рСквСстах.

Dsonar.projectKey β€” Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π² сонарС, ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΌΠΎΠΆΠ½ΠΎ Π² настройках ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°.

Dsonar.organization β€” Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΎΡ€Π³Π°Π½ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΈΠ· GitHub.

Π”Π΅Π»Π°Π΅ΠΌ ΠΏΡƒΠ»Π»-рСквСст ΠΈ ΠΆΠ΄Π΅ΠΌ, ΠΊΠΎΠ³Π΄Π° sonarcloud[bot] ΠΏΡ€ΠΈΠ΄Π΅Ρ‚ Π² ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ:

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Release management

Π‘ΠΈΠ»Π΄ настроили, тСсты ΠΏΡ€ΠΎΠ³Π½Π°Π»ΠΈ, ΠΌΠΎΠΆΠ½ΠΎ ΠΈ Ρ€Π΅Π»ΠΈΠ· ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ. Π”Π°Π²Π°ΠΉΡ‚Π΅ посмотрим, ΠΊΠ°ΠΊ GitHub Actions ΠΏΠΎΠΌΠΎΠ³Π°Π΅Ρ‚ сущСствСнно ΡƒΠΏΡ€ΠΎΡΡ‚ΠΈΡ‚ΡŒ release managΠ΅ment.

На Ρ€Π°Π±ΠΎΡ‚Π΅ Ρƒ мСня Π΅ΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρ‹, кодовая Π±Π°Π·Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π»Π΅ΠΆΠΈΡ‚ Π² bitbucket(всС ΠΊΠ°ΠΊ Π² Ρ‚ΠΎΠΉ истории Β«Π΄Π½Π΅ΠΌ ΠΏΠΈΡˆΡƒ Π² Π±ΠΈΡ‚Π±Π°ΠΊΠ΅Ρ‚, Π½ΠΎΡ‡ΡŒΡŽ ΠΊΠΎΠΌΠΌΠΈΡ‡Ρƒ Π² GitHubΒ»). К соТалСнию, Π² bitbucket Π½Π΅Ρ‚ встроСнных срСдств для управлСния Ρ€Π΅Π»ΠΈΠ·Π°ΠΌΠΈ. Π­Ρ‚ΠΎ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ΄ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π΅Π»ΠΈΠ· приходится Ρ€ΡƒΠΊΠ°ΠΌΠΈ Π·Π°Π²ΠΎΠ΄ΠΈΡ‚ΡŒ страничку Π² confluence ΠΈ ΡΠΊΠΈΠ΄Ρ‹Π²Π°Ρ‚ΡŒ Ρ‚ΡƒΠ΄Π° всС Ρ„ΠΈΡ‡ΠΈ вошСдшиС Π² Ρ€Π΅Π»ΠΈΠ·, ΡˆΠ΅Ρ€ΡΡ‚ΠΈΡ‚ΡŒ Ρ‡Π΅Ρ€Ρ‚ΠΎΠ³ΠΈ Ρ€Π°Π·ΡƒΠΌΠ°, таски Π² jira, ΠΊΠΎΠΌΠΌΠΈΡ‚Ρ‹ Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ. Шансов ΠΎΡˆΠΈΠ±ΠΈΡ‚ΡŒΡΡ ΠΌΠ½ΠΎΠ³ΠΎ, ΠΌΠΎΠΆΠ½ΠΎ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π·Π°Π±Ρ‹Ρ‚ΡŒ ΠΈΠ»ΠΈ Π²ΠΏΠΈΡΠ°Ρ‚ΡŒ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΡƒΠΆΠ΅ Ρ€Π΅Π»ΠΈΠ·ΠΈΠ»ΠΈ Π² ΠΏΡ€ΠΎΡˆΠ»Ρ‹ΠΉ Ρ€Π°Π·, ΠΈΠ½ΠΎΠ³Π΄Π° просто Π½Π΅ понятно, ΠΊ Ρ‡Π΅ΠΌΡƒ отнСсти ΠΊΠ°ΠΊΠΎΠΉ-Ρ‚ΠΎ ΠΏΡƒΠ»Π»-рСквСст β€” это Ρ„ΠΈΡ‡Π° ΠΈΠ»ΠΈ фикс Π±Π°Π³ΠΎΠ², ΠΈΠ»ΠΈ ΠΏΡ€Π°Π²ΠΊΠ° тСстов, ΠΈΠ»ΠΈ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ инфраструктурноС.

Как Π½Π°ΠΌ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΌΠΎΡ‡ΡŒ GitHub actions? Π•ΡΡ‚ΡŒ ΠΎΡ‚Π»ΠΈΡ‡Π½Ρ‹ΠΉ экшСн β€” release drafter, ΠΎΠ½ позволяСт Π·Π°Π΄Π°Ρ‚ΡŒ шаблон Ρ„Π°ΠΉΠ»Π° release notes, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΠΈ ΠΏΡƒΠ»Π»-рСквСстов ΠΈ автоматичСски Π³Ρ€ΡƒΠΏΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΈΡ… Π² release notes Ρ„Π°ΠΉΠ»Π΅:

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

ΠŸΡ€ΠΈΠΌΠ΅Ρ€ шаблона для настройки ΠΎΡ‚Ρ‡Π΅Ρ‚Π°(.github/release-drafter.yml):

name-template: 'v$NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
  - title: ' New Features'
    labels:
      - 'type:features'
# Π² эту ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΡŽ собираСм всС PR с ΠΌΠ΅Ρ‚ΠΊΠΎΠΉ type:features

  - title: ' Bugs Fixes'
    labels:
      - 'type:fix'
# Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½ΠΎ для ΠΌΠ΅Ρ‚ΠΊΠΈ type:fix ΠΈ Ρ‚.Π΄.

  - title: ' Documentation'
    labels:
      - 'type:documentation'

  - title: ' Configuration'
    labels:
      - 'type:config'

change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
template: |
  ## Changes
  $CHANGES

ДобавляСм скрипт для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ Ρ‡Π΅Ρ€Π½ΠΎΠ²ΠΈΠΊΠ° Ρ€Π΅Π»ΠΈΠ·Π° (.github/workflows/release-draft.yml):

name: "Create draft release"

on:
  push:
    branches:
      - master

jobs:
  update_draft_release:
    runs-on: ubuntu-18.04
    steps:
      - uses: release-drafter/release-drafter@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

ВсС ΠΏΡƒΠ»Π»-рСквСсты с этого ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° Π±ΡƒΠ΄ΡƒΡ‚ ΡΠΎΠ±ΠΈΡ€Π°Ρ‚ΡŒΡΡ Π² release notes автоматичСски β€” magic!

Π’ΡƒΡ‚ ΠΌΠΎΠΆΠ΅Ρ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ вопрос: Π° Ρ‡Ρ‚ΠΎ Ссли Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ Π·Π°Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΡ€ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΊΠΈ Π² PR? Π’ΠΎΠ³Π΄Π° нСпонятно, Π² ΠΊΠ°ΠΊΡƒΡŽ ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΡŽ Π΅Π³ΠΎ отнСсти, ΠΈ ΠΎΠΏΡΡ‚ΡŒ придСтся Ρ€Π°Π·Π±ΠΈΡ€Π°Ρ‚ΡŒΡΡ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ, с ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ PR-ΠΎΠΌ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎ. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΈΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ эту ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π΅Ρ‰Π΅ ΠΎΠ΄Π½ΠΈΠΌ экшСном β€” label verifier β€” ΠΎΠ½ провСряСт Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ тэгов Π½Π° ΠΏΡƒΠ»Π»-рСквСстС. Если Π½Π΅Ρ‚ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ тэга, Ρ‚ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Π±ΡƒΠ΄Π΅Ρ‚ Π·Π°Π²Π°Π»Π΅Π½Π° ΠΈ сообщСниС ΠΎΠ± этом ΠΌΡ‹ ΡƒΠ²ΠΈΠ΄ΠΈΠΌ Π² нашСм ΠΏΡƒΠ»Π»-рСквСстС.

name: "Verify type labels"

on:
  pull_request:
    types: [opened, labeled, unlabeled, synchronize]

jobs:
  triage:
    runs-on: ubuntu-18.04
    steps:
      - uses: zwaldowski/match-label-action@v2
        with:
          allowed: 'type:fix, type:features, type:documentation, type:tests, type:config'

Π’Π΅ΠΏΠ΅Ρ€ΡŒ любой pull-request Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ ΠΎΠ΄Π½ΠΈΠΌ ΠΈΠ· тэгов: type:fix, type:features, type:documentation, type:tests, type:config.

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Авто-Π°Π½Π½ΠΎΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΏΡƒΠ»Π»-рСквСстов

Π Π°Π· ΡƒΠΆ ΠΌΡ‹ ΠΊΠΎΡΠ½ΡƒΠ»ΠΈΡΡŒ Ρ‚Π°ΠΊΠΎΠΉ Ρ‚Π΅ΠΌΡ‹ ΠΊΠ°ΠΊ эффСктивная Ρ€Π°Π±ΠΎΡ‚Π° с ΠΏΡƒΠ»Π»-рСквСстами, Ρ‚ΠΎ стоит ΡΠΊΠ°Π·Π°Ρ‚ΡŒ Π΅Ρ‰Π΅ ΠΎ Ρ‚Π°ΠΊΠΎΠΌ экшСнС, ΠΊΠ°ΠΊ labeler, ΠΎΠ½ проставляСт ΠΌΠ΅Ρ‚ΠΊΠΈ Π² PR Π½Π° основании Ρ‚ΠΎΠ³ΠΎ, ΠΊΠ°ΠΊΠΈΠ΅ Ρ„Π°ΠΉΠ»Ρ‹ Π±Ρ‹Π»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Ρ‹. НапримСр, ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ ΠΊΠ°ΠΊ [build] любой ΠΏΡƒΠ»-рСквСст Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π΅ΡΡ‚ΡŒ измСнСния Π² ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³Π΅ .github/workflow.

ΠŸΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π΅Π³ΠΎ довольно просто:

name: "Auto-assign themes to PR"

on:
  - pull_request

jobs:
  triage:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/labeler@v2
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}

Π•Ρ‰Π΅ Π½Π°ΠΌ понадобится Ρ„Π°ΠΉΠ» с описаниСм соотвСтствия ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³ΠΎΠ² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° с Ρ‚Π΅ΠΌΠ°Ρ‚ΠΈΠΊΠ°ΠΌΠΈ ΠΏΡƒΠ»Π»-рСквСстов:

theme:build:
  - ".github/**"
  - "pom.xml"
  - ".travis.yml"
  - ".gitignore"
  - "Dockerfile"

theme:code:
  - "src/main/*"

theme:tests:
  - "src/test/*"

theme:documentation:
  - "docs/**"

theme:TRASH:
  - ".idea/**"
  - "target/**"

ΠŸΠΎΠ΄Ρ€ΡƒΠΆΠΈΡ‚ΡŒ дСйствиС автоматичСски ΠΏΡ€ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‰Π΅Π΅ ΠΌΠ΅Ρ‚ΠΊΠΈ Π² ΠΏΡƒΠ»Π»-рСквСсты ΠΈ дСйствиС, ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡŽΡ‰Π΅Π΅ Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠΊ, Ρƒ мСня Π½Π΅ Π²Ρ‹ΡˆΠ»ΠΎ, match-label Π½Π° ΠΎΡ‚Ρ€Π΅Π· Π½Π΅ Ρ…ΠΎΡ‡Π΅Ρ‚ Π²ΠΈΠ΄Π΅Ρ‚ΡŒ проставлСнныС Π±ΠΎΡ‚ΠΎΠΌ ΠΌΠ΅Ρ‚ΠΊΠΈ. ΠŸΠΎΡ…ΠΎΠΆΠ΅ ΠΏΡ€ΠΎΡ‰Π΅ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ своС дСйствиС, ΡΠΎΠ²ΠΌΠ΅Ρ‰Π°ΡŽΡ‰Π΅Π΅ ΠΎΠ±Π° этапа. Но Π΄Π°ΠΆΠ΅ Π² Ρ‚Π°ΠΊΠΎΠΌ Π²ΠΈΠ΄Π΅ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ довольно ΡƒΠ΄ΠΎΠ±Π½ΠΎ, Π½ΡƒΠΆΠ½ΠΎ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ ΠΌΠ΅Ρ‚ΠΊΡƒ ΠΈΠ· списка ΠΏΡ€ΠΈ создании ΠΏΡƒΠ»Π»-рСквСста.

ΠŸΠΎΡ€Π° Π΄Π΅ΠΏΠ»ΠΎΠΈΡ‚ΡŒ

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Π― ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Π» нСсколько Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² дСплоя Ρ‡Π΅Ρ€Π΅Π· GitHub Actions (Ρ‡Π΅Ρ€Π΅Π· ssh, Ρ‡Π΅Ρ€Π΅Π· scp, ΠΈ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ docker-hub), ΠΈ ΠΌΠΎΠ³Ρƒ ΡΠΊΠ°Π·Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ, скорСС всСго, Π²Ρ‹ Π½Π°ΠΉΠ΄Π΅Ρ‚Π΅ способ Π·Π°Π»ΠΈΡ‚ΡŒ Π±ΠΈΠ½Π°Ρ€ΠΊΡƒ Π½Π° сСрвСр, ΠΊΠ°ΠΊΠΈΠΌ Π±Ρ‹ ΠΈΠ·Π²Ρ€Π°Ρ‰Π΅Π½Π½Ρ‹ΠΌ Π½Π΅ Π±Ρ‹Π» ваш pipeline.

МнС понравился Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ всю инфраструктуру Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС, поэтому рассмотрим, ΠΊΠ°ΠΊ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π΄Π΅ΠΏΠ»ΠΎΠΉ Π² GitHub Packages (это Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ для Π±ΠΈΠ½Π°Ρ€Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π°, npm, jar, docker).

CΠΊΠΏΡ€ΠΈΠΏΡ‚ сборки docker ΠΎΠ±Ρ€Π°Π·Π° ΠΈ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ Π΅Π³ΠΎ Π² GitHub Packages:

name: Deploy docker image

on:
  push:
    branches:
      - 'master'

jobs:

  build_docker_image:
    runs-on: ubuntu-18.04
    steps:

#     Build JAR:
      - uses: actions/checkout@v1
      - name: set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: 1.11
      - name: Maven Package
        run: mvn -B clean compile package -DskipTests

#     Set global environment variables:
      - name: set global env
        id: global_env
        run: |
          echo "::set-output name=IMAGE_NAME::${GITHUB_REPOSITORY#*/}"
          echo "::set-output name=DOCKERHUB_IMAGE_NAME::docker.pkg.github.com/${GITHUB_REPOSITORY}/${GITHUB_REPOSITORY#*/}"

#     Build Docker image:
      - name: Build and tag image
        run: |
          docker build -t "${{ steps.global_env.outputs.DOCKERHUB_IMAGE_NAME }}:latest" -t "${{ steps.global_env.outputs.DOCKERHUB_IMAGE_NAME }}:${GITHUB_SHA::8}" .

      - name: Docker login
        run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}

#     Publish image to github package repository:
      - name: Publish image
        env:
          IMAGE_NAME: $GITHUB_REPOSITORY
        run: docker push "docker.pkg.github.com/$GITHUB_REPOSITORY/${{ steps.global_env.outputs.IMAGE_NAME }}"

Для Π½Π°Ρ‡Π°Π»Π° Π½Π°ΠΌ Π½Π°Π΄ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ JAR-Ρ„Π°ΠΉΠ» нашСго прилоТСния, послС Ρ‡Π΅Π³ΠΎ ΠΌΡ‹ вычисляСм ΠΏΡƒΡ‚ΡŒ ΠΊ GitHub docker registry ΠΈ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ нашСго ΠΎΠ±Ρ€Π°Π·Π°. Π’ΡƒΡ‚ Π΅ΡΡ‚ΡŒ нСсколько хитростСй, с ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌΠΈ ΠΌΡ‹ Π΅Ρ‰Π΅ Π½Π΅ ΡΡ‚Π°Π»ΠΊΠΈΠ²Π°Π»ΠΈΡΡŒ:

  • конструкция Π²ΠΈΠ΄Π°: echo «::set-output name=NAME::VALUE» позволяСт Π·Π°Π΄Π°Ρ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ Π² Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΌ шагС, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π΅Π³ΠΎ ΠΏΠΎΡ‚ΠΎΠΌ ΠΌΠΎΠΆΠ½ΠΎ Π±Ρ‹Π»ΠΎ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π²ΠΎ всСх ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… ΡˆΠ°Π³Π°Ρ….
  • ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ установлСной Π½Π° ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ шагС ΠΌΠΎΠΆΠ½ΠΎ Ρ‡Π΅Ρ€Π΅Π· ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ этого шага: ${{ steps.global_env.outputs.DOCKERHUB_IMAGE_NAME }}
  • Π’ стандартной ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ GITHUB_REPOSITORY хранится Π½Π°Π·Π²Π°Π½ΠΈΠ΅ рСпозитория ΠΈ Π΅Π³ΠΎ Π²Π»Π°Π΄Π΅Π»Π΅Ρ† (Β«owner/repo-nameΒ»). Для Ρ‚ΠΎΠ³ΠΎ Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹Ρ€Π΅Π·Π°Ρ‚ΡŒ ΠΈΠ· этой строки всС ΠΊΡ€ΠΎΠΌΠ΅ названия рСпозитория, Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ bash синтаксисом: ${GITHUB_REPOSITORY#*/}

Π”Π°Π»Π΅Π΅ Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ Π΄ΠΎΠΊΠ΅Ρ€-ΠΎΠ±Ρ€Π°Π·:

docker build -t "docker.pkg.github.com/antkorwin/github-actions/github-actions:latest"

ΠΠ²Ρ‚ΠΎΡ€ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π² registry:

docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}

И ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Π· Π² GitHub Packages Repository:

docker push "docker.pkg.github.com/antkorwin/github-actions/github-actions"

Для Ρ‚ΠΎΠ³ΠΎ Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ Π²Π΅Ρ€ΡΠΈΡŽ ΠΎΠ±Ρ€Π°Π·Π°, ΠΌΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΏΠ΅Ρ€Π²Ρ‹Π΅ Ρ†ΠΈΡ„Ρ€Ρ‹ ΠΈΠ· SHA-Ρ…ΡΡˆΠ° ΠΊΠΎΠΌΠΌΠΈΡ‚Π° β€” GITHUB_SHA Ρ‚ΡƒΡ‚ Ρ‚ΠΎΠΆΠ΅ Π΅ΡΡ‚ΡŒ Π½ΡŽΠ°Π½ΡΡ‹, Ссли Π²Ρ‹ Π±ΡƒΠ΄Π΅Ρ‚Π΅ Π΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΈΠ΅ сборки Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΈ merge Π² master, Π° Π΅Ρ‰Π΅ ΠΈ ΠΏΠΎ ΡΠΎΠ±Ρ‹Ρ‚ΠΈΡŽ создания ΠΏΡƒΠ»Π»-рСквСста, Ρ‚ΠΎ SHA ΠΌΠΎΠΆΠ΅Ρ‚ Π½Π΅ ΡΠΎΠ²ΠΏΠ°Π΄Π°Ρ‚ΡŒ с Ρ…ΡΡˆΠ΅ΠΌ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ Π²ΠΈΠ΄ΠΈΠΌ Π² истории Π³ΠΈΡ‚Π°, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ дСйствиС actions/checkout Π΄Π΅Π»Π°Π΅Ρ‚ свой ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ…ΡΡˆ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ Π²Π·Π°ΠΈΠΌΠ½Ρ‹Ρ… Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ дСйствий Π² PR.

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Если всС ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ Π±Π»Π°Π³ΠΎΠΏΠΎΠ»ΡƒΡ‡Π½ΠΎ, Ρ‚ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Π² Ρ€Π°Π·Π΄Π΅Π» packages (https://github.com/antkorwin/github-actions/packages) Π² Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ, Π²Ρ‹ ΡƒΠ²ΠΈΠ΄ΠΈΡ‚Π΅ Π½ΠΎΠ²Ρ‹ΠΉ Π΄ΠΎΠΊΠ΅Ρ€ ΠΎΠ±Ρ€Π°Π·:

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Π’Π°ΠΌ ΠΆΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ список вСрсий Π΄ΠΎΠΊΠ΅Ρ€-ΠΎΠ±Ρ€Π°Π·Π°.

ΠžΡΡ‚Π°Π΅Ρ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π°ΡΡ‚Ρ€ΠΎΠΈΡ‚ΡŒ наш сСрвСр Π½Π° Ρ€Π°Π±ΠΎΡ‚Ρƒ с этим registry ΠΈ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ пСрСзапуск сСрвиса. О Ρ‚ΠΎΠΌ ΠΊΠ°ΠΊ это ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· systemd, я, ΠΏΠΎΠΆΠ°Π»ΡƒΠΉ, расскаТу Π² Π΄Ρ€ΡƒΠ³ΠΎΠΉ Ρ€Π°Π·.

ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³

Π”Π°Π²Π°ΠΉΡ‚Π΅ посмотрим нСслоТный Π²Π°Ρ€ΠΈΠ°Π½Ρ‚, ΠΊΠ°ΠΊ Π΄Π΅Π»Π°Ρ‚ΡŒ health check нашСго прилоТСния ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ GitHub Actions. Π’ нашСм Π±ΡƒΡ‚ΠΎΠ²ΠΎΠΌ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ Π΅ΡΡ‚ΡŒ actuator, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ API для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π΅Π³ΠΎ состояния Π΄Π°ΠΆΠ΅ ΠΈ ΠΏΠΈΡΠ°Ρ‚ΡŒ Π½Π΅ Π½Π°Π΄ΠΎ, для Π»Π΅Π½ΠΈΠ²Ρ‹Ρ… ΡƒΠΆΠ΅ всС сдСлали. НуТно Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π΄Π΅Ρ€Π½ΡƒΡ‚ΡŒ хост: SERVER-URL:PORT/actuator/health

$ curl -v 127.0.0.1:8080/actuator/health

> GET /actuator/health HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.61.1
> Accept: */*

< HTTP/1.1 200
< Content-Type: application/vnd.spring-boot.actuator.v3+json
< Transfer-Encoding: chunked
< Date: Thu, 04 Jun 2020 12:33:37 GMT

{"status":"UP"}

ВсС, Ρ‡Ρ‚ΠΎ Π½Π°ΠΌ Π½ΡƒΠΆΠ½ΠΎ β€” Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ таск ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ сСрвСра ΠΏΠΎ ΠΊΡ€ΠΎΠ½Ρƒ, Π½Ρƒ Π° Ссли Π²Π΄Ρ€ΡƒΠ³ ΠΎΠ½ Π½Π°ΠΌ Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‚ΠΈΡ‚, Ρ‚ΠΎ Π±ΡƒΠ΄Π΅ΠΌ ΡΠ»Π°Ρ‚ΡŒ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅ Π² Ρ‚Π΅Π»Π΅Π³Ρ€Π°ΠΌ.

Для Π½Π°Ρ‡Π°Π»Π° разбСрСмся, ΠΊΠ°ΠΊ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ workflow ΠΏΠΎ ΠΊΡ€ΠΎΠ½Ρƒ:

on:
  schedule:
    - cron:  '*/5 * * * *'

ВсС просто, Π΄Π°ΠΆΠ΅ Π½Π΅ вСрится Ρ‡Ρ‚ΠΎ Π² Π³ΠΈΡ‚Ρ…Π°Π±Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚Π°ΠΊΠΈΠ΅ ΠΈΠ²Π΅Π½Ρ‚Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ совсСм Π½Π΅ ΡƒΠΊΠ»Π°Π΄Ρ‹Π²Π°ΡŽΡ‚ΡΡ Π² webhook-ΠΈ. Π”Π΅Ρ‚Π°Π»ΠΈ Π΅ΡΡ‚ΡŒ Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ: help.github.com/en/actions/reference/events-that-trigger-workflows#scheduled-events-schedule

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ статуса сСрвСра сдСлаСм Ρ€ΡƒΠΊΠ°ΠΌΠΈ Ρ‡Π΅Ρ€Π΅Π· curl:

jobs:
  ping:
    runs-on: ubuntu-18.04
    steps:

      - name: curl actuator
        id: ping
        run: |
          echo "::set-output name=status::$(curl ${{secrets.SERVER_HOST}}/api/actuator/health)"

      - name: health check
        run: |
          if [[ ${{ steps.ping.outputs.status }} != *"UP"* ]]; then
            echo "health check is failed"
            exit 1
          fi
          echo "It's OK"

Π‘Π½Π°Ρ‡Π°Π»Π° сохраняСм Π² ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΎΡ‚Π²Π΅Ρ‚ΠΈΠ» сСрвСр Π½Π° запрос, Π½Π° ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΌ шагС провСряСм Ρ‡Ρ‚ΠΎ статус UP ΠΈ, Ссли это Π½Π΅ Ρ‚Π°ΠΊ, Ρ‚ΠΎ Π²Ρ‹Ρ…ΠΎΠ΄ΠΈΠΌ с ошибкой. Если Π½ΡƒΠΆΠ½ΠΎ Ρ€ΡƒΠΊΠ°ΠΌΠΈ Β«Π·Π°Π²Π°Π»ΠΈΡ‚ΡŒΒ» дСйствиС, Ρ‚ΠΎ exit 1 β€” подходящСС ΠΎΡ€ΡƒΠΆΠΈΠ΅.

  - name: send alert in telegram
    if: ${{ failure() }}
    uses: appleboy/telegram-action@master
    with:
      to: ${{ secrets.TELEGRAM_TO }}
      token: ${{ secrets.TELEGRAM_TOKEN }}
      message: |
        Health check of the:
        ${{secrets.SERVER_HOST}}/api/actuator/health
        failed with the result:
        ${{ steps.ping.outputs.status }}

ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΡƒ Π² Ρ‚Π΅Π»Π΅Π³Ρ€Π°ΠΌ Π΄Π΅Π»Π°Π΅ΠΌ, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли дСйствиС завалилось Π½Π° ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ шагС. Для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ сообщСния ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ appleboy/telegram-action, ΠΎ Ρ‚ΠΎΠΌ, ΠΊΠ°ΠΊ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ‚ΠΎΠΊΠ΅Π½ Π±ΠΎΡ‚Π° ΠΈ id Ρ‡Π°Ρ‚Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ: github.com/appleboy/telegram-action

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

НС Π·Π°Π±ΡƒΠ΄ΡŒΡ‚Π΅ ΠΏΡ€ΠΎΠΏΠΈΡΠ°Ρ‚ΡŒ Π² сСкрСтах Π½Π° Π³ΠΈΡ‚Ρ…Π°Π±Π΅: URL для сСрвСра ΠΈ Ρ‚ΠΎΠΊΠ΅Π½Ρ‹ для Ρ‚Π΅Π»Π΅Π³Ρ€Π°ΠΌ Π±ΠΎΡ‚Π°.

Бонус Ρ‚Ρ€Π΅ΠΊ β€” JIRA для Π»Π΅Π½ΠΈΠ²Ρ‹Ρ…

Π― ΠΎΠ±Π΅Ρ‰Π°Π» Ρ‡Ρ‚ΠΎ ΠΌΡ‹ вСрнСмся ΠΊ JIRA, ΠΈ ΠΌΡ‹ Π²Π΅Ρ€Π½ΡƒΠ»ΠΈΡΡŒ. Π‘ΠΎΡ‚Π½ΠΈ Ρ€Π°Π· наблюдал Π½Π° стСндапах ΡΠΈΡ‚ΡƒΠ°Ρ†ΠΈΡŽ, ΠΊΠΎΠ³Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ сдСлали Ρ„ΠΈΡ‡Ρƒ, слили Π²Π΅Ρ‚ΠΊΡƒ, Π½ΠΎ Π·Π°Π±Ρ‹Π»ΠΈ ΠΏΠ΅Ρ€Π΅Ρ‚ΡΠ½ΡƒΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡Ρƒ Π² JIRA. ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, Ссли Π±Ρ‹ всС это дСлалось Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС, Ρ‚ΠΎ Π±Ρ‹Π»ΠΎ Π±Ρ‹ ΠΏΡ€ΠΎΡ‰Π΅, Π½ΠΎ фактичСски ΠΌΡ‹ пишСм ΠΊΠΎΠ΄ Π² IDE, сливаСм Π²Π΅Ρ‚ΠΊΠΈ Π² bitbucket ΠΈΠ»ΠΈ GitHub, Π° Π·Π°Π΄Π°Ρ‡ΠΈ ΠΏΠΎΡ‚ΠΎΠΌ таскаСм Π² Jira, для этого Π½Π°Π΄ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ Π½ΠΎΠ²Ρ‹Π΅ ΠΎΠΊΠ½Π°, ΠΈΠ½ΠΎΠ³Π΄Π° Π»ΠΎΠ³ΠΈΠ½ΠΈΡ‚ΡŒΡΡ Π΅Ρ‰Π΅ Ρ€Π°Π· ΠΈ Ρ‚.Π΄. Когда Ρ‚Ρ‹ прСкрасно помнишь, Ρ‡Ρ‚ΠΎ Π½Π°Π΄ΠΎ Π΄Π΅Π»Π°Ρ‚ΡŒ дальшС, Ρ‚ΠΎ ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ Π±ΠΎΡ€Π΄Ρƒ лишний Ρ€Π°Π· Π½Π΅Ρ‚ смысла. Π’ ΠΈΡ‚ΠΎΠ³Π΅, ΡƒΡ‚Ρ€ΠΎΠΌ Π½Π° стСндапС Π½Π°Π΄ΠΎ Ρ‚Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ врСмя Π½Π° Π°ΠΊΡ‚ΡƒΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ доски Π·Π°Π΄Π°Ρ‡.

GitHub ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°ΠΌ ΠΈ Π² этом Ρ€ΡƒΡ‚ΠΈΠ½Π½ΠΎΠΌ занятии, для Π½Π°Ρ‡Π°Π»Π° ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠ΅Ρ€Π΅Ρ‚ΡΠ³ΠΈΠ²Π°Ρ‚ΡŒ Π·Π°Π΄Π°Ρ‡ΠΈ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΎΠΌ, Π² ΠΊΠΎΠ»ΠΎΠ½ΠΊΡƒ code_review, ΠΊΠΎΠ³Π΄Π° Π·Π°ΠΊΠΈΠ½ΡƒΠ»ΠΈ ΠΏΡƒΠ»Π»-рСквСст. ВсС, Ρ‡Ρ‚ΠΎ Π½ΡƒΠΆΠ½ΠΎ β€” это ΠΏΡ€ΠΈΠ΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒΡΡ соглашСния Π² Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠΈ Π²Π΅Ρ‚ΠΎΠΊ:

[имя ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°]-[Π½ΠΎΠΌΠ΅Ρ€ таска]-Π½Π°Π·Π²Π°Π½ΠΈΠ΅

Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Ссли ΠΊΠ»ΡŽΡ‡ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Β«GitHub ActionsΒ» Π±ΡƒΠ΄Π΅Ρ‚ GA, Ρ‚ΠΎ GA-8-jira-bot ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Π²Π΅Ρ‚ΠΊΠΎΠΉ для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π·Π°Π΄Π°Ρ‡ΠΈ GA-8.

Π˜Π½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡ с JIRA Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‡Π΅Ρ€Π΅Π· ΡΠΊΡˆΠ΅Π½Ρ‹ ΠΎΡ‚ Atlassian, ΠΎΠ½ΠΈ Π½Π΅ ΠΈΠ΄Π΅Π°Π»ΡŒΠ½Ρ‹, Π½Π°Π΄ΠΎ ΡΠΊΠ°Π·Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΈΠ· Π½ΠΈΡ… Ρƒ мСня Π²ΠΎΠΎΠ±Ρ‰Π΅ Π½Π΅ Π·Π°Ρ€Π°Π±ΠΎΡ‚Π°Π»ΠΈ. Но ΠΌΡ‹ обсудим Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅, Ρ‡Ρ‚ΠΎ Ρ‚ΠΎΡ‡Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ ΠΈ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ.

Для Π½Π°Ρ‡Π°Π»Π° Π½ΡƒΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠΉΡ‚ΠΈ Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΡŽ Π² JIRA ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ дСйствия: atlassian/gajira-login

jobs:
  build:
    runs-on: ubuntu-latest
    name: Jira Workflow
    steps:
      - name: Login
        uses: atlassian/gajira-login@master
        env:
          JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
          JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }}
          JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}

Для этого Π½Π°Π΄ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ‚ΠΎΠΊΠ΅Π½ Π² JIRA, ΠΊΠ°ΠΊ это ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ расписано Ρ‚ΡƒΡ‚: confluence.atlassian.com/cloud/api-tokens-938839638.html

ВычлСняСм ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π·Π°Π΄Π°Ρ‡ΠΈ ΠΈΠ· названия Π²Π΅Ρ‚ΠΊΠΈ:

  - name: Find Issue
    id: find_issue
    shell: bash
    run: |
      echo "::set-output name=ISSUE_ID::$(echo ${GITHUB_HEAD_REF} | egrep -o 'GA-[0-9]{1,4}')"
      echo brach name: $GITHUB_HEAD_REF
      echo extracted issue: ${GITHUB_HEAD_REF} | egrep -o 'GA-[0-9]{1,4}'

  - name: Check Issue
    shell: bash
    run: |
      if [[ "${{steps.find_issue.outputs.ISSUE_ID}}" == "" ]]; then
        echo "Please name your branch according to the JIRA issue: [project_key]-[task_number]-branch_name"
        exit 1
      fi
      echo succcessfully found JIRA issue: ${{steps.find_issue.outputs.ISSUE_ID}}

Если ΠΏΠΎΠΈΡΠΊΠ°Ρ‚ΡŒ Π² GitHub marketplace, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡ‚ΠΈ дСйствиС для этой Π·Π°Π΄Π°Ρ‡ΠΈ, Π½ΠΎ ΠΌΠ½Π΅ ΠΏΡ€ΠΈΡˆΠ»ΠΎΡΡŒ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Ρ‚ΠΎΠΆΠ΅ самоС Ρ‡Π΅Ρ€Π΅Π· grep ΠΏΠΎ названию Π²Π΅Ρ‚ΠΊΠΈ, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ это дСйствиС ΠΎΡ‚ Atlassian Π½ΠΈ Π² ΠΊΠ°ΠΊΡƒΡŽ Π½Π΅ Π·Π°Ρ…ΠΎΡ‚Π΅Π»ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π½Π° ΠΌΠΎΠ΅ΠΌ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅, Ρ€Π°Π·Π±ΠΈΡ€Π°Ρ‚ΡŒΡΡ, Ρ‡Ρ‚ΠΎ ΠΆΠ΅ Ρ‚Π°ΠΌ Π½Π΅ Ρ‚Π°ΠΊ β€” дольшС, Ρ‡Π΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ€ΡƒΠΊΠ°ΠΌΠΈ Ρ‚ΠΎΠΆΠ΅ самоС.

ΠžΡΡ‚Π°Π»ΠΎΡΡŒ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π·Π°Π΄Π°Ρ‡Ρƒ Π² ΠΊΠΎΠ»ΠΎΠ½ΠΊΡƒ Β«Code reviewΒ» ΠΏΡ€ΠΈ создании ΠΏΡƒΠ»Π»-рСквСста:

  - name: Transition issue
    if: ${{ success() }}
    uses: atlassian/gajira-transition@master
    with:
      issue: ${{ steps.find_issue.outputs.ISSUE_ID }}
      transition: "Code review"

Для этого Π΅ΡΡ‚ΡŒ ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠ΅ дСйствиС Π½Π° GitHub, всС, Ρ‡Ρ‚ΠΎ Π΅ΠΌΡƒ Π½ΡƒΠΆΠ½ΠΎ β€” это ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π·Π°Π΄Π°Ρ‡ΠΈ, ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΉ Π½Π° ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ шагС ΠΈ авторизация Π² JIRA, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ Π΄Π΅Π»Π°Π»ΠΈ Π²Ρ‹ΡˆΠ΅.

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

Π’Π°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅Ρ‚ΡΠ³ΠΈΠ²Π°Ρ‚ΡŒ Π·Π°Π΄Π°Ρ‡ΠΈ ΠΏΡ€ΠΈ merge Π² мастСр, ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ… событиях ΠΈΠ· GitHub workflow. Π’ ΠΎΠ±Ρ‰Π΅ΠΌ, всС зависит ΠΎΡ‚ вашСй Ρ„Π°Π½Ρ‚Π°Π·ΠΈΠΈ ΠΈ ТСлания Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ€ΡƒΡ‚ΠΈΠ½Π½Ρ‹Π΅ процСссы.

Π’Ρ‹Π²ΠΎΠ΄Ρ‹

Если ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° ΠΊΠ»Π°ΡΡΠΈΡ‡Π΅ΡΠΊΡƒΡŽ Π΄ΠΈΠ°Π³Ρ€Π°ΠΌΠΌΡƒ DEVOPS, Ρ‚ΠΎ ΠΌΡ‹ ΠΏΠΎΠΊΡ€Ρ‹Π»ΠΈ всС этапы, Ρ€Π°Π·Π²Π΅ Ρ‡Ρ‚ΠΎ ΠΊΡ€ΠΎΠΌΠ΅ operate, Π΄ΡƒΠΌΠ°ΡŽ, Ссли ΠΏΠΎΡΡ‚Π°Ρ€Π°Ρ‚ΡŒΡΡ, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΉΡ‚ΠΈ ΠΊΠ°ΠΊΠΎΠΉ-Π½ΠΈΠ±ΡƒΠ΄ΡŒ экшСн Π² ΠΌΠ°Ρ€ΠΊΠ΅Ρ‚Π΅ для ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ с help-desk систСмой, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ Π±ΡƒΠ΄Π΅ΠΌ ΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ pipeline получился ΠΎΡΠ½ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΈ Π½Π° основании Π΅Π³ΠΎ использования ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Π²Ρ‹Π²ΠΎΠ΄Ρ‹.

ΠšΡ€ΡƒΠ³ΠΈ Π°Π΄Π° с GitHub Actions (строим CI/CD pipeline для Java-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°)

ΠŸΠ»ΡŽΡΡ‹:

  • Marketplace с Π³ΠΎΡ‚ΠΎΠ²Ρ‹ΠΌΠΈ дСйствиями Π½Π° всС случаи ΠΆΠΈΠ·Π½ΠΈ, это ΠΎΡ‡Π΅Π½ΡŒ ΠΊΡ€ΡƒΡ‚ΠΎ. Π’ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π΅ ΠΈΠ· Π½ΠΈΡ… Π΅Ρ‰Π΅ ΠΈ исходники ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ½ΡΡ‚ΡŒ ΠΊΠ°ΠΊ Ρ€Π΅ΡˆΠΈΡ‚ΡŒ ΠΏΠΎΡ…ΠΎΠΆΡƒΡŽ Π·Π°Π΄Π°Ρ‡Ρƒ Π»ΠΈΠ±ΠΎ Π·Π°ΠΏΠΎΡΡ‚ΠΈΡ‚ΡŒ feature request Π°Π²Ρ‚ΠΎΡ€Ρƒ прямо Π² Π³ΠΈΡ‚Ρ…Π°Π± Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ.
  • Π’Ρ‹Π±ΠΎΡ€ Ρ†Π΅Π»Π΅Π²ΠΎΠΉ ΠΏΠ»Π°Ρ‚Ρ„ΠΎΡ€ΠΌΡ‹ для сборки: Linux, mac os, windows довольно интСрСсная Ρ„ΠΈΡ‡Π°.
  • Github Packages отличная Π²Π΅Ρ‰ΡŒ, Π΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ всю инфраструктуру Π² ΠΎΠ΄Π½ΠΎΠΌ мСстС ΡƒΠ΄ΠΎΠ±Π½ΠΎ, Π½Π΅ Π½Π°Π΄ΠΎ ΡΠ΅Ρ€Ρ„ΠΈΡ‚ΡŒ ΠΏΠΎ Ρ€Π°Π·Π½Ρ‹ΠΌ окошкам, всС Π² радиусС ΠΎΠ΄Π½ΠΎΠ³ΠΎ-Π΄Π²ΡƒΡ… ΠΊΠ»ΠΈΠΊΠΎΠ² ΠΌΡ‹ΡˆΠΈ ΠΈ прСкрасно ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Π½ΠΎ с GitHub Actions. ΠŸΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° docker registry Π² бСсплатной вСрсии β€” это Ρ‚ΠΎΠΆΠ΅ Ρ…ΠΎΡ€ΠΎΡˆΠ΅Π΅ прСимущСство.
  • GitHub прячСт сСкрСты Π² Π»ΠΎΠ³Π°Ρ… сборки, поэтому ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΈΠΌ для хранСния ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΉ ΠΈ Ρ‚ΠΎΠΊΠ΅Π½ΠΎΠ² Π½Π΅ Ρ‚Π°ΠΊ ΡƒΠΆ ΠΈ ΡΡ‚Ρ€Π°ΡˆΠ½ΠΎ. Π—Π° всС врСмя экспСримСнтов ΠΌΠ½Π΅ Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π½ΠΈ Ρ€Π°Π·Ρƒ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ сСкрСт Π² чистом Π²ΠΈΠ΄Π΅ Π² консоли.
  • БСсплатСн для Open Source ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΎΠ²

ΠœΠΈΠ½ΡƒΡΡ‹:

  • YML, Π½Ρƒ Π½Π΅ люблю я Π΅Π³ΠΎ. ΠŸΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с Ρ‚Π°ΠΊΠΈΠΌ Ρ„Π»ΠΎΡƒ Ρƒ мСня самый частый commit message это Β«fix yml formatΒ», Ρ‚ΠΎ Π·Π°Π±ΡƒΠ΄Π΅ΡˆΡŒ Π³Π΄Π΅-Ρ‚ΠΎ Ρ‚Π°Π± ΠΏΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ, Ρ‚ΠΎ Π½Π΅ Π½Π° Ρ‚ΠΎΠΉ строкС напишСшь. Π’ ΠΎΠ±Ρ‰Π΅ΠΌ, ΡΠΈΠ΄Π΅Ρ‚ΡŒ ΠΏΠ΅Ρ€Π΅Π΄ экраном с транспортиром ΠΈ Π»ΠΈΠ½Π΅ΠΉΠΊΠΎΠΉ Π½Π΅ самоС приятноС занятиС.
  • DEBUG, ΠΎΡ‚Π»Π°ΠΆΠΈΠ²Π°Ρ‚ΡŒ Ρ„Π»ΠΎΡƒ ΠΊΠΎΠΌΠΌΠΈΡ‚Π°ΠΌΠΈ, запуском пСрСсборки ΠΈ Π²Ρ‹Π²ΠΎΠ΄ΠΎΠΌ Π² консоль Π½Π΅ всСгда ΡƒΠ΄ΠΎΠ±Π½ΠΎ, Π½ΠΎ это большС ΠΈΠ· разряда Β«Π²Ρ‹ Π·Π°ΠΆΡ€Π°Π»ΠΈΡΡŒΒ», ΠΏΡ€ΠΈΠ²Ρ‹ΠΊΠ»ΠΈ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΡƒΠ΄ΠΎΠ±Π½Ρ‹ΠΌΠΈ IDEA, ΠΊΠΎΠ³Π΄Π° ΠΌΠΎΠΆΠ½ΠΎ ΠΎΡ‚Π»Π°ΠΆΠΈΠ²Π°Ρ‚ΡŒ всС, Ρ‡Ρ‚ΠΎ ΡƒΠ³ΠΎΠ΄Π½ΠΎ.
  • Π‘Π²ΠΎΠΉ экшСн ΠΌΠΎΠΆΠ½ΠΎ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Π½Π° Ρ‡Π΅ΠΌ ΡƒΠ³ΠΎΠ΄Π½ΠΎ Ссли Π·Π°Π²Π΅Ρ€Π½ΡƒΡ‚ΡŒ Π΅Π³ΠΎ Π² Π΄ΠΎΠΊΠ΅Ρ€, Π½ΠΎ Π½Π°Ρ‚ΠΈΠ²Π½ΠΎ поддСрТиваСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ javascript, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ это Π΄Π΅Π»ΠΎ вкуса, Π½ΠΎ я Π±Ρ‹ ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡Π΅Π» Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π΄Ρ€ΡƒΠ³ΠΎΠ΅ замСсто js.

Напомню, Ρ‡Ρ‚ΠΎ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ со всСми скриптами Ρ‚ΡƒΡ‚: github.com/antkorwin/github-actions

На ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ Π½Π΅Π΄Π΅Π»Π΅ я Π±ΡƒΠ΄Ρƒ Π²Ρ‹ΡΡ‚ΡƒΠΏΠ°Ρ‚ΡŒ с Π΄ΠΎΠΊΠ»Π°Π΄ΠΎΠΌ Π½Π° ΠΊΠΎΠ½Ρ„Π΅Ρ€Π΅Π½Ρ†ΠΈΠΈ Heisenbug 2020 Piter. РасскаТу Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ, ΠΊΠ°ΠΊ ΠΈΠ·Π±Π΅ΠΆΠ°Ρ‚ΡŒ ошибок ΠΏΡ€ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ΅ тСстовых Π΄Π°Π½Π½Ρ‹Ρ…, Π½ΠΎ ΠΈ подСлюсь своими сСкрСтами Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π½Π°Π±ΠΎΡ€Π°ΠΌΠΈ Π΄Π°Π½Π½Ρ‹Ρ… Π² Java-прилоТСниях!

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com