From 89333dd4d88aa2a5cc67a4e93e70fffee2f10977 Mon Sep 17 00:00:00 2001 From: mrleo1nid <43583385+mrleo1nid@users.noreply.github.com> Date: Tue, 6 Oct 2026 21:59:00 +0300 Subject: [PATCH] =?UTF-8?q?CI:=20=D1=81=D0=B1=D0=BE=D1=80=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B5=D0=BB=D0=B8=D0=B7=D1=8B=20=D1=87=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B7=20Gitea=20Actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ci.yml: gofmt, go vet, тесты, сборка backupctl linux/amd64+arm64 на push и PR - release.yml: на тег v* — сборка, checksums.txt и публикация релиза через API Gitea Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/ci.yml | 58 ++++++++++++++++++ .gitea/workflows/release.yml | 113 +++++++++++++++++++++++++++++++++++ README.md | 6 ++ 3 files changed, 177 insertions(+) create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitea/workflows/release.yml diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..eb16031 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + # Официальный образ Go: версия компилятора совпадает с go.mod и релизной сборкой. + container: golang:1.26 + steps: + # Код забираем сами, а не через actions/checkout: это JS-действие, ему + # нужен Node внутри контейнера, которого в образе golang нет. + - name: Забрать код + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + git config --global --add safe.directory "$PWD" + git init -q . + git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" + git -c http.extraheader="Authorization: token ${TOKEN}" \ + fetch --depth 1 origin "${GITHUB_SHA}" + git checkout -q FETCH_HEAD + + - name: Проверить форматирование + run: | + unformatted="$(gofmt -l cmd internal)" + if [ -n "$unformatted" ]; then + echo "Не отформатировано (запустите gofmt -w):" + echo "$unformatted" + exit 1 + fi + + - name: go vet + run: go vet ./... + + - name: Тесты + run: go test ./... + + - name: Собрать бинарники + run: | + set -eu + VERSION="main-$(git rev-parse --short HEAD)" + for arch in amd64 arm64; do + echo "==> backupctl-linux-${arch}" + CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" \ + go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ + -o "dist/backupctl-linux-${arch}" ./cmd/backupctl + done + ls -la dist + ./dist/backupctl-linux-amd64 version + + - name: Проверить синтаксис скриптов + run: bash -n internal/restore/provision.sh diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..42d9db6 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,113 @@ +name: Release + +on: + push: + tags: + - "v*" + +jobs: + release: + runs-on: ubuntu-latest + container: golang:1.26 + steps: + # История нужна целиком, чтобы собрать список изменений с прошлого тега. + - name: Забрать код + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eu + git config --global --add safe.directory "$PWD" + git init -q . + git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" + git -c http.extraheader="Authorization: token ${TOKEN}" \ + fetch --tags origin + git checkout -q "${GITHUB_SHA}" + + - name: Тесты + run: go test ./... + + - name: Собрать бинарники + run: | + set -eu + VERSION="${GITHUB_REF_NAME}" + mkdir -p dist + for arch in amd64 arm64; do + echo "==> backupctl-linux-${arch}" + CGO_ENABLED=0 GOOS=linux GOARCH="${arch}" \ + go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ + -o "dist/backupctl-linux-${arch}" ./cmd/backupctl + done + cp deploy/backup-master.service deploy/Caddyfile.example dist/ + cd dist + sha256sum * > checksums.txt + cat checksums.txt + + - name: Опубликовать релиз + env: + # Токен репозитория Actions выдаёт сам; RELEASE_TOKEN нужен, только + # если в настройках инстанса права такого токена урезаны. + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + DEFAULT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # GITHUB_SERVER_URL внутри Actions может быть внутренним адресом — + # для ссылок в описании релиза берём внешний (переменная репозитория). + PUBLIC_URL: ${{ vars.PUBLIC_URL }} + run: | + set -eu + apt-get update -qq && apt-get install -y -qq jq + + TOKEN="${RELEASE_TOKEN:-$DEFAULT_TOKEN}" + if [ -z "$TOKEN" ]; then + echo "Нет токена для публикации: заведите секрет RELEASE_TOKEN" >&2 + exit 1 + fi + + API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" + TAG="${GITHUB_REF_NAME}" + AUTH="Authorization: token ${TOKEN}" + + PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)" + if [ -n "$PREV" ]; then + CHANGES="$(git log --no-merges --pretty=format:'- %s' "${PREV}..${TAG}")" + else + CHANGES="$(git log --no-merges --pretty=format:'- %s')" + fi + + PUBLIC="${PUBLIC_URL:-https://gitea.hsrv.site}" + DL="${PUBLIC%/}/${GITHUB_REPOSITORY}/releases/download/${TAG}" + NOTES="$(printf '## Обновление сервера\n\n```bash\nsudo curl -fsSL %s/backupctl-linux-amd64 -o /usr/local/bin/backupctl\nsudo chmod 755 /usr/local/bin/backupctl\nsudo install -d /opt/backup-master/dist\nfor a in amd64 arm64; do sudo curl -fsSL %s/backupctl-linux-$a -o /opt/backup-master/dist/backupctl-linux-$a; done\nsudo systemctl restart backup-master\n```\n\nАгенты обновляются той же командой `agent.sh` без `--token`: конфиг сохраняется.\n\n## Изменения\n\n%s\n' \ + "${DL}" "${DL}" "${CHANGES}")" + + PAYLOAD="$(jq -n --arg tag "$TAG" --arg body "$NOTES" \ + '{tag_name: $tag, name: $tag, body: $body, draft: false, prerelease: false}')" + + # Тег могли перевыпустить — тогда работаем с существующим релизом. + ID="$(curl -sS -H "$AUTH" "${API}/releases/tags/${TAG}" | jq -r '.id // empty')" + if [ -n "$ID" ]; then + echo "==> Обновляю существующий релиз ${TAG} (id ${ID})" + curl -sS -X PATCH -H "$AUTH" -H "Content-Type: application/json" \ + -d "$PAYLOAD" "${API}/releases/${ID}" > /dev/null + curl -sS -H "$AUTH" "${API}/releases/${ID}/assets" | jq -r '.[].id' \ + | while read -r asset; do + [ -n "$asset" ] || continue + curl -sS -X DELETE -H "$AUTH" "${API}/releases/${ID}/assets/${asset}" > /dev/null + done + else + echo "==> Создаю релиз ${TAG}" + ID="$(curl -sS -X POST -H "$AUTH" -H "Content-Type: application/json" \ + -d "$PAYLOAD" "${API}/releases" | jq -r '.id')" + fi + + if [ -z "$ID" ] || [ "$ID" = "null" ]; then + echo "Не удалось получить id релиза — проверьте права токена" >&2 + exit 1 + fi + + for file in dist/*; do + name="$(basename "$file")" + echo "==> Загружаю ${name}" + curl -sS -f -X POST -H "$AUTH" \ + -F "attachment=@${file}" \ + "${API}/releases/${ID}/assets?name=${name}" > /dev/null + done + + echo "Готово: ${PUBLIC%/}/${GITHUB_REPOSITORY}/releases/tag/${TAG}" diff --git a/README.md b/README.md index 8ef7e0a..60c6533 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,12 @@ make build # dist/backupctl-linux-amd64, dist/backupctl-linux-arm64 make test ``` +Готовые бинарники собирает Gitea Actions. `.gitea/workflows/ci.yml` на каждый push и PR проверяет форматирование, запускает vet и тесты и собирает бинарники. `release.yml` на тег `v*` публикует релиз с `backupctl-linux-{amd64,arm64}`, `checksums.txt` и файлами из `deploy/`: + +```bash +git tag v0.1.0 && git push origin v0.1.0 +``` + Без make: ```bash