1
0
mirror of https://github.com/ONLYOFFICE/DocSpace.git synced 2025-04-18 20:24:12 +03:00

Create actions to automate release

This commit is contained in:
Evgeniy Antonyuk 2025-03-07 17:28:55 +03:00 committed by Alexey Golubev
parent b829e15009
commit 982acd77f6
2 changed files with 361 additions and 0 deletions

64
.github/workflows/create_branch.yml vendored Normal file
View File

@ -0,0 +1,64 @@
name: Create Branch
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch name to create'
required: true
default: 'release/vX.X.X'
source_branch:
description: 'Source branch'
required: false
default: 'develop'
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
create_branch:
name: Create branch in ${{ matrix.repo }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
repo: [ 'DocSpace', 'DocSpace-buildtools', 'DocSpace-client', 'DocSpace-server' ]
steps:
- name: Create branch in ${{ matrix.repo }}
run: |
SOURCE_HASH=$(gh api /repos/${{ github.repository_owner }}/${{ matrix.repo }}/git/ref/heads/${{ github.event.inputs.source_branch }} --jq '.object.sha' 2>/dev/null || true)
[ -z "$SOURCE_HASH" ] && { echo "::error:: Source branch '${{ github.event.inputs.source_branch }}' not found"; exit 1; }
if ! gh api /repos/${{ github.repository_owner }}/${{ matrix.repo }}/git/ref/heads/${{ github.event.inputs.branch }} >/dev/null 2>&1; then
if gh api --method POST /repos/${{ github.repository_owner }}/${{ matrix.repo }}/git/refs -f ref="refs/heads/${{ github.event.inputs.branch }}" -f sha="${SOURCE_HASH}"; then
echo "Branch '${{ github.event.inputs.branch }}' created successfully."
else
echo "::error:: Error occurred while creating branch '${{ github.event.inputs.branch }}'." && exit 1
fi
else
echo "::warning:: Target branch '${{ github.event.inputs.branch }}' already exists, skipping creation."
fi
notify:
name: Notify Create branch
runs-on: ubuntu-latest
needs: [create_branch ]
if: ${{ always() }}
steps:
- name: Notify Create branch
run: |
JOB_OUTPUT=$(gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs")
TOTAL=$(jq '[.jobs[] | select(.name | test("^Create branch in "))] | length' <<< "${JOB_OUTPUT}")
SUCCESS=$(jq '[.jobs[] | select((.name | test("^Create branch in ")) and (.conclusion=="success"))] | length' <<< "${JOB_OUTPUT}")
MESSAGE="\[${SUCCESS}/${TOTAL}] Created ${{ github.event.inputs.branch }} (base: ${{ github.event.inputs.source_branch }})"$'\n'
MESSAGE+=$(jq -r --arg OWNER "${{ github.repository_owner }}" --arg GIT_URL "${{ github.server_url }}" '
.jobs[]
| select(.name | test("^Create branch in "))
| (.name | sub("^Create branch in ";"")) as $REPO
| (.conclusion | if .=="success" then "🟢" else "🔴" end)
+ " [" + $REPO + "](" + $GIT_URL + "/" + $OWNER + "/" + $REPO + ")"
' <<< "${JOB_OUTPUT}")
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage" \
-d chat_id="${{ secrets.TELEGRAM_TEAM_CHAT_ID }}" -d text="${MESSAGE}" -d parse_mode="Markdown" -d disable_web_page_preview=true

297
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,297 @@
name: Release Action
on:
workflow_dispatch:
inputs:
version:
description: 'Release version'
required: true
default: 'vX.X.X'
branch:
description: 'Release branch'
required: true
default: 'release/vX.X.X'
target_branch:
description: 'Target branch'
required: true
default: 'master'
trigger_release:
type: boolean
description: 'GitHub Release'
required: true
default: true
trigger_docker_release:
type: boolean
description: 'Docker images release'
required: true
default: true
docker_version:
description: 'Source Docker version'
required: true
default: 'X.X.X.XXXX'
trigger_packages_release:
type: boolean
description: 'Packages release'
required: true
default: true
deb_build_number:
description: 'deb package build number'
required: true
default: 'XXXX'
rpm_build_number:
description: 'rpm package build number'
required: true
default: 'XXX'
exe_build_number:
description: 'exe package build number'
required: true
default: 'XXXX'
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
jobs:
trigger_docker_release:
if: ${{ github.event.inputs.trigger_docker_release == 'true' }}
name: Trigger Docker release
runs-on: ubuntu-latest
outputs:
docker_run_id: ${{ steps.docker_release.outputs.docker_run_id }}
steps:
- name: Trigger Docker release
id: docker_release
run: |
DOCKER_VERSION="${{ github.event.inputs.version }}"
DOCKER_VERSION="${DOCKER_VERSION#v}.1"
WORKFLOW_ID=$(gh api repos/${{ github.repository }}-buildtools/actions/workflows \
--jq '.workflows[] | select(.path == ".github/workflows/release-docspace.yaml") | .id')
gh api /repos/${{ github.repository }}-buildtools/actions/workflows/${WORKFLOW_ID}/dispatches -X POST \
-f "ref=${{ github.ref_name }}" -f "inputs[release_version]=${DOCKER_VERSION}" -f "inputs[source_version]=${{ github.event.inputs.docker_version }}"
until [ -n "${DOCKER_RUN_ID}" ]; do
DOCKER_RUN_ID=$(gh api repos/${{ github.repository }}-buildtools/actions/runs \
--jq '.workflow_runs[] | select(.workflow_id=='"${WORKFLOW_ID}"' and (.status=="in_progress" or .status=="queued")) | .id' | head -n 1)
sleep 5
done
echo "docker_run_id=${DOCKER_RUN_ID}" >> $GITHUB_OUTPUT
trigger_packages_release:
if: ${{ github.event.inputs.trigger_packages_release == 'true' }}
name: Trigger Packages release
runs-on: ubuntu-latest
outputs:
deb_build_number: ${{ steps.package_release.outputs.deb_build_number }}
rpm_build_number: ${{ steps.package_release.outputs.rpm_build_number }}
exe_build_number: ${{ steps.package_release.outputs.exe_build_number }}
steps:
- name: Trigger Packages release
id: package_release
run: |
JENKINS_URL="${{ secrets.JENKINS_URL }}"; TOKEN="${{ secrets.JENKINS_TOKEN }}"; VERSION="${{ github.event.inputs.version }}"
declare -A NUM=([deb]="${{ github.event.inputs.deb_build_number }}" [rpm]="${{ github.event.inputs.rpm_build_number }} [exe]="${{ github.event.inputs.exe_build_number }}")
for TYPE in deb rpm exe; do
BUILD_NUM=${NUM[${TYPE}]}
curl -s -S -X POST -H "Content-Length: 0" -u "${TOKEN}" "${JENKINS_URL}/job/appserver.${TYPE}/${BUILD_NUM}/toggleLogKeep"
curl -s -S -X POST -u "${TOKEN}" -d "description=${VERSION}.${BUILD_NUM}" "${JENKINS_URL}/job/appserver.${TYPE}/${BUILD_NUM}/submitDescription"
curl -s -S -X POST -H "Content-Length: 0" -u "${TOKEN}" "${JENKINS_URL}/job/production.${TYPE}.publish/buildWithParameters"
sleep 10
echo "${TYPE}_build_number=$(curl -s -S -L -u "${TOKEN}" "${JENKINS_URL}/job/production.${TYPE}.publish/lastBuild/api/json" | jq '.number')" >> "$GITHUB_OUTPUT"
done
create_and_merge_pr:
name: Create/merge PRs ${{ matrix.repo }}
runs-on: ubuntu-latest
strategy:
matrix:
repo: [ "DocSpace", "DocSpace-buildtools", "DocSpace-client", "DocSpace-server" ]
steps:
- name: Check branch existence
run: |
if ! gh api repos/${{ github.repository_owner }}/${{ matrix.repo }}/git/ref/heads/${{ github.event.inputs.branch }} --silent; then
echo "::error::Branch '${{ github.event.inputs.branch }}' does not exist in repository ${{ matrix.repo }}" && exit 1
fi
- name: Check commits difference
run: |
AHEAD_BY=$(gh api repos/${{ github.repository_owner }}/${{ matrix.repo }}/compare/${{ github.event.inputs.target_branch }}...${{ github.event.inputs.branch }} --jq '.ahead_by')
if [[ "$AHEAD_BY" =~ ^[0-9]+$ ]] && [ "$AHEAD_BY" -eq 0 ]; then
echo "::warning::No commits to merge in ${{ matrix.repo }}, skipping PR creation"
echo "SKIP_PR=true" >> $GITHUB_ENV
fi
- name: Create Pull Request
if: ${{ env.SKIP_PR != 'true' }}
run: |
PR_NUMBER=$(gh pr list --repo ${{ github.repository_owner }}/${{ matrix.repo }} \
--base ${{ github.event.inputs.target_branch }} \
--head ${{ github.event.inputs.branch }} \
--state open \
--json number --jq '.[0].number')
if [[ -n "$PR_NUMBER" ]]; then
echo "Pull request #$PR_NUMBER already exists in ${{ matrix.repo }}"
else
gh pr create --repo ${{ github.repository_owner }}/${{ matrix.repo }} \
-t "Merge ${{ github.event.inputs.branch }} into ${{ github.event.inputs.target_branch }}" \
-b "Automatically created by Release Action" \
-B ${{ github.event.inputs.target_branch }} \
-H ${{ github.event.inputs.branch }}
fi
- name: Auto merge Pull Request
if: ${{ env.SKIP_PR != 'true' }}
run: |
PR_NUMBER=$(timeout 60 bash -c 'while :; do
NUM=$(gh pr list --repo ${{ github.repository_owner }}/${{ matrix.repo }} \
--base ${{ github.event.inputs.target_branch }} \
--head "${{ github.event.inputs.branch }}" \
--state open \
--json number --jq ".[0].number");
[[ -n "$NUM" && "$NUM" != "UNKNOWN" ]] && echo $NUM; sleep 2; exit 0;
done;')
if [ -z "${PR_NUMBER}" ]; then
echo "::error::Failed to find open PR in ${{ matrix.repo }}" && exit 1
fi
MERGEABLE_STATUS=$(gh pr view "${PR_NUMBER}" \
--repo ${{ github.repository_owner }}/${{ matrix.repo }} \
--json mergeable --jq '.mergeable')
if [ "${MERGEABLE_STATUS}" = "MERGEABLE" ]; then
gh pr merge "${PR_NUMBER}" --repo ${{ github.repository_owner }}/${{ matrix.repo }} \
-t "Merge ${{ github.event.inputs.branch }} into ${{ github.event.inputs.target_branch }}" --merge
else
echo "::warning::PR #$PR_NUMBER in ${{ matrix.repo }} is not mergeable or contains conflicts."
fi
release:
if: ${{ github.event.inputs.trigger_release == 'true' }}
name: Release ${{ matrix.repo }}
runs-on: ubuntu-latest
needs: create_and_merge_pr
strategy:
fail-fast: false
matrix:
repo: [ "DocSpace", "DocSpace-buildtools", "DocSpace-client", "DocSpace-server" ]
steps:
- name: Wait for all PR merges across repositories
env:
REPOS: "DocSpace DocSpace-client DocSpace-server DocSpace-buildtools"
run: |
echo "Waiting for PR merges in all repositories..."
while :; do
MERGED=0
for REPO in ${REPOS}; do
PR_INFO=$(gh pr list --repo ${{ github.repository_owner }}/${REPO} \
--base ${{ github.event.inputs.target_branch }} --head ${{ github.event.inputs.branch }} \
--state all --json state,url --jq '.[0] // {state: "NOT_FOUND", url: ""}')
PR_STATE=$(echo "${PR_INFO}" | jq -r '.state')
PR_URL=$(echo "${PR_INFO}" | jq -r '.url')
echo "PR state for $REPO: ${PR_STATE} ($PR_URL)"
[ "${PR_STATE}" = "MERGED" ] && MERGED=$((MERGED + 1))
[ "${PR_STATE}" = "NOT_FOUND" ] && REPOS=$(xargs -n1 <<< "$REPOS" | grep -vx "$REPO" | xargs)
done
printf '%.0s-' {1..48}; echo
[ "${MERGED}" -eq "$(echo ${REPOS} | wc -w)" ] && break
sleep 5
done
- name: Checkout repository ${{ matrix.repo }}
uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/${{ matrix.repo }}
token: ${{ secrets.GH_TOKEN }}
submodules: recursive
fetch-depth: 0
- name: Update submodules (DocSpace only)
if: ${{ matrix.repo == 'DocSpace' }}
run: |
git pull --recurse-submodules
git submodule update --remote --merge
if [ -n "$(git status --porcelain)" ]; then
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git commit -am "Update submodules"
git push origin HEAD
else
echo "::warning::No submodule changes detected."
fi
- name: Create Tag
run: |
TAG_NAME=${{ github.event.inputs.version }}$([ "${{ matrix.repo }}" != "DocSpace" ] && echo '-server' || echo '')
git fetch --tags
if git rev-parse "${TAG_NAME}" >/dev/null 2>&1; then
echo "::warning::Tag ${TAG_NAME} already exists. Skipping tag creation."
else
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git tag ${TAG_NAME} -m "${TAG_NAME}"
git push origin ${TAG_NAME}
fi
- name: Create Release (DocSpace only)
if: ${{ matrix.repo == 'DocSpace' }}
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GH_TOKEN }}
tag_name: ${{ github.event.inputs.version }}
name: ${{ github.event.inputs.version }}
body: See changes at [CHANGELOG.md](${{ github.server_url }}/${{ github.repository }}/blob/master/CHANGELOG.md)
draft: false
prerelease: false
notify:
name: Notify Release
runs-on: ubuntu-latest
needs: [release, trigger_docker_release, trigger_packages_release ]
if: ${{ always() }}
steps:
- name: Notify Release
env:
REPOS: "DocSpace DocSpace-client DocSpace-server DocSpace-buildtools"
DOCKER_RUN_ID: ${{ needs.trigger_docker_release.outputs.docker_run_id }}
DEB_BUILD_NUMBER: ${{ needs.trigger_packages_release.outputs.deb_build_number }}
RPM_BUILD_NUMBER: ${{ needs.trigger_packages_release.outputs.rpm_build_number }}
EXE_BUILD_NUMBER: ${{ needs.trigger_packages_release.outputs.exe_build_number }}
run: |
JQ_TARGET=$([ "${{ github.event.inputs.trigger_release }}" = "true" ] && echo "Release" || echo "Create/merge PR")
JQ_FILTER=".jobs[] | select(.name | startswith(\"${JQ_TARGET}\")) | \"\\(.name | sub(\"^${JQ_TARGET}s? ?\"; \"\")) \\(.conclusion)\""
JOB_OUTPUT=$(gh api "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs" --jq "${JQ_FILTER}")
for REPO in ${REPOS}; do
[[ "${JOB_OUTPUT}" =~ "${REPO} success" ]] && SUCCESS=$((SUCCESS+1)) && EMOJI="🟢" || EMOJI="🔴"
STATUS+=$'\n'"${EMOJI} [${REPO}](${{ github.server_url }}/${{ github.repository_owner }}/${REPO})"
done
MESSAGE="\[${SUCCESS:-0}/$(echo $REPOS | wc -w)] ${{ github.event.inputs.branch }} → ${{ github.event.inputs.target_branch }} ${STATUS}"
if ${{ github.event.inputs.trigger_docker_release == 'true' }} || ${{ github.event.inputs.trigger_packages_release == 'true' }}; then
MESSAGE+=$'\n'$'\n''Releases: '
if ${{ github.event.inputs.trigger_docker_release == 'true' }}; then
MESSAGE+='[Docker](${{ github.server_url }}/${{ github.repository }}-buildtools/actions/runs/${{ env.DOCKER_RUN_ID }}) | '
fi
if ${{ github.event.inputs.trigger_packages_release == 'true' }}; then
MESSAGE+='[DEB](${{ secrets.JENKINS_URL }}/job/production.deb.publish/${{ env.DEB_BUILD_NUMBER }}) | '
MESSAGE+='[RPM](${{ secrets.JENKINS_URL }}/job/production.rpm.publish/${{ env.RPM_BUILD_NUMBER }}) | '
MESSAGE+='[EXE](${{ secrets.JENKINS_URL }}/job/production.exe.publish/${{ env.EXE_BUILD_NUMBER }})'
fi
fi
JSON_PAYLOAD=$(jq -n --arg CHAT_ID "${{ secrets.TELEGRAM_TEAM_CHAT_ID }}" --arg TEXT "${MESSAGE}" \
'{chat_id: $CHAT_ID, text: $TEXT, parse_mode: "Markdown", disable_web_page_preview: true}')
curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage" \
-H "Content-Type: application/json" -d "${JSON_PAYLOAD}"
if "${{ github.event.inputs.trigger_release }}" = "true" && ${{ github.event.inputs.trigger_docker_release == 'true' }} && ${{ github.event.inputs.trigger_packages_release == 'true' }}; then
curl -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_TOKEN }}/sendMessage" \
-d chat_id=${{ secrets.TELEGRAM_RELEASE_NOTIFICATION_CHAT_ID }} -d parse_mode=Markdown \
-d "text=ONLYOFFICE DocSpace [${{ github.event.inputs.version }}](${{ github.server_url }}/${{ github.repository }}/blob/master/CHANGELOG.md) for Docker, Linux and Windows has been released"
fi