162 lines
5.3 KiB
Groovy
162 lines
5.3 KiB
Groovy
pipeline {
|
|
agent any
|
|
|
|
parameters {
|
|
string(
|
|
name: 'REPO_URL',
|
|
defaultValue: 'https://git.crowmate.fr/crowmate/Nest.git',
|
|
description: 'Gitea repository URL (to retrieve docker-compose.prod.yml)'
|
|
)
|
|
string(
|
|
name: 'BRANCH',
|
|
defaultValue: 'main',
|
|
description: 'Branch'
|
|
)
|
|
string(
|
|
name: 'STACK_NAME',
|
|
defaultValue: 'nest',
|
|
description: 'Stack name in Portainer'
|
|
)
|
|
string(
|
|
name: 'IMAGE_TAG',
|
|
defaultValue: 'latest',
|
|
description: 'Image tag to deploy'
|
|
)
|
|
}
|
|
|
|
environment {
|
|
GITEA_CREDENTIALS_ID = 'gitea-token'
|
|
PORTAINER_URL = 'https://docker.devgoblin.fr'
|
|
PORTAINER_ENV_ID = '3'
|
|
}
|
|
|
|
stages {
|
|
|
|
stage('Clone') {
|
|
steps {
|
|
cleanWs()
|
|
checkout([
|
|
$class: 'GitSCM',
|
|
branches: [[name: "*/${params.BRANCH}"]],
|
|
userRemoteConfigs: [[
|
|
url: params.REPO_URL,
|
|
credentialsId: env.GITEA_CREDENTIALS_ID
|
|
]]
|
|
])
|
|
echo "Repository cloned"
|
|
}
|
|
}
|
|
|
|
stage('Validation') {
|
|
steps {
|
|
script {
|
|
if (!fileExists('docker-compose.prod.yml')) {
|
|
error("docker-compose.prod.yml not found at the repository root")
|
|
}
|
|
echo "docker-compose.prod.yml found"
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Prepare compose payload') {
|
|
steps {
|
|
sh '''
|
|
base64 -w 0 docker-compose.prod.yml > compose-content.b64
|
|
echo "Compose payload prepared"
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('Fetch Portainer stacks') {
|
|
steps {
|
|
withCredentials([string(credentialsId: 'portainer-token', variable: 'PORTAINER_TOKEN')]) {
|
|
sh '''
|
|
curl -s -X GET "''' + env.PORTAINER_URL + '''/api/stacks" \
|
|
-H "X-API-Key: $PORTAINER_TOKEN" > portainer-stacks.json
|
|
echo "Portainer stacks fetched"
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Resolve target stack') {
|
|
steps {
|
|
script {
|
|
env.STACK_ID = sh(
|
|
script: "jq -r '.[] | select(.Name == \"${params.STACK_NAME}\") | .Id' portainer-stacks.json | head -n 1",
|
|
returnStdout: true
|
|
).trim()
|
|
|
|
if (env.STACK_ID) {
|
|
echo "Existing stack found (ID: ${env.STACK_ID})"
|
|
} else {
|
|
echo "Stack does not exist and will be created"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Update existing stack') {
|
|
when {
|
|
expression { return env.STACK_ID?.trim() }
|
|
}
|
|
steps {
|
|
withCredentials([string(credentialsId: 'portainer-token', variable: 'PORTAINER_TOKEN')]) {
|
|
sh '''
|
|
COMPOSE_CONTENT=$(cat compose-content.b64)
|
|
|
|
curl -s -X PUT "''' + env.PORTAINER_URL + '''/api/stacks/$STACK_ID?endpointId=''' + env.PORTAINER_ENV_ID + '''" \
|
|
-H "X-API-Key: $PORTAINER_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"stackFileContent\": \"$COMPOSE_CONTENT\", \"prune\": true, \"pullImage\": true}" > portainer-response.json
|
|
|
|
echo "Stack updated"
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Create new stack') {
|
|
when {
|
|
expression { return !env.STACK_ID?.trim() }
|
|
}
|
|
steps {
|
|
withCredentials([string(credentialsId: 'portainer-token', variable: 'PORTAINER_TOKEN')]) {
|
|
sh '''
|
|
COMPOSE_CONTENT=$(cat compose-content.b64)
|
|
|
|
curl -s -X POST "''' + env.PORTAINER_URL + '''/api/stacks/create/standalone/string?endpointId=''' + env.PORTAINER_ENV_ID + '''" \
|
|
-H "X-API-Key: $PORTAINER_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"name\": \"''' + params.STACK_NAME + '''\", \"stackFileContent\": \"$COMPOSE_CONTENT\"}" > portainer-response.json
|
|
|
|
echo "Stack created"
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Validate Portainer response') {
|
|
steps {
|
|
sh '''
|
|
if jq -e '.message' portainer-response.json > /dev/null 2>&1; then
|
|
echo "Portainer error: $(jq -r '.message' portainer-response.json)"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Stack deployed successfully"
|
|
'''
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
post {
|
|
success {
|
|
echo "Stack '${params.STACK_NAME}' deployed to Portainer (tag: ${params.IMAGE_TAG})"
|
|
}
|
|
failure {
|
|
echo "Failed to deploy stack '${params.STACK_NAME}'"
|
|
}
|
|
}
|
|
} |