foo/Jenkinsfile
2024-09-11 16:30:33 +03:00

437 lines
13 KiB
Groovy
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

defaults = [
action_type: ['print_branches'],
action_help: '',
version: '0.0.0',
protect_branch: true
]
if (env.BRANCH_NAME == 'master') {
defaults.action_type.add('start_hotfix')
defaults.branch_type = 'hotfix'
}
if (env.BRANCH_NAME == 'develop') {
defaults.action_type.add('start_release')
defaults.branch_type = 'release'
}
if (env.BRANCH_NAME ==~ /^(hotfix)\/.+/) {
defaults.action_type.addAll(['merge_hotfix', 'finish_hotfix', 'rename_hotfix', 'delete_hotfix', 'unprotect_hotfix'])
defaults.branch_type = 'hotfix'
}
if (env.BRANCH_NAME ==~ /^(release)\/.+/) {
defaults.action_type.addAll(['merge_release', 'finish_release', 'rename_release', 'delete_release', 'unprotect_release'])
defaults.branch_type = 'release'
}
pipeline {
agent {
label 'branch_manager'
}
environment {
GIT_SERVER = 'git.onlyoffice.com'
GITEA_TOKEN = credentials('gitea-token')
GITHUB_TOKEN = credentials('github-token')
TELEGRAM_TOKEN = credentials('telegram-bot-token')
}
options {
disableConcurrentBuilds()
}
parameters {
booleanParam (
name: 'wipe',
description: 'Wipe out current workspace',
defaultValue: false
)
choice (
name: 'action_type',
description: "Action type",
choices: defaults.action_type
)
string (
name: 'version',
description: 'Release version (for start only)',
defaultValue: defaults.version
)
booleanParam (
name: 'protect_branch',
description: 'Protect branch (for start only)',
defaultValue: defaults.protect_branch
)
string (
name: 'extra_branch',
description: 'Extra branch (for finish only)',
defaultValue: ''
)
booleanParam (
name: 'notify',
description: 'Telegram notification',
defaultValue: true
)
}
stages {
stage('Branch Manager') {
steps {
script {
currentBuild.displayName += ' - ' + params.action_type
if (params.action_type in ['start_hotfix', 'start_release'])
currentBuild.displayName += ' ' + params.version
if (params.wipe) {
deleteDir()
checkout scm
}
stats = [repos: [:]]
String branch = env.BRANCH_NAME
ArrayList baseBranches = []
getRepos().each {
stats.repos.put(it, [:])
}
Boolean pAction
Boolean sAction
if (params.action_type == 'print_branches') {
stats.repos.each { repo, status ->
pAction = printBranches(repo)
status.primary = (pAction) ? 'success' : 'failure'
}
} else if params.action_type.startsWith('start') {
branch = defaults.branch_type + '/v' + params.version
baseBranches = [env.BRANCH_NAME]
stats.repos.each { repo, status ->
if (checkRemoteBranch(repo, branch)) {
echo "${repo}: Branch already ${branch} exists."
status.primary = 'skip'
} else {
dir ('repos/' + repo) {
checkoutRepo(repo)
if (!checkRemoteBranch(repo, 'develop')
&& !createBranch(repo, 'develop', 'master'))
error("Can't create develop branch.")
pAction = createBranch(repo, branch, baseBranches[0])
status.primary = (pAction) ? 'success' : 'failure'
}
}
if (params.protect_branch) {
sAction = protectBranch(repo, branch)
status.secondary = (sAction) ? 'lock' : ''
}
}
} else if params.action_type.startsWith('merge') {
baseBranches = ['master']
stats.repos.each { repo, status ->
if (!checkRemoteBranch(repo, branch)) {
echo "${repo}: Branch doesn't ${branch} exist."
status.primary = 'skip'
} else {
dir ('repos/' + repo) {
checkoutRepo(repo)
pAction = mergeBranch(repo, branch, baseBranches)
status.primary = (pAction) ? 'success' : 'failure'
}
}
}
} else if params.action_type.startsWith('finish') {
baseBranches = ['master', 'develop']
if (!params.extra_branch.isEmpty())
baseBranches.add(params.extra_branch)
stats.repos.each { repo, status ->
if (!checkRemoteBranch(repo, branch)) {
echo "${repo}: Branch doesn't ${branch} exist."
status.primary = 'skip'
} else {
dir ('repos/' + repo) {
checkoutRepo(repo)
unprotectBranch(repo, branch)
pAction = mergeBranch(repo, branch, baseBranches)
status.primary = (pAction) ? 'success' : 'failure'
if (pAction && !repo.contains('documents-pipeline')) {
sAction = deleteBranch(repo, branch)
status.secondary = (sAction) ? 'delete' : ''
}
}
}
}
} else if params.action_type.startsWith('rename') {
branch = defaults.branch_type + '/v' + params.version
baseBranches = [env.BRANCH_NAME]
stats.repos.each { repo, status ->
if (checkRemoteBranch(repo, branch)) {
echo "${repo}: Branch already ${branch} exists."
status.primary = 'skip'
} else {
dir ('repos/' + repo) {
checkoutRepo(repo, env.BRANCH_NAME)
pAction = createBranch(repo, branch, env.BRANCH_NAME)
status.primary = (pAction) ? 'success' : 'failure'
if (pAction) {
unprotectBranch(repo, env.BRANCH_NAME)
deleteBranch(repo, env.BRANCH_NAME)
if (params.protect_branch) {
sAction = protectBranch(repo, branch)
status.secondary = (sAction) ? 'lock' : ''
}
}
}
}
}
} else if params.action_type.startsWith('delete') {
stats.repos.each { repo, status ->
if (!checkRemoteBranch(repo, branch)) {
echo "${repo}: Branch doesn't ${branch} exist."
status.primary = 'skip'
} else {
dir ('repos/' + repo) {
checkoutRepo(repo, branch)
unprotectBranch(repo, branch)
if (!repo.contains('documents-pipeline')) {
pAction = deleteBranch(repo, branch)
status.primary = (pAction) ? 'success' : 'failure'
}
}
}
}
} else if params.action_type.startsWith('protect') {
stats.repos.each { repo, status ->
pAction = protectBranch(repo, branch)
status.primary = (pAction) ? 'success' : 'failure'
status.secondary = 'none'
}
} else if params.action_type.startsWith('unprotect') {
stats.repos.each { repo, status ->
pAction = unprotectBranch(repo, branch)
status.primary = (pAction) ? 'success' : 'failure'
status.secondary = 'none'
}
}
stats.putAll([
branch: branch,
baseBranches: baseBranches,
success: stats.repos.findAll { repo, status ->
status.primary in ['skip', 'success']
}.size(),
total: stats.repos.size()
])
println stats
}
}
}
}
post {
success {
script {
if (stats.success == 0)
currentBuild.result = 'FAILURE'
else if (stats.success != stats.total)
currentBuild.result = 'UNSTABLE'
else if (stats.success == stats.total)
currentBuild.result = 'SUCCESS'
sendNotification()
}
}
}
}
def getRepos() {
return ['heatray/foo']
}
def checkoutRepo(String repo, String branch = 'master') {
sh (
label: "${repo}: checkout",
script: """
if [ "\$(GIT_DIR=.git git rev-parse --is-inside-work-tree)" = 'true' ]; then
git fetch --all --prune
git switch -f ${branch}
git reset --hard origin/${branch}
git clean -df
else
rm -rfv ./*
git clone -b ${branch} git@\$GIT_SERVER:${repo}.git .
fi
git branch -vv
"""
)
}
def checkRemoteBranch(String repo, String branch = 'master') {
return sh (
label: "${repo}: check branch ${branch}",
script: "git ls-remote --exit-code git@\$GIT_SERVER:${repo}.git ${branch}",
returnStatus: true
) == 0
}
def createBranch(String repo, String branch, String baseBranch) {
return sh (
label: "${repo}: start ${branch}",
script: """
git switch ${baseBranch}
git reset --hard origin/${baseBranch}
git checkout -B ${branch}
git push origin ${branch}
git branch -vv
echo "Branch created."
""",
returnStatus: true
) == 0
}
def mergeBranch(String repo, String branch, ArrayList baseBranches) {
return sh (
label: "${repo}: merge ${branch} into ${baseBranches.join(' ')}",
script: """#!/bin/bash -xe
git switch ${branch}
git reset --hard origin/${branch}
base_branches=(${baseBranches.join(' ')})
merged=0
rev_branch=\$(git rev-parse @)
for base in "\${base_branches[@]}"; do
git switch \$base || (((++merged)) && continue)
git reset --hard origin/\$base
rev_base=\$(git rev-parse @)
if [[ \$rev_branch == \$rev_base ]]; then
((++merged))
echo "No new commits."
continue
fi
# gh pr create --repo ${repo} --base \$base --head ${branch} \
# --title "Merge branch ${branch} into \$base" --fill || \
# true
if ! git merge ${branch} --no-edit --no-ff \
-m "Merge branch ${branch} into \$base"; then
git merge --abort
continue
fi
git push origin \$base
((++merged))
done
git branch -vv
if [[ \$merged -ne \${#base_branches[@]} ]]; then
echo "Not fully merged."
exit 2
fi
echo "Branch merged."
""",
returnStatus: true
) == 0
}
def deleteBranch(String repo, String branch) {
return sh (
label: "${repo}: delete ${branch}",
script: """
git switch -f master
git branch -D ${branch}
git push --delete origin ${branch}
echo "Branch deleted."
""",
returnStatus: true
) == 0
}
def printBranches(String repo) {
return sh (
label: "${repo}: branches list",
script: """
curl -X 'GET' \
'https://\$GIT_SERVER/api/v1/repos/${repo}/branches' \
-H 'accept: application/json' \
-H 'Authorization: \$GITEA_TOKEN' | \
jq -r '.[] | [.name, .protected] | @tsv'
""",
returnStatus: true
) == 0
}
def protectBranch(String repo, String branch) {
return sh (
label: "${repo}: protect ${branch}",
script: """
echo '{
"branch_name": "master"
}' | \
curl -X 'POST' \
'https://\$GIT_SERVER/api/v1/repos/${repo}/branch_protections?token=\$GITEA_TOKEN' \
-H 'accept: application/json' \
-H 'Authorization: \$GITEA_TOKEN' \
-H 'Content-Type: application/json' \
-d -
""",
returnStatus: true
) == 0
}
def unprotectBranch(String repo, String branch) {
return sh (
label: "${repo}: unprotect ${branch}",
script: """
curl -X 'DELETE' \
'https://\$GIT_SERVER/api/v1/repos/${repo}/branch_protections/${branch}?token=\$GITEA_TOKEN' \
-H 'accept: application/json' \
-H 'Authorization: \$GITEA_TOKEN'
"""
returnStatus: true
) == 0
}
def sendNotification() {
String text = ''
switch(params.action_type) {
case ['start_hotfix', 'start_release']:
text = "Branch `${stats.branch}` created from `${stats.baseBranches[0]}`"
break
case ['merge_hotfix', 'merge_release']:
text = "Branch `${stats.branch}` merged into `${stats.baseBranches[0]}`"
break
case ['finish_hotfix', 'finish_release']:
text = "Branch `${stats.branch}` merged into "
text += stats.baseBranches.collect({"`$it`"}).join(', ')
break
default: text = 'Stats'
}
text += " \\[${stats.success}/${stats.total}]"
stats.repos.each { repo, status ->
text += '\n'
switch(status.primary) {
case 'skip': text += '🔘'; break
case 'success': text += '☑️'; break
case 'failure': text += '🚫'; break
default: text += ''
}
switch(status.secondary) {
case 'lock': text += '🔒'; break
case 'delete': text += '♻️'; break
case 'none': text += ''; break
default: text += ''
}
text += " [${repo}](https://${env.GIT_SERVER}/${repo})"
}
echo text
}