292 lines
16 KiB
YAML
292 lines
16 KiB
YAML
name: deploy-preland
|
||
|
||
# После коммита в main сохраняется legacy-выпуск на Beget. Новые домены ZoomPixel
|
||
# доставляются отдельными SFTP job: staging — только на stage, main — только на production.
|
||
# Цель — домен со своим хостингом (список — matrix ниже): реквизиты — секреты репозитория <prefix>_FTP_HOST,
|
||
# <prefix>_FTP_USER, <prefix>_FTP_PASS (порт 21). Цели независимы: сбой одного хостинга остальные не останавливает.
|
||
# Заливаются файлы репозитория (git ls-files), кроме служебных (.gitea/, README.md, AGENTS.md, CLAUDE.md, .gitignore) и тех, что живут
|
||
# только на хостинге: requisites.html (реквизиты заполняют на месте — пустой файл из git их бы затёр).
|
||
# Рабочий config.json настраивается владельцем на каждом хостинге и является
|
||
# источником истины. Обычный deploy его не перезаписывает; задача может менять
|
||
# только прямо согласованные ключи после чтения текущего host-файла.
|
||
# На хостинге ничего не удаляется: удалённое из репозитория убирать вручную.
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- main
|
||
- staging
|
||
|
||
jobs:
|
||
deploy:
|
||
name: deploy ${{ matrix.name }}
|
||
if: github.ref == 'refs/heads/main'
|
||
# Legacy — тоже production-выпуск: выполняем на выделенном runner, а не
|
||
# на общем Alpine-пуле, чтобы не зависеть от внешних apk mirrors.
|
||
runs-on: zoomzilla-production
|
||
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
# name — имя цели, url — адрес преленда (для проверки), prefix — префикс секретов <prefix>_FTP_*,
|
||
# dir — каталог сайта на FTP ("" — корень FTP, как на beget). Новый домен — новая запись и три секрета;
|
||
# requisites.html создают на хостинге цели. Рабочий config.json остаётся
|
||
# на хостинге и обычным deploy не заменяется.
|
||
- name: barbariskatest
|
||
url: https://preland.anton.barbariskatest.ru
|
||
prefix: PRELAND
|
||
dir: ""
|
||
|
||
defaults:
|
||
run:
|
||
shell: sh
|
||
|
||
env:
|
||
PRELAND_URL: ${{ matrix.url }}
|
||
REMOTE_DIR: ${{ matrix.dir }}
|
||
|
||
steps:
|
||
- name: Install tools
|
||
run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates git curl lftp php-cli
|
||
|
||
# На production runner образ задания не содержит Node.js, поэтому
|
||
# actions/checkout не запускается. Клонируем тот же main напрямую:
|
||
# это не меняет содержимое host config.json.
|
||
- name: Checkout production branch
|
||
run: git clone --depth 1 --branch main http://git.dira89kv.beget.tech:3000/anton/preland.git .
|
||
|
||
- name: Preserve host config and add focus if absent
|
||
env:
|
||
FTP_HOST: ${{ secrets[format('{0}_FTP_HOST', matrix.prefix)] }}
|
||
FTP_USER: ${{ secrets[format('{0}_FTP_USER', matrix.prefix)] }}
|
||
FTP_PASS: ${{ secrets[format('{0}_FTP_PASS', matrix.prefix)] }}
|
||
run: |
|
||
set -eu
|
||
[ -n "$FTP_HOST" ] && [ -n "$FTP_USER" ] && [ -n "$FTP_PASS" ] || { echo "нет секретов ${{ matrix.prefix }}_FTP_*"; exit 1; }
|
||
dir=$(printf '%s' "$REMOTE_DIR" | sed 's#^/*##; s#/*$##')
|
||
umask 077
|
||
netrc=$(mktemp); config=$(mktemp)
|
||
trap 'rm -f "$netrc" "$config"' EXIT
|
||
printf 'machine %s login %s password %s\n' "$FTP_HOST" "$FTP_USER" "$FTP_PASS" > "$netrc"
|
||
curl -sS --ssl-reqd --netrc-file "$netrc" --fail --retry 3 \
|
||
"ftp://$FTP_HOST/${dir:+$dir/}config.json" -o "$config"
|
||
changed=0
|
||
php bin/ensure-focus-config.php "$config" || changed=$?
|
||
case "$changed" in
|
||
0) ;;
|
||
10) curl -sS --ssl-reqd --netrc-file "$netrc" --ftp-create-dirs --retry 3 --fail \
|
||
-T "$config" "ftp://$FTP_HOST/${dir:+$dir/}config.json" ;;
|
||
*) exit "$changed" ;;
|
||
esac
|
||
|
||
- name: Upload over FTP (TLS)
|
||
env:
|
||
FTP_HOST: ${{ secrets[format('{0}_FTP_HOST', matrix.prefix)] }}
|
||
FTP_USER: ${{ secrets[format('{0}_FTP_USER', matrix.prefix)] }}
|
||
FTP_PASS: ${{ secrets[format('{0}_FTP_PASS', matrix.prefix)] }}
|
||
run: |
|
||
set -eu
|
||
[ -n "$FTP_HOST" ] && [ -n "$FTP_USER" ] && [ -n "$FTP_PASS" ] || { echo "нет секретов ${{ matrix.prefix }}_FTP_*"; exit 1; }
|
||
dir=$(printf '%s' "$REMOTE_DIR" | sed 's#^/*##; s#/*$##')
|
||
case "$dir" in *[!A-Za-z0-9._/-]*) echo "недопустимый dir: $dir"; exit 1 ;; esac
|
||
umask 077
|
||
script=$(mktemp)
|
||
trap 'rm -f "$script"' EXIT
|
||
git ls-files | grep -vE '^(\.gitea/|README\.md$|AGENTS\.md$|CLAUDE\.md$|\.gitignore$|requisites\.html$|config\.json$)' > files.txt
|
||
# имя уходит в адрес ftp:// как есть — пробелы и прочее экранирование не поддерживаем
|
||
if grep -q '[^A-Za-z0-9._/-]' files.txt; then echo "имена с недопустимыми символами:"; grep '[^A-Za-z0-9._/-]' files.txt; exit 1; fi
|
||
{
|
||
echo 'set cmd:fail-exit yes'
|
||
echo 'set ftp:ssl-force true'
|
||
echo 'set ftp:ssl-protect-data true'
|
||
echo 'set ssl:verify-certificate yes'
|
||
echo 'set net:max-retries 3'
|
||
echo 'set net:timeout 30'
|
||
} > "$script"
|
||
# Сервер legacy обрывает бинарные загрузки curl с 426. lftp ведёт
|
||
# каждую передачу отдельной FTPS-командой, не ослабляя TLS.
|
||
while read -r f; do
|
||
echo "загрузка: $f"
|
||
remote_dir=${f%/*}
|
||
[ "$remote_dir" = "$f" ] && remote_dir='.'
|
||
printf 'mkdir -p %s%s\nput -O %s%s %s\n' \
|
||
"${dir:+$dir/}" "$remote_dir" "${dir:+$dir/}" "$remote_dir" "$f" >> "$script"
|
||
done < files.txt
|
||
printf 'bye\n' >> "$script"
|
||
lftp -u "$FTP_USER","$FTP_PASS" "ftp://$FTP_HOST" -f "$script"
|
||
echo "залито файлов: $(wc -l < files.txt)"
|
||
|
||
- name: Check the page
|
||
run: |
|
||
set -eu
|
||
page=$(curl -fsS --max-time 30 "$PRELAND_URL/?nc=$(date +%s)")
|
||
api=$(printf '%s' "$page" | grep -o 'const createApiUrl = "[^"]*"' || true)
|
||
echo "$api"
|
||
case "$api" in
|
||
*'\/api\/images\/public\/create'*) ;;
|
||
*) echo "на странице нет адреса загрузки"; exit 1 ;;
|
||
esac
|
||
own=$(printf '%s' "$PRELAND_URL" | sed -E 's#^https?://([^/]+).*#\1#')
|
||
case "$api" in
|
||
*"$own"*) echo "загрузка уходит на сам преленд: на хостинге нет config.json с host"; exit 1 ;;
|
||
esac
|
||
printf '%s' "$page" | grep -q 'claim?key=' || { echo "переход не на claim — залит старый код?"; exit 1; }
|
||
echo "страница в порядке"
|
||
|
||
deploy-zoomstage:
|
||
name: deploy zoomstage.online
|
||
if: github.ref == 'refs/heads/staging'
|
||
runs-on: zoomzilla-production
|
||
defaults:
|
||
run:
|
||
shell: sh
|
||
env:
|
||
PRELAND_URL: https://zoomstage.online
|
||
REMOTE_DIR: domains/zoomstage.online
|
||
steps:
|
||
- name: Install tools
|
||
run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates git curl openssh-client sshpass php-cli
|
||
|
||
- name: Checkout staging branch
|
||
run: git clone --depth 1 --branch staging http://git.dira89kv.beget.tech:3000/anton/preland.git .
|
||
|
||
- name: Preserve host config and add focus if absent
|
||
env:
|
||
SFTP_HOST: ${{ secrets.ZOOMPIXEL_SFTP_HOST }}
|
||
SFTP_USER: ${{ secrets.ZOOMPIXEL_SFTP_USER }}
|
||
SFTP_PASS: ${{ secrets.ZOOMPIXEL_SFTP_PASS }}
|
||
SFTP_KNOWN_HOSTS: ${{ secrets.ZOOMPIXEL_SFTP_KNOWN_HOSTS }}
|
||
run: |
|
||
set -eu
|
||
[ -n "$SFTP_HOST" ] && [ -n "$SFTP_USER" ] && [ -n "$SFTP_PASS" ] && [ -n "$SFTP_KNOWN_HOSTS" ] || { echo "не заданы секреты ZOOMPIXEL_SFTP_*"; exit 1; }
|
||
umask 077
|
||
known_hosts=$(mktemp); batch=$(mktemp); config=$(mktemp)
|
||
trap 'rm -f "$known_hosts" "$batch" "$config"' EXIT
|
||
printf '%s\n' "$SFTP_KNOWN_HOSTS" > "$known_hosts"
|
||
printf '%s\n' "get -p $REMOTE_DIR/config.json $config" > "$batch"
|
||
SSHPASS="$SFTP_PASS" sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=yes -oUserKnownHostsFile="$known_hosts" "$SFTP_USER@$SFTP_HOST" < "$batch"
|
||
changed=0
|
||
php bin/ensure-focus-config.php "$config" || changed=$?
|
||
case "$changed" in
|
||
0) ;;
|
||
10) printf '%s\n' "put -p $config $REMOTE_DIR/config.json" > "$batch"
|
||
SSHPASS="$SFTP_PASS" sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=yes -oUserKnownHostsFile="$known_hosts" "$SFTP_USER@$SFTP_HOST" < "$batch" ;;
|
||
*) exit "$changed" ;;
|
||
esac
|
||
|
||
- name: Upload over SFTP
|
||
env:
|
||
SFTP_HOST: ${{ secrets.ZOOMPIXEL_SFTP_HOST }}
|
||
SFTP_USER: ${{ secrets.ZOOMPIXEL_SFTP_USER }}
|
||
SFTP_PASS: ${{ secrets.ZOOMPIXEL_SFTP_PASS }}
|
||
SFTP_KNOWN_HOSTS: ${{ secrets.ZOOMPIXEL_SFTP_KNOWN_HOSTS }}
|
||
run: |
|
||
set -eu
|
||
[ -n "$SFTP_HOST" ] && [ -n "$SFTP_USER" ] && [ -n "$SFTP_PASS" ] && [ -n "$SFTP_KNOWN_HOSTS" ] || { echo "не заданы секреты ZOOMPIXEL_SFTP_*"; exit 1; }
|
||
umask 077
|
||
known_hosts=$(mktemp)
|
||
batch=$(mktemp)
|
||
files=$(mktemp)
|
||
trap 'rm -f "$known_hosts" "$batch" "$files"' EXIT
|
||
printf '%s\n' "$SFTP_KNOWN_HOSTS" > "$known_hosts"
|
||
git ls-files | grep -vE '^(\.gitea/|README\.md$|AGENTS\.md$|CLAUDE\.md$|\.gitignore$|requisites\.html$|config\.json$)' > "$files"
|
||
if grep -q '[^A-Za-z0-9._/-]' "$files"; then echo "имена с недопустимыми символами:"; grep '[^A-Za-z0-9._/-]' "$files"; exit 1; fi
|
||
printf '%s\n' "-mkdir $REMOTE_DIR" > "$batch"
|
||
sed 's#/[^/]*$##' "$files" | grep -v '^$' | sort -u | while read -r dir; do printf '%s\n' "-mkdir $REMOTE_DIR/$dir" >> "$batch"; done
|
||
while read -r file; do printf '%s\n' "put -p $file $REMOTE_DIR/$file" >> "$batch"; done < "$files"
|
||
SSHPASS="$SFTP_PASS" sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=yes -oUserKnownHostsFile="$known_hosts" "$SFTP_USER@$SFTP_HOST" < "$batch"
|
||
echo "залито файлов: $(wc -l < "$files")"
|
||
|
||
- name: Check the page
|
||
run: |
|
||
set -eu
|
||
page=$(curl -fsS --max-time 30 "$PRELAND_URL/?nc=$(date +%s)")
|
||
api=$(printf '%s' "$page" | grep -o 'const createApiUrl = "[^"]*"' || true)
|
||
echo "$api"
|
||
case "$api" in *'zoomzilla.ru\/api\/images\/public\/create'*) ;; *) echo "на странице нет адреса загрузки Zoomzilla"; exit 1 ;; esac
|
||
printf '%s' "$page" | grep -q 'claim?key=' || { echo "переход не на claim"; exit 1; }
|
||
|
||
deploy-zoompixel-production:
|
||
name: deploy ${{ matrix.name }}
|
||
if: github.ref == 'refs/heads/main'
|
||
runs-on: zoomzilla-production
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- name: zoompixel.ru
|
||
url: https://zoompixel.ru
|
||
dir: domains/zoompixel.ru
|
||
- name: zoompixel.online
|
||
url: https://zoompixel.online
|
||
dir: domains/zoompixel.online
|
||
defaults:
|
||
run:
|
||
shell: sh
|
||
env:
|
||
PRELAND_URL: ${{ matrix.url }}
|
||
REMOTE_DIR: ${{ matrix.dir }}
|
||
steps:
|
||
- name: Install tools
|
||
run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates git curl openssh-client sshpass php-cli
|
||
|
||
- name: Checkout production branch
|
||
run: git clone --depth 1 --branch main http://git.dira89kv.beget.tech:3000/anton/preland.git .
|
||
|
||
- name: Preserve host config and add focus if absent
|
||
env:
|
||
SFTP_HOST: ${{ secrets.ZOOMPIXEL_SFTP_HOST }}
|
||
SFTP_USER: ${{ secrets.ZOOMPIXEL_SFTP_USER }}
|
||
SFTP_PASS: ${{ secrets.ZOOMPIXEL_SFTP_PASS }}
|
||
SFTP_KNOWN_HOSTS: ${{ secrets.ZOOMPIXEL_SFTP_KNOWN_HOSTS }}
|
||
run: |
|
||
set -eu
|
||
[ -n "$SFTP_HOST" ] && [ -n "$SFTP_USER" ] && [ -n "$SFTP_PASS" ] && [ -n "$SFTP_KNOWN_HOSTS" ] || { echo "не заданы секреты ZOOMPIXEL_SFTP_*"; exit 1; }
|
||
umask 077
|
||
known_hosts=$(mktemp); batch=$(mktemp); config=$(mktemp)
|
||
trap 'rm -f "$known_hosts" "$batch" "$config"' EXIT
|
||
printf '%s\n' "$SFTP_KNOWN_HOSTS" > "$known_hosts"
|
||
printf '%s\n' "get -p $REMOTE_DIR/config.json $config" > "$batch"
|
||
SSHPASS="$SFTP_PASS" sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=yes -oUserKnownHostsFile="$known_hosts" "$SFTP_USER@$SFTP_HOST" < "$batch"
|
||
changed=0
|
||
php bin/ensure-focus-config.php "$config" || changed=$?
|
||
case "$changed" in
|
||
0) ;;
|
||
10) printf '%s\n' "put -p $config $REMOTE_DIR/config.json" > "$batch"
|
||
SSHPASS="$SFTP_PASS" sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=yes -oUserKnownHostsFile="$known_hosts" "$SFTP_USER@$SFTP_HOST" < "$batch" ;;
|
||
*) exit "$changed" ;;
|
||
esac
|
||
|
||
- name: Upload over SFTP
|
||
env:
|
||
SFTP_HOST: ${{ secrets.ZOOMPIXEL_SFTP_HOST }}
|
||
SFTP_USER: ${{ secrets.ZOOMPIXEL_SFTP_USER }}
|
||
SFTP_PASS: ${{ secrets.ZOOMPIXEL_SFTP_PASS }}
|
||
SFTP_KNOWN_HOSTS: ${{ secrets.ZOOMPIXEL_SFTP_KNOWN_HOSTS }}
|
||
run: |
|
||
set -eu
|
||
[ -n "$SFTP_HOST" ] && [ -n "$SFTP_USER" ] && [ -n "$SFTP_PASS" ] && [ -n "$SFTP_KNOWN_HOSTS" ] || { echo "не заданы секреты ZOOMPIXEL_SFTP_*"; exit 1; }
|
||
umask 077
|
||
known_hosts=$(mktemp)
|
||
batch=$(mktemp)
|
||
files=$(mktemp)
|
||
trap 'rm -f "$known_hosts" "$batch" "$files"' EXIT
|
||
printf '%s\n' "$SFTP_KNOWN_HOSTS" > "$known_hosts"
|
||
git ls-files | grep -vE '^(\.gitea/|README\.md$|AGENTS\.md$|CLAUDE\.md$|\.gitignore$|requisites\.html$|config\.json$)' > "$files"
|
||
if grep -q '[^A-Za-z0-9._/-]' "$files"; then echo "имена с недопустимыми символами:"; grep '[^A-Za-z0-9._/-]' "$files"; exit 1; fi
|
||
printf '%s\n' "-mkdir $REMOTE_DIR" > "$batch"
|
||
sed 's#/[^/]*$##' "$files" | grep -v '^$' | sort -u | while read -r dir; do printf '%s\n' "-mkdir $REMOTE_DIR/$dir" >> "$batch"; done
|
||
while read -r file; do printf '%s\n' "put -p $file $REMOTE_DIR/$file" >> "$batch"; done < "$files"
|
||
SSHPASS="$SFTP_PASS" sshpass -e sftp -oBatchMode=no -oStrictHostKeyChecking=yes -oUserKnownHostsFile="$known_hosts" "$SFTP_USER@$SFTP_HOST" < "$batch"
|
||
echo "залито файлов: $(wc -l < "$files")"
|
||
|
||
- name: Check the page
|
||
run: |
|
||
set -eu
|
||
page=$(curl -fsS --max-time 30 "$PRELAND_URL/?nc=$(date +%s)")
|
||
api=$(printf '%s' "$page" | grep -o 'const createApiUrl = "[^"]*"' || true)
|
||
echo "$api"
|
||
case "$api" in *'zoomzilla.ru\/api\/images\/public\/create'*) ;; *) echo "на странице нет адреса загрузки Zoomzilla"; exit 1 ;; esac
|
||
printf '%s' "$page" | grep -q 'claim?key=' || { echo "переход не на claim"; exit 1; }
|