Jenkinsfile

This commit is contained in:
Ramón Vásquez 2025-10-14 15:37:30 -03:00
parent 30529c4938
commit 0c048c0c88

271
Jenkinsfile vendored
View file

@ -1,15 +1,270 @@
pipeline {
agent any
stages {
stage('Stage Test')
{
steps {
echo 'Un día me preguntaron qué es lo que quería...'
echo 'les contesté que Racing el domingo me dé una alegría...'
echo 'para poder festejar con toda la gente...'
echo 'Racing, mi buen amigo...'
environment {
IMAGE_NAME = 'test-img'
DOCKERHUB_USER = 'ramonvasquezliesa'
DOCKERHUB_REPO = 'ramonvasquezliesa/test-img'
IMAGE_TAG = 'latest'
IMAGE = "${DOCKERHUB_REPO}:${IMAGE_TAG}"
CONTAINER_NAME = 'test-img-container'
PORTS = '8081:80'
VOLUMES = ''
ENV_FILE = ''
REMOVE_DANGLING_IMAGES = 'true'
USE_DOCKERHUB_LOGIN = 'false'
FORGEJO_URL = 'https://forgejo.test.dev.it.liesa.com.ar'
FORGEJO_REPO = 'ramon.vasquez/test-img.git'
// Policy
REQUIRE_VERSION_TAG = 'true' // Fail if no matching tag exists remotely
}
stages {
stage('Checkout (with submodules)') {
steps {
checkout(scmGit(
branches: [[name: '*/main']],
userRemoteConfigs: [[
url: 'https://forgejo.test.dev.it.liesa.com.ar/ramon.vasquez/pl-1.git',
credentialsId: 'hermes'
]],
// extensions: [
// [$class: 'SubmoduleOption',
// disableSubmodules: false,
// recursiveSubmodules: true,
// parentCredentials: true,
// trackingSubmodules: true
// ]
// ]
))
}
}
stage('Docker Login (optional)') {
when {
expression { return env.USE_DOCKERHUB_LOGIN?.toBoolean() }
}
steps {
withCredentials([
usernamePassword(
credentialsId: 'dockerhub-credentials',
usernameVariable: 'DOCKERHUB_USER',
passwordVariable: 'DOCKERHUB_PASS'
)
]) {
sh '''
set -eu pipefail
echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USER" --password-stdin
'''
}
}
}
// 🔎 Resolve the latest version tag from the Forgejo repo *remotely* (no local clone required)
stage('Resolve Latest Version Tag (Forgejo remote)') {
steps {
sh '''
set -eu pipefail
: > .latest_version_tag
: > .tag_check_status
: > .all_tags
: > .matching_tags
# Version pattern: vN, vN.N, vN.N.N (N = digits)
pattern='^[vV][0-9]+(\\.[0-9]+){0,2}$'
# List all tags from remote (strip refs/tags/ and peeled ^{} suffixes), unique, sorted
git ls-remote --tags "${FORGEJO_URL}/${FORGEJO_REPO}" \
| awk '{print $2}' \
| sed -E 's@^refs/tags/@@; s/\\^\\{\\}$//' \
| sort -u > .all_tags
# Filter only tags that match the version pattern
matches="$(grep -E "${pattern}" .all_tags || true)"
printf '%s\n' "$matches" > .matching_tags
if [ -z "$matches" ]; then
echo 'NONE' > .tag_check_status
exit 0
fi
# Select numerically highest MAJOR.MINOR.PATCH (missing minor/patch => 0)
latest_line="$(
awk '
function to_num(x){ return (x == "" ? 0 : x) + 0 }
{
orig=$0
t=$0
gsub(/^[vV]/,"",t)
n=split(t, a, ".")
maj=to_num(a[1]); min=to_num(a[2]); pat=to_num(a[3])
# zero-padded numeric key → stable lexical sort
printf("%09d.%09d.%09d %s\\n", maj, min, pat, orig)
}
' .matching_tags | sort -t" " -k1,1 | tail -n1
)"
latest_tag="$(printf '%s\\n' "$latest_line" | awk '{ $1=""; sub(/^ /,""); print }')"
printf '%s\n' "$latest_tag" > .latest_version_tag
echo 'OK' > .tag_check_status
'''
script {
def status = readFile('.tag_check_status').trim()
def latestTag = readFile('.latest_version_tag').trim()
def requireTag = (env.REQUIRE_VERSION_TAG ?: 'true').toBoolean()
def PATTERN_DISPLAY = '^[vV][0-9]+(\\.[0-9]+){0,2}$'
def allTags = sh(script: 'cat .all_tags || true', returnStdout: true).trim()
def matches = sh(script: 'cat .matching_tags || true', returnStdout: true).trim()
if (status == 'NONE') {
if (requireTag) {
error('''❌ No tags in the Forgejo repo match the required version pattern ''' + PATTERN_DISPLAY)
} else {
echo ' No matching version tags found; will clone default branch and keep IMAGE_TAG as-is.'
env.LATEST_TAG = ''
}
} else {
env.LATEST_TAG = latestTag
// Optionally sync Docker tag with Git tag (comment out if you prefer not to)
env.IMAGE_TAG = latestTag
env.IMAGE = "${env.DOCKERHUB_REPO}:${env.IMAGE_TAG}"
echo "✅ Latest Forgejo tag selected: ${env.LATEST_TAG}"
echo "➡️ Docker image will use tag: ${env.IMAGE_TAG}"
}
echo '📌 All remote tags:'
echo allTags ? allTags : '<none>'
echo '📌 Matching version tags:'
echo matches ? matches : '<none>'
}
}
}
// ⬇️ Clone the Forgejo repo at the *latest version tag* (detached HEAD), or default branch if none
stage('Clone Git Repository') {
steps {
sh """
set -eu pipefail
echo "Preparing clone from ${FORGEJO_URL}/${FORGEJO_REPO} ..."
rm -rf test-img || true
if [ -n "\${LATEST_TAG:-}" ]; then
echo "Cloning tag: \${LATEST_TAG}"
git clone --branch "\${LATEST_TAG}" --depth 1 "${FORGEJO_URL}/${FORGEJO_REPO}" test-img
else
echo "Cloning default branch (no version tag selected)"
git clone "${FORGEJO_URL}/${FORGEJO_REPO}" test-img
fi
cd test-img
echo "Repository ready at commit: \$(git rev-parse --short HEAD)"
"""
}
}
stage('Pull or Build Image') {
steps {
sh '''
set -eu pipefail
echo "Pulling $IMAGE ..."
docker pull "$IMAGE"
docker image inspect "$IMAGE" >/dev/null
echo "Image ready: $IMAGE"
'''
}
}
stage('Deploy (stop old, run new)') {
steps {
sh label: 'Deploy with bash', script: '''
bash -euo pipefail <<'BASH'
# Default possibly-unset Jenkins params to empty strings
: "${PORTS:=}"
: "${VOLUMES:=}"
: "${ENV_FILE:=}"
# Stop & remove existing container (if any)
if docker ps -a --format '{{.Names}}' | grep -w "$CONTAINER_NAME" >/dev/null 2>&1; then
echo "Stopping and removing existing container: $CONTAINER_NAME"
docker rm -f "$CONTAINER_NAME" || true
fi
# Check host-port availability (fail fast if any are busy)
if [ -n "$PORTS" ]; then
IFS=', ' read -r -a PORT_ARR <<< "$PORTS"
BUSY=""
for map in "${PORT_ARR[@]}"; do
[ -z "$map" ] && continue
IFS=':' read -r p1 p2 p3 <<< "$map"
if [ -n "$p3" ]; then HP="$p2"; else HP="$p1"; fi
HP="${HP%%/*}" # strip /proto
[ -z "$HP" ] && continue
if command -v ss >/dev/null 2>&1; then
if ss -ltnH | awk '{print $4}' | grep -Eq "(:|\\.)${HP}$"; then
BUSY="$BUSY $HP"
fi
elif command -v lsof >/dev/null 2>&1; then
if lsof -iTCP:"$HP" -sTCP:LISTEN -P -n >/dev/null 2>&1; then
BUSY="$BUSY $HP"
fi
fi
done
if [ -n "$BUSY" ]; then
echo "ERROR: Host port(s) in use:$BUSY"
echo "Hint: Jenkins usually listens on 8081. Change PORTS to something like '8081:80'"
exit 1
fi
fi
# Build runtime args dynamically
RUNTIME_ARGS=""
if [ -n "$PORTS" ]; then
IFS=', ' read -r -a PORT_ARR <<< "$PORTS"
for p in "${PORT_ARR[@]}"; do
[ -n "$p" ] && RUNTIME_ARGS="$RUNTIME_ARGS -p $p"
done
fi
if [ -n "$VOLUMES" ]; then
IFS=', ' read -r -a VOL_ARR <<< "$VOLUMES"
for v in "${VOL_ARR[@]}"; do
[ -n "$v" ] && RUNTIME_ARGS="$RUNTIME_ARGS -v $v"
done
fi
if [ -n "$ENV_FILE" ]; then
RUNTIME_ARGS="$RUNTIME_ARGS --env-file \"$ENV_FILE\""
fi
echo "Running container: $CONTAINER_NAME from $IMAGE"
set -x
# shellcheck disable=SC2086
docker run -d --restart on-failure --name "$CONTAINER_NAME" $RUNTIME_ARGS "$IMAGE"
set +x
echo "Container status:"
docker ps --filter "name=^${CONTAINER_NAME}$"
'''
}
}
}
post {
always {
script {
if ((env.REMOVE_DANGLING_IMAGES ?: 'true').toBoolean()) {
sh 'docker image prune -f || true'
}
}
sh 'docker logout || true'
}
}
}