implemented issueBranchName
nclazz/gitea-bot/pipeline/head This commit looks good
Details
nclazz/gitea-bot/pipeline/head This commit looks good
Details
parent
79e604f832
commit
f133114bcf
|
@ -4,6 +4,7 @@ pipeline {
|
|||
environment {
|
||||
NEXUS = credentials('jenkins_nexus')
|
||||
NCLAZZ = credentials('nclazz_api_token')
|
||||
GITEA_TOKEN = credentials('jenkins_gitea_token')
|
||||
|
||||
DOCKER_REGISTRY = "docker.nclazz.de"
|
||||
DOCKER_GROUP = 'nclazz-bots'
|
||||
|
@ -46,7 +47,9 @@ pipeline {
|
|||
env: [
|
||||
PORT: 7007,
|
||||
VERSION: env.DOCKER_VERSION,
|
||||
AUTH_TOKEN: env.NCLAZZ
|
||||
AUTH_TOKEN: env.NCLAZZ,
|
||||
GITEA_TOKEN: env.GITEA_TOKEN,
|
||||
GITEA_BASE_URL: 'https://git.l--n.de/api/v1'
|
||||
]
|
||||
)
|
||||
exposeService(
|
||||
|
|
20
README.md
20
README.md
|
@ -1,4 +1,22 @@
|
|||
Gitea Bot
|
||||
============================
|
||||
|
||||
This bot/server manages issues in gitea repositories.
|
||||
This bot/server manages issues in gitea repositories.
|
||||
|
||||
## Webhooks
|
||||
|
||||
The bot provides some useful webhooks. Webhooks can either be enabled opt-in by
|
||||
listing the required webhooks in the query-parameter `hooks` as a comma separated list
|
||||
or enable all hooks by leaving the hooks parameter empty.
|
||||
|
||||
Send gitea events to `https://gitea-bot.nclazz.de/webhook`
|
||||
|
||||
### issueBranchName
|
||||
|
||||
Updates the branch name in the issues body. The name is created from the title,
|
||||
issue number and optionally a `bug` label, resulting in the following format:
|
||||
|
||||
> `feature/{issue}-{title}`
|
||||
|
||||
the prefix is either`feature` or `bugfix`. Whitespaces in the title are replaced by
|
||||
`-`.
|
|
@ -9,6 +9,7 @@
|
|||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"body-parser": "^1.20.0",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.1"
|
||||
|
@ -33,6 +34,22 @@
|
|||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/axios/-/axios-0.27.2.tgz",
|
||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/body-parser/-/body-parser-1.20.0.tgz",
|
||||
|
@ -79,6 +96,18 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -124,6 +153,15 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/depd/-/depd-2.0.0.tgz",
|
||||
|
@ -242,6 +280,40 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/forwarded/-/forwarded-0.2.0.tgz",
|
||||
|
@ -672,6 +744,20 @@
|
|||
"resolved": "https://nexus.nclazz.de/repository/npm_public/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/axios/-/axios-0.27.2.tgz",
|
||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"body-parser": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/body-parser/-/body-parser-1.20.0.tgz",
|
||||
|
@ -705,6 +791,14 @@
|
|||
"get-intrinsic": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
|
@ -736,6 +830,11 @@
|
|||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
|
||||
},
|
||||
"depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/depd/-/depd-2.0.0.tgz",
|
||||
|
@ -823,6 +922,21 @@
|
|||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://nexus.nclazz.de/repository/npm_public/forwarded/-/forwarded-0.2.0.tgz",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"author": "Niclas Thobaben",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"body-parser": "^1.20.0",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.1"
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
const axios = require('axios')
|
||||
const api = {}
|
||||
|
||||
const BASE_URL = process.env.GITEA_BASE_URL || 'https://git.l--n.de/api/v1'
|
||||
const TOKEN = process.env.GITEA_TOKEN
|
||||
|
||||
console.log(`Use gitea api @ ${BASE_URL}`)
|
||||
|
||||
api.post = (path, payload) => {
|
||||
const url = `${BASE_URL}${path}`
|
||||
|
||||
return axios.post(
|
||||
url,
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `token ${TOKEN}`
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
api.patch = (path, payload) => {
|
||||
const url = `${BASE_URL}${path}`
|
||||
|
||||
return axios.patch(
|
||||
url,
|
||||
payload,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `token ${TOKEN}`
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = api
|
|
@ -0,0 +1,17 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const hooks = {}
|
||||
fs.readdirSync(__dirname).forEach(file => {
|
||||
if(file === 'index.js') {
|
||||
return
|
||||
}
|
||||
const name = file.replace(/\.[^/.]+$/, '')
|
||||
const hook = require(path.join(__dirname, file))
|
||||
hook.name = name
|
||||
hooks[name] = hook
|
||||
})
|
||||
|
||||
console.log(`Loaded available Hooks [${Object.keys(hooks)}]`)
|
||||
|
||||
module.exports = hooks
|
|
@ -0,0 +1,37 @@
|
|||
const gitea = require('../gitea-api')
|
||||
|
||||
const log = (msg) => {
|
||||
console.log('[issueBranchName]', msg)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
exec: (req) => {
|
||||
if(req.headers['x-gitea-event'] !== 'issues') {
|
||||
return
|
||||
}
|
||||
const { issue, repository } = req.body
|
||||
const isBug = issue.labels.length && !!issue.labels.find(label => label.name === 'bug')
|
||||
const prefix = isBug ? 'bugfix' : 'feature'
|
||||
const branch = issue.title.replace(/[^a-z0-9\s]/gi, '').replaceAll(' ', '-')
|
||||
const branchName = `${prefix}/${issue.number}-${branch}`
|
||||
|
||||
log(`Created branch name ${branchName} in ${repository.full_name}`)
|
||||
|
||||
issue.body = issue.body || ''
|
||||
|
||||
let body
|
||||
if(issue.body.includes('Branchname')) {
|
||||
body = issue.body.replaceAll(/\*\*Branchname\*\*: <code>.+<\/code>/g, `**Branchname**: <code>${branchName}</code>`)
|
||||
}else {
|
||||
body = `${issue.body}<br>**Branchname**: <code>${branchName}</code>`
|
||||
}
|
||||
|
||||
body = body.replaceAll('\n', '\\n')
|
||||
|
||||
const path = `/repos/${repository.full_name}/issues/${issue.number}`
|
||||
return gitea.patch(path, { body })
|
||||
.then(() => {})
|
||||
.catch(reason => reason)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
const log = (msg) => {
|
||||
console.log('[issueBranchRef]', msg)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
exec: (req) => {
|
||||
if(req.headers['x-gitea-event'] !== 'issues') {
|
||||
return
|
||||
}
|
||||
log('issue branch ref')
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
const express = require('express')
|
||||
const bodyParser = require('body-parser')
|
||||
const hooks = require('./hooks')
|
||||
|
||||
console.log('Initialize express server')
|
||||
|
||||
|
@ -9,5 +10,36 @@ app.use(bodyParser.json())
|
|||
|
||||
const port = process.env.SERVER_PORT || 8080
|
||||
|
||||
app.post('/webhook', async (req, res) => {
|
||||
const selectedHooks = []
|
||||
if(req.query['hooks']) {
|
||||
req.query.hooks.split(',').forEach(hookName => {
|
||||
const hook = hooks[hookName.trim()]
|
||||
if(!hook) {
|
||||
console.warn(`Hook ${hookName} does not exist! from=${req.ip}`)
|
||||
return
|
||||
}
|
||||
selectedHooks.push(hook)
|
||||
})
|
||||
}else {
|
||||
selectedHooks.push(...Object.values(hooks))
|
||||
}
|
||||
|
||||
const errors = {}
|
||||
const promises = selectedHooks.map(async hook => {
|
||||
console.log(`Execute hook ${hook.name} from=${req.ip}`)
|
||||
const error = await hook.exec(req)
|
||||
if(error) {
|
||||
errors[hook.name] = error
|
||||
}
|
||||
})
|
||||
await Promise.all(promises)
|
||||
if(Object.keys(errors).length) {
|
||||
res.status(400).json(errors)
|
||||
return
|
||||
}
|
||||
res.status(200).send()
|
||||
})
|
||||
|
||||
app.listen(port)
|
||||
console.log(`Started express server on port ${port}`)
|
||||
|
|
Loading…
Reference in New Issue