====== GitHub Actions ====== ===== Введение ===== Сервис GitHub, который позволяет автоматизировать любой процесс связанный с деплоем кода на продакшн сервер. Код проекта выполняется на виртуальных серверах GH и далее доставляется куда нужно\\
:!: more Имеет множество готовых решений, шаблонов и т.д.\\ Файлы располагаются во вложенной директории "./GitHub/workflows/.yml", файлы в формате yml\\ Можно работать в онлайн редакторе\\
==== Workflow ==== Поток выполнения действий\\ Задачи внутри - джобы (**jobs**), которые состоят из шагов (**steps**)\\
:!: more Workflow обычно выполняет какую то большую задачу (очевидно правильно разбивать "одна задача - один воркфлоу"), их может быть множество, могут выполняться как последовательно так и параллельно, как и джобы\\ Может быть вызван автоматически, вручную либо выполняться по расписанию\\ Пример workflow:\\ name: Print workflow # запуск вручную on: workflow_dispatch jobs: # название джобы print_hello: # среда для запуска джобы runs-on: ubuntu-latest steps: - name: Print hello # команда которая будет выполнена в терминале run: echo "Hello world" В этом примере три логических блока: * параметры самого воркфлоу (name и on (пар-р запуска)), * настройка джоб (название и runs-on (инструкция для докера)) * и шаги (так же название и run (команда для выполнения))
==== Jobs ==== "**Джобы выполняются** на разных VM", для выполнения в рамках одной VM есть **steps**\\
:!: Порядок выполнения jobs Параллельное выполнение jobs: print_hello: runs-on: ubuntu-latest steps: - name: Print hello run: echo "Hello world!" print_full_tech_info: runs-on: ubuntu-latest steps: - name: print full_tech_info run: sudo lshw print_short_tech_info: runs-on: ubuntu-latest steps: - name: run: sudo lshw -short В интерфейсе GH, данные джобы будут в одном блоке, вместе, выполнялись параллельно\\ Для последовательного выполнения используется параметр "needs"\\ jobs: print_hello: runs-on: ubuntu-latest steps: - name: print hello run: echo "hello world" print_full_tech_info: needs: print_hello runs-on: ubuntu-latest steps: - name: print_full_tech_info run: sudo lshw print_short_tech_info: needs: print_full_tech_info runs-on: ubuntu-latest steps: - name: print_short_tech_info run: sudo lswh -short В интерфейсе GH джобы будут размещены в разных блоках, друг за другом\\ Если одна из них завершится с ошибкой, то остальные выполняться не будут\\ Такие цепочки зависимостей можно строить и целыми workflow\\ name: my_Workflow # секция условий выполнения текущего флоу on: workflow_run: workflows: ["Run tests"] branches: [main] types: # статус выполнения первого флоу - completed
==== Actions ==== Вместо инструкции "**run**", для вызова команд, можно использовать готовые модули или решения, написанные другими разработчиками, они распространяются бесплатно на маркетплейсе GH\\
:!: пример Например популярный экшен для загрузки кода из репозитория, "checkout@v3"\\ name: print workflow on: workflow_dispatch jobs: checkout: runs-on: ubuntu-latest steps: - name: checkout_repo uses: actions/checkout@v3
==== Примеры ====
:!: Большой пример Главный экшен "ci-production" является точкой входа, внутри запускает доп экшены\\ Выполняются на self-hosted раннерах\\ Настроено несколько окружений, содержащих свой набор переменных, задаются в настройках репозитория\\ "ci-production.yml"\\ Триггеримся на пуш тега с формате "release-"\\ Нюанс в том что сработает пуш этого тега в любую ветку а выпуск релиза предполагается наверно из мастера\\ name: ci-production on: push: tags: - 'release-*' jobs: build: uses: ./.github/workflows/build.yml secrets: inherit deploy-stage: needs: build uses: ./.github/workflows/deploy.yml with: environment: stage secrets: inherit deploy-prod: needs: deploy-stage uses: ./.github/workflows/deploy.yml with: environment: prod secrets: inherit Для запуска тестов делаем flow с триггером на пуш в любую ветку кроме "master", в него как раз предполагается пуш релизного тега, иначе запускаются оба флоу \\ name: ci-test on: push: branches: - '**' - '!master' jobs: build: uses: ./.github/workflows/build.yml secrets: inherit deploy-stage: needs: build uses: ./.github/workflows/deploy.yml with: environment: stage secrets: inherit "build.yml" name: build on: workflow_call: jobs: build: runs-on: - self-hosted - Linux - kube-common steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: 17 distribution: 'adopt' - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v2.1.1 - name: Compile run: sudo bash ./gradlew :compileJava - name: Test run: sudo bash ./gradlew :test - name: Build run: sudo bash ./gradlew :bootJar - name: Inject GitHub variables uses: rlespinasse/github-slug-action@v4 - name: Set docker tags run: | echo 'DOCKER_TAGS<> $GITHUB_ENV # В этой переменной последний тег, если нет собственного то в нем будет название ветки if [[ "$GITHUB_REF_SLUG" =~ "release-".* ]]; then echo "registry.myapp.ru/myapp:$GITHUB_REF_SLUG-$GITHUB_SHA_SHORT" echo "registry.myapp.ru/myapp:latest" else echo "registry.myapp.ru/myapp:branch-$GITHUB_REF_SLUG-$GITHUB_SHA_SHORT" fi >> $GITHUB_ENV echo 'EOF' >> $GITHUB_ENV cat $GITHUB_ENV - name: Login to Registry uses: docker/login-action@v3 with: registry: registry.myapp.ru username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 - name: Build and push image uses: docker/build-push-action@v5 with: context: . file: ./docker/Dockerfile push: true tags: ${{ env.DOCKER_TAGS }} "deploy.yml"\\ Деплоится на удаленный сервер, кредлы ssh сохранены в секретах репы\\ Для применения окружения переменных "stage", нужен блок "environment"\\ name: deploy on: workflow_call: inputs: environment: required: true type: string jobs: deploy: runs-on: - self-hosted - Linux - kube-common environment: name: ${{ inputs.environment }} steps: - uses: actions/checkout@v4 # Для того чтобы задеплоить именно эту версию образа, чтобы и для тестов тоже работало # Указываем в докер-компоуз тег образа который мы добавляли на прошлом шаге - name: Inject GitHub variables uses: rlespinasse/github-slug-action@v4 - name: Get docker-image tags run: | echo 'DOCKER_TAGS<> $GITHUB_ENV if [[ "$GITHUB_REF_SLUG" =~ "release-".* ]]; then echo "$GITHUB_REF_SLUG-$GITHUB_SHA_SHORT" else echo "branch-$GITHUB_REF_SLUG-$GITHUB_SHA_SHORT" fi >> $GITHUB_ENV echo 'EOF' >> $GITHUB_ENV cat $GITHUB_ENV # Далее добавить еще один replace в компоуз - name: Set secrets & environment run: | cd ./docker/ sed -i 's//${{ vars.DATABASE_NAME }}/g' ./docker_compose_myapp.yml sed -i 's//${{ vars.DATABASE_USER_NAME }}/g' ./docker_compose_myapp.yml - name: Deploy on remote host uses: cross-the-world/ssh-scp-ssh-pipelines@latest with: host: ${{ secrets.SSH_HOST }} user: ${{ secrets.SSH_USER }} pass: ${{ secrets.SSH_PASS }} first_ssh: | echo '===== PREPARE HOST =====' sudo mkdir -p /opt/${{ vars.APP_DIRECTORY }}/ sudo mkdir -p /opt/pgdata/ sudo chown -R ${{ secrets.SSH_USER }}. /opt/ scp: | echo '===== SENDING FILES =====' './docker/postgres' => /opt/${{ vars.APP_DIRECTORY }}/ './docker/docker_compose_myapp.yml' => /opt/${{ vars.APP_DIRECTORY }}/ last_ssh: | echo '===== LOGIN TO REGISTRY =====' sudo docker login registry.myapp.io -u '${{ secrets.DOCKER_USERNAME }}' -p '${{ secrets.DOCKER_PASSWORD }}' echo '===== RESTART DOCKER-COMPOSE =====' cd /opt/${{ vars.APP_DIRECTORY }}/ sudo docker-compose -f docker_compose_myapp.yml down sudo docker-compose -f docker_compose_myapp.yml up -d
==== ====