В предыдущей статье мы познакомились с GitHub Actions и некоторыми его концепциями. В этой статье мы более плотно поработаем с GitHub Actions: научимся управлять окружением с помощью переменных, создавать секреты, артефакты и использовать их в workflow.
Перед разбором новой темы, освежим память и вспомним, что такое GitHub Actions и какие сущности там есть. GitHub Actions — это сервис от GitHub для автоматизации различных CI/CD-процессов: начиная от тестирования, заканчивая деплоем кода на продакшен серверы. Actions (экшены) — это модули автоматизации различных сценариев, таких как загрузка репозитория на GitHub VM или подключение по SSH.
В GitHub Actions есть три базовые сущности, с которыми мы работаем:
- Workflow — это настраиваемый сценарий выполнения потока задач( jobs/джобы);
- Jobs (джобы) — это логическое объединение шагов сценария, на котором задается среда выполнения задач;
- Steps (шаги) — конкретные действия выполнения сценария.
Каждый из уровней абстракции имеет свои возможности работы с переменными.
Работа с переменными в gitHub Actions
На самом деле переменные могут быть заданы не только на уровне workflow, джобов и шагов, но и на уровне репозитория и организации. Организация — это административная сущность, которая может управлять репозиториями. С помощью организации можно создавать переменные, доступные для всех репозиториев.
Уровень репозитория позволяет создавать переменные доступные для всех workflow в рамках родительского репозитория. Вот схема иерархии переменных с указателем контекста их вызова:
Переменные уровня GitHub содержат данные о репозитории, владельце, workflow и так далее. Они задаются автоматически и их можно только читать. Остальные переменные можно задавать. Наиболее часто придётся работать с переменными уровня workflow. C них и начнём.
Работа с переменными уровня workflow
Переменные уровня workflow задаются вначале файла директивой env: [variable_name]: [variable_value]. Эти переменные доступны из любого подуровня workflow. Такие переменные можно использовать для хранения путей или иной текстовой информации, которую можно переиспользовать множество раз в каждой джобе.[/variable_value][/variable_name]
Вот яркий пример использования переменных уровня workflow для управления путями и именами создаваемых файлов:
name: Test env
on: workflow_dispatch
env:
work_path: ./project
project_name: ./pyoops.com
project_files_set_1: main.py db_python.sql readme.md
project_files_set_2: numerics.js readme.md base.json
jobs:
create_workdir_and_files_on_1_vm:
runs-on: ubuntu-latest
steps:
- name: Make work dir and create files
run: |
mkdir ${{ env.work_path }}
cd ${{ env.work_path }}
mkdir ${{ env.project_name }}
cd ${{ env.project_name }}
touch ${{ env.project_files_set_1 }}
ls -la
create_workdir_and_files_on_2_vm:
runs-on: ubuntu-latest
steps:
- name: Make work dir and create files
run: |
mkdir ${{ env.work_path }}
cd ${{ env.work_path }}
mkdir ${{ env.project_name }}
cd ${{ env.project_name }}
touch ${{ env.project_files_set_2 }}
ls -la
В данном примере мы создали в начале workflow раздел env с четырьмя переменными:
env:
work_path: ./project
project_name: ./pyoops.com
project_files_set_1: main.py db_python.sql readme.md
project_files_set_2: numerics.js readme.md base.json
Далее мы обращались к этим переменным из разных джобов с помощью контекста env: ${{ env.project_name }}. Важно понимать, что каждая джоба запускается на отдельной виртуальной машине, соответственно вы не можете использовать переменные созданные в одной джобе в другой.
С переменными уровня workflow — всё, теперь опустимся на один логический уровень ниже и посмотрим как работают переменные на уровне джобы.
Работа с переменными уровня jobs
Специфика в работа с переменными на уровне джобы заключается в том, что эти переменные доступны только в рамках джобы, где были заданы. Задаются переменные также с помощью директивы env:, с указанием имени переменной и значением.
Вот пример задания переменных в джобе:
name: Test env
on: workflow_dispatch
env:
work_path: ./project
project_name: ./pyoops.com
project_files_set_1: main.py db_python.sql readme.md
jobs:
create_workdir_and_files_on_1_vm:
runs-on: ubuntu-latest
env:
my_project_name: pseudoproject
steps:
- name: Make work dir and create files
run: |
mkdir ${{ env.work_path }}
cd ${{ env.work_path }}
mkdir ${{ env.my_project_name }}
cd $my_project_name
touch ${{ env.project_files_set_1 }}
Если более внимательно рассмотреть блок run, где мы построчно вызываем команды — можно заметить, что к переменной my_project_name мы обратились дважды, но по разному: в первом случае через контекст env — mkdir ${{ env.my_project_name }}, а во втором случае — через вызов переменной Linux-окружения — cd $my_project_name.
Технически конструкция установки переменных вида env: my_project_name: pseudoproject равнозначна ручному назначению переменных из Linux-терминала командой export my_project_name=pseudoproject. То есть GitHub Actions при создании виртуальной машины прогружает в виртуальное окружение Linux переменные заданные в workflow, поэтому мы можем обращаться к переменным как через контекст env, так и просто по имени, также как в терминале Linux.
Хорошо, до это мы только брали заранее предопределенное значения из переменных и работали с ним, но мы можем также и создавать переменные по ходу выполнения workflow и присваивать им результаты выполнения команд, однако делается это необычно, поэтому сначала расскажем о том, где хранятся переменные окружения внутри джобов.
Переменные окружения джобы хранится в специальном контейнере, который обозначается как переменная $GITHUB_ENV. В неё с помощью нехитрой команды echo [variable_name]=[variable.value] > $GITHUB_ENV можно записать пользовательскую переменную с каким-то значением, после её можно вызвать через контекст env или через $.
Вот пример, где мы узнаём внутренний IP адрес виртуальной машины GitHub, передаем его в контейнер $GITHUB_ENV и читаем потом это значение двумя способами:
name: Test env
on: workflow_dispatch
jobs:
crete_var_env:
runs-on: ubuntu-latest
steps:
- name: create var
run:
echo "SERVER_IP='$(hostname -I | awk '{print $1}')'" >> $GITHub_ENV
- name: read var
run: |
echo "Calling var from env using env context: server ip is ${{ env.SERVER_IP }}"
echo "Calling var from env: server ip is $SERVER_IP"
Вот конструкция, которая создает переменную SERVER_IP, присваивает ей значение и записывает в контейнер $GITHUB_ENV:
run:
echo "SERVER_IP='$(hostname -I | awk '{print $1}')'" >> $GITHub_ENV
Далее мы обращаемся к переменной SERVER_IP двумя способами: через контекст env — ${{ env.SERVER_IP }} и без него — $SERVER_IP. В обоих случаях мы получаем одинаковый результат, а в выводе консоли выполнения workflow, мы видим, что запись с использованием команды echo равнозначна созданию блока env:
Так мы можем использовать переменные окружения для передачи данных между различными шагами внутри одной джобы. Передавать данные между джобами тоже возможно, но там задействован совершенно другой механизм, о нём мы расскажем в следующей статье, где будем непосредственно применять на практике все полученные знания о GitHub Actions для деплоя репозитория на стейджинг сервер.
Теперь, когда мы разобрались с тем, как работают переменные уровня workflow и jobs, можно перейти к рассмотрению специфических переменных, которые используются в GitHub для хранения секретных данных.
Работа с секретами GitHub
Секреты GitHub — это переменные, которые могут содержать такую, секретную информацию как API-токены, SHH-ключи, SSH-пароли и другую информацию, которую нужно передать в качестве аргумента в workflow. Фишка секретов в том, что их невозможно вывести на печать в терминале или как-то раскрыть. К их созданию есть доступ только у владельца репозитория или организации. К тому же отображаются данные внутри секрета только один раз, при его создании, потом невозможно будет узнать, что в нём записано.
Вот как можно быстро создать секрет:
Обратиться к секрету из workflow можно через контекст secrets: ${{ secrets.[secret_name] }}. Вот пример, как можно передать SSH-ключ в Python для создания VPS (более подробнее об этом будет в следующей статье):
- name: "Run the VPS create script"
run: |
pip install requests
python3 ./1cloud_create_vps.py ${{ secrets.API_KEY }}
Обратиться к секрету из workflow можно через контекст secrets: ${{ secrets.[secret_name] }}. Вот пример, как можно передать SSH-ключ в Python для создания VPS (более подробнее об этом будет в следующей статье):
- name: "Run the VPS create script"
run: |
pip install requests
python3 ./1cloud_create_vps.py ${{ secrets.API_KEY }}
А вот другой яркий пример использования секретов — подключения по SSH к VPS и скачивания на сервер репозитория:
- name: "Upload repo to VPS"
uses: appleboy/ssh-action@master
with:
host: ${{ env.VPS_IP }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_KEY }}
script: |
mkdir ${{ env.PROJECT_DIR }}
cd ${{ env.PROJECT_DIR }}
git clone ${{ env.REPO_LINK }}
Помимо переменных и секретов есть ещё одна сущность, которая позволяет сохранять в себе данные и, в отличии от переменных, данные сохраняются на длительное время и могут передаваться между джобами.
Работа с артефактами
Артефакты — это файлы, которые можно передавать между джобами. Артефакты хранятся в специальных контейнерах отдельно от репозиториев в виртуальном пространстве GitHub. По дефолту срок хранения артефактов 90 дней. Как правило, артефакты содержат в себе простую текстовую информацию или данные в формате json.
Перейдём сразу к коду и покажем, как можно создать и прочитать простенький артефакт в виде текстового файла, содержащий одну строку текста:
name: Working with artifacts
on: workflow_dispatch
env:
file_name: artifact_body.txt
path: ./
artifact_name: artifact_1
jobs:
create_artifact:
runs-on: ubuntu-latest
steps:
- name: Create artifact data
run:
echo "This is body of artifact_1." > ${{ env.path }}${{ env.file_name }}
- name: Save Artifact
uses: actions/upload-artifact@v3
with:
name: ${{ env.artifact_name }}
path: ${{ env.path }}${{ env.file_name }}
read_artifact:
needs: create_artifact
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ env.artifact_name }}
path: ${{ env.path }}
- name: Read artifact
run:
echo "${{ env.artifact_name }}:" && cat ${{ env.path }}${{ env.file_name }}
В двух словах расскажем, как тут всё работает:
- На шаге Create artifact data, с помощью команды echo создаем файл artifact_body.txt и записываем в него "This is body of artifact_1.";
- На следующем шаге Save Artifact, мы вызываем готовый экшен actions/upload-artifact@v3 и с помощью директивы with задаём:
- name — имя артефакта, который будет создан;
- path — путь до содержимого файла, из которого будут прочитаны данные.
В результате работы экшена мы увидим следующую информацию:
Артефакты уникальны для каждого workflow. Просмотреть содержимое артефакта и скачать его на локальную машину можно из раздела выполнения workflow:
- В следующей джобе на шаге Download artifact, мы опять используем готовый экшен actions/download-artifact@v3 и с помощью директивы with передаем следующие параметры:
Если артефакт был найден и загружен, то в терминале GitHub VM будет выведено следующие сообщение:
- name — название артефакты для скачивания
- path — директория для скачивания артефакта на GitHub VM
- На последнем шаге мы вводим в терминал название артефакта и его содержимое с помощью связки команды echo и cat: echo "${{ env.artifact_name }}:" && cat ${{ env.path }}${{ env.file_name }}.
Так с помощью артефактов можно передавать данные из одной джобы в другую. Хорошо, мы разобрались как работать с переменными окружения, секретами и артефактами. Настало время собрать все знания воедино и подвести итог.
Что нужно знать о переменных, секретах и артефактах?
В GitHub Actions переменные разделяются по уровням доступа: начиная от переменных уровня GitHub, которые доступны с любого шага workflow до переменных окружения джобы, доастых только для текущей джобы. Наиболее часто придется работать с переменными окружения workflow и джобы.
Переменные уровня workflow задаются вначале файла директивой env, с указанием названия переменных и их значением:
env:
vm_name: test_vps_1
vm_ip: 192.123.456.18
С помощью такой же конструкции задаются переменные окружения уровня джобы. Напомним, что каждая джоба запускается на своей отдельной виртуальной машине, поэтому переменные созданные в одной джобе не доступна из другой джобы.
Переменные уровня джобы можно задавать вручную с помощью директивы env или назначать через терминал внутри GitHub VM с помощью следующей конструкции:
run:
echo "[VAR_NAME]:[VAR_VALUE]" >> $GITHUB_ENV
$GITHUB_ENV — это контейнер для хранения переменных, куда можно добавлять пользовательские переменные. Обращаться к переменным можно через контекст, определяющий их зону действия или на прямую по имени, если это переменные виртуального окружения Linux:
- ${{ env.[VARIABLE_NAME] }} — обращение к переменным через контекст env уровня workflow и уровня джобы;
- $[VARIABLE_NAME] — прямое обращение к переменным виртуального окружения Linux.
Для обращения к секретам GitHub Actions используется контекст ${{ secrets.[VARIABLE_NAME] }}. Секреты используются для хранения секретной информации, которая не может быть распечатана в терминале и её содержимое появляется только один раз при создании секрета.
К переменным уровня workflow и секретам можно обращаться с любого шага выполнения workflow, но они статичны, для передачи динамических данных между джобами в рамках одного workflow применяются артефакты — это файлы, содержащие текстовую информацию и хранящиеся в отдельных контейнерах на GitHub VM.
Для создания и чтения артефактов используются готовые экшены:
jobs:
create_artifact:
runs-on: ubuntu-latest
steps:
- name: Create artifact data
run:
echo "This is body of artifact_1." > ${{ env.path }}${{ env.file_name }}
- name: Save Artifact
uses: actions/upload-artifact@v3
with:
name: ${{ env.artifact_name }}
path: ${{ env.path }}${{ env.file_name }}
read_artifact:
needs: create_artifact
runs-on: ubuntu-latest
steps:
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ env.artifact_name }}
path: ${{ env.path }}
- name: Read artifact
run:
echo "${{ env.artifact_name }}:" && cat ${{ env.path }}${{ env.file_name }}
В следующей статье мы объединим все знания о GitHub Actions и создадим pipline автоматического деплоя одного из наших проектов на виртуальный сервер 1cloud. Настоятельно рекомендуем ознакомиться с предыдущими публикациями — они помогут лучше понять текущий материал и подготовится к прочтению следующей статьи.
GitHub Actions: знакомство и первые шаги
Разбираемся в GitHub Actions: что такое GitHub Actions, из каких элементов он состоит и как им пользоваться.
Git: работа с gitHUB
Знакомимся с gitHub: рассматриваем его базовые понятия, органы управления и функционал.
Git: коммиты, ветки и перемещение между ними
Разбираемся с объектами git: создаём ветви, коммиты, мерджим их и работаем с зонами видимости в git.