Jenkins CI/CD Hands-On: Building an Automated Pipeline from Scratch

From installing Jenkins and configuring Gitee credentials, to auto-building a Hugo site on push via Webhook + ngrok — a complete practical walkthrough with pitfalls.

1. Why Jenkins

Jenkins is an open-source CI/CD tool. Its core purpose: after code is pushed to a Git repository, automatically run “fetch code → build → test → package → deploy”.

2. Installation

Prerequisite: Java

Jenkins requires Java 17 or 21. Newer versions (e.g., Java 26) fail to start:

$$Running with Java 26 ... not yet fully supported. Supported Java versions are: [17, 21]$$
wget https://get.jenkins.io/war-stable/latest/jenkins.war -O /tmp/jenkins.war
/opt/jdk-21/bin/java -jar /tmp/jenkins.war --httpPort=9090
docker run -d \
  --name jenkins \
  -p 9090:8080 \
  -v jenkins_home:/var/jenkins_home \
  jenkins/jenkins:lts-jdk21

3. Initial Setup

Visit http://localhost:9090:

  1. Enter the initial password (cat ~/.jenkins/secrets/initialAdminPassword)
  2. Choose plugin installation (skip recommended plugins, install what you need)
  3. Create an admin account (remember the password!)

4. Plugins

Manage Jenkins → Plugins → Available plugins

PluginPurpose
GitPull code from Git repos
PipelinePipeline job type
GiteeGitee integration + Webhook trigger

Pitfall: plugin versions may require a newer Jenkins. Upgrade Jenkins to the latest LTS:

wget https://get.jenkins.io/war-stable/latest/jenkins.war -O /tmp/jenkins.war

5. Creating a Pipeline Job

New Item → name → Pipeline → OK

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                git url: 'git@gitee.com:your-user/your-repo.git',
                    branch: 'master'
            }
        }

        stage('Build') {
            steps {
                sh 'echo building && ls -la'
            }
        }
    }

    post {
        success { echo '✅ Build succeeded' }
        failure { echo '❌ Build failed' }
    }
}

Debug tip: first run a pure debug script to verify the Jenkins environment:

pipeline {
    agent any
    stages {
        stage('Debug') {
            steps {
                echo 'Pipeline works!'
                sh 'echo pwd: $(pwd) && echo user: $(whoami)'
            }
        }
    }
}

6. Gitee Credentials

Generate a personal access token at https://gitee.com/profile/personal_access_tokens (scope: projects).

Manage Jenkins → Credentials → System → Global credentials → Add Credentials

FieldValue
KindUsername with password
UsernameGitee username
PasswordPersonal access token (NOT login password)
IDgitee-git

Use it in the pipeline:

git url: 'https://gitee.com/user/repo.git',
    branch: 'master',
    credentialsId: 'gitee-git'

⚠️ Pitfall: the Gitee API Token credential type (added by the Gitee plugin) cannot be used for git clone. You need Username with password.

Easier: if SSH keys are configured locally, use the SSH URL — no credentials needed:

$$git@gitee.com:user/repo.git$$

7. Auto-Trigger (Webhook + ngrok)

Gitee cannot reach localhost:9090, so expose it with ngrok:

./ngrok config add-authtoken YOUR_TOKEN
./ngrok http 9090

Steps:

  1. Set Jenkins URL (Manage Jenkins → System) to the ngrok address
  2. Job → ConfigureBuild Triggers → check Gitee webhook trigger
  3. Copy the Webhook URL shown on the page
  4. Gitee repo → ManageWebHooksAdd WebHook:
    • URL: the copied address
    • Events: Push

Alternative: use Poll SCM with H/10 * * * * to check every 10 minutes.

8. Troubleshooting

8.1 No valid crumb (403)

Jenkins CSRF protection blocks Webhook/API requests. For test environments, remove from ~/.jenkins/config.xml:

<crumbIssuer class="hudson.security.csrf.DefaultCrumbIssuer"/>

Restart Jenkins.

8.2 Forgotten admin password

Option A: stop Jenkins → edit ~/.jenkins/users/admin/config.xml → delete the whole block:

<hudson.security.HudsonPrivateSecurityRealm_-Details>
  <passwordHash>...</passwordHash>
</hudson.security.HudsonPrivateSecurityRealm_-Details>

Restart, enter any password to reset.

Option B: replace the hash directly:

python3 -c "
import bcrypt
print(bcrypt.hashpw(b'admin123', bcrypt.gensalt(10, prefix=b'2a')).decode())
"

Option C: set <useSecurity>false</useSecurity> in ~/.jenkins/config.xml, restart, create a new user, then re-enable security.

8.3 Git clone auth failure

$$Authentication failed for 'https://gitee.com/...'$$

Private repo → configure credentials (section 6); or switch to the SSH URL.

8.4 Wrong Git URL format

$$❌ https:///gitee.com:user/repo.git ✅ https://gitee.com/user/repo.git ✅ git@gitee.com:user/repo.git$$

8.5 Hugo config not found

$$ERROR Unable to locate config file or config directory$$

Jenkins cloned the wrong repo — check the git url points to the actual Hugo site repo.

9. Full Example: Auto-Building a Hugo Site

The migelan-touch repo is a Hugo site with public/ committed to Gitee. Goal: push source → auto hugo build → auto-commit public/.

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                git url: 'git@gitee.com:user/migelan-touch.git',
                    branch: 'master'
            }
        }

        stage('Build Hugo') {
            steps {
                sh 'hugo'
            }
        }

        stage('Commit Artifacts') {
            steps {
                sh '''
                    git add public/
                    git commit -m "auto build: $(date)" || echo "no changes"
                    git push origin master
                '''
            }
        }
    }

    post {
        success { echo '✅ Site updated' }
        failure { echo '❌ Build failed' }
    }
}

10. Quick Command Reference

# Start / stop Jenkins
/opt/jdk-21/bin/java -jar /tmp/jenkins.war --httpPort=9090
pkill -f jenkins.war

# Initial password
cat ~/.jenkins/secrets/initialAdminPassword

# Logs
tail -100 ~/.jenkins/logs/jenkins.log

# Reload config without restart
curl http://localhost:9090/reload

# ngrok
./ngrok http 9090
curl -s http://localhost:4040/api/tunnels | grep public_url