Skip to content

Instantly share code, notes, and snippets.

@verazza
Last active April 7, 2025 23:39
Show Gist options
  • Save verazza/46c053440a59b244fdde0039373916e5 to your computer and use it in GitHub Desktop.
Save verazza/46c053440a59b244fdde0039373916e5 to your computer and use it in GitHub Desktop.
Jar-AutoBuildAndPusher

Jar-AutoBuildAndPusher

This project utilizes Jenkins for automated builds and releases triggered by GitHub webhooks.

Key Features:

  • Automated Builds: Triggers builds upon Git pushes.
  • Automatic Tag and Release Creation: Creates Git tags and GitHub releases.
  • Discord Notifications: Sends notifications to Discord for build status and releases.

Here's an example of the Discord notification:

Discord Notification

Setup Instructions:

To set up this automated build and release pipeline, follow these steps:

1. Jenkins Configuration:

  • Install Required Plugins:
    • Ensure you have the following Jenkins plugins installed:
      • Git plugin
      • Gradle plugin (if using Gradle)
      • Credentials plugin
      • HTTP Request plugin
  • Configure Credentials:
    • In Jenkins, go to "Credentials" and create the following credentials:
      • GitUserName: Your Git username.
      • GitUserEmail: Your Git email address.
      • GitHub-Token: A GitHub personal access token with repo scope.
      • Discord-Webhook-URL: The Discord webhook URL for notifications.
  • Create a Jenkins Pipeline Job:
    • Create a new "Pipeline" job in Jenkins.
    • In the "Pipeline" section, select "Pipeline script" and paste the provided Jenkinsfile content.
  • Configure "Remote build (token auth)":
    • In the job configuration, under "Build Triggers", check "Remote build (token auth)".
    • Set an authentication token. For example kN5826t42CG2uaXpaPfX.
  • Configure Jenkins URL and user authentication:
    • The URL that the Github webhook will use needs to be reachable by github.
    • The URL also needs to have user authentication.
    • Example: https://bella:[email protected]/jenkins/job/AutoBuilder_FMC-Dependency/build?token=kN5826t42CG2uaXpaPfX
    • Where bella is the username, and 11901c5bcf5e85a0a78867233326f75e7b is the user's API token.
    • Be aware that exposing user name and API token in URL is a security risk.

2. GitHub Webhook Configuration:

  • Add a Webhook:
    • In your GitHub repository, go to "Settings" -> "Webhooks" -> "Add webhook".
    • Payload URL: Use the Jenkins remote build URL. For example: https://bella:[email protected]/jenkins/job/AutoBuilder_FMC-Dependency/build?token=kN5826t42CG2uaXpaPfX.
    • Content type: application/json.
    • Which events would you like to trigger this webhook?: Select the events that should trigger the build (e.g., "Push").
    • Click "Add webhook".

3. Jenkinsfile Configuration:

  • Environment Variables:
    • Customize the environment variables in the Jenkinsfile to match your project settings:
      • USERNAME: Your GitHub username.
      • REPO_NAME: Your repository name.
      • BRANCH_NAME: The branch to build.
      • REPO_URL: The repository URL.
      • JAR_PATH: The path to the JAR file.
      • TAG_NAME: The tag name format.
      • BUILD_INTERVAL_MINUTES: Minimum minutes between builds.
      • AUTO_BUILD_NOTIFICATION: Enable/disable Discord notifications.
      • BUILD_RESULT_NOTIFICATION: Enable/disable build result notifications.
      • WEBHOOK_NAME: The name of the webhook sender.
      • WEBHOOK_AVATAR_URL: The avatar URL for Discord notifications.
      • AUTO_BUILD_THUMBNAIL_URL, BUILD_RESULT_THUMBNAIL_URL, BUILD_FAILED_THUMBNAIL_URL: Thumbnail URLs for Discord embeds.
      • WEBHOOK_AUTO_BUILD_COLOR, WEBHOOK_BUILD_RESULT_COLOR, WEBHOOK_BUILD_RESULT_FAILDED_COLOR: Discord embed colors.
  • Credential IDs:
    • Ensure that the credentialsId values in the Jenkinsfile match the credential IDs you created in Jenkins.

License:

MIT

Jenkinsfile
pipeline {
agent any
environment {
USERNAME = 'bella2391'
REPO_NAME = 'Dependency-Provider'
BRANCH_NAME = 'master'
REPO_URL = "https://github.com/${USERNAME}/${REPO_NAME}.git"
JAR_PATH = 'build/libs/FMC-Dependency-1.0.0.jar'
TAG_NAME = "${REPO_NAME}-${BUILD_NUMBER}"
// Caution: Do not set this value too low, as it may cause Jenkins to get stuck in a loop of aborted builds.
// build intervals (mininutes)
BUILD_INTERVAL_MINUTES = 5
AUTO_BUILD_NOTIFICATION = true
BUILD_RESULT_NOTIFICATION = true
WEBHOOK_NAME = 'Jenkins'
WEBHOOK_AVATAR_URL = 'https://licensecounter.jp/devops-hub/assets/images/products/logo-jenkins680_500.png' // 'https://www.spiceworks.co.jp/blog/wp-content/uploads/2014/06/logo.png'
AUTO_BUILD_THUMBNAIL_URL = 'https://engineering.tomtom.com/assets/images/java/create-java-jar.png'
BUILD_RESULT_THUMBNAIL_URL = 'https://meister-kentei.jp/magazine/wp-content/uploads/2022/11/github-6980894_1280.png'
BUILD_FAILED_THUMBNAIL_URL = 'https://slack.engineering/wp-content/uploads/sites/7/2021/05/jenkins-fire.png'
WEBHOOK_AUTO_BUILD_COLOR = 16705372 // yellow // blue: 5814783
WEBHOOK_BUILD_RESULT_COLOR = 5763719 // green
WEBHOOK_BUILD_RESULT_FAILDED_COLOR = 16711680 // red
}
stages {
stage('Check Build Interval') {
steps {
script {
def previousBuild = currentBuild.previousBuild
if (previousBuild != null) {
def currentTime = System.currentTimeMillis()
def lastBuildTime = previousBuild.getTimeInMillis()
def diffMinutes = (currentTime - lastBuildTime) / (1000 * 60)
def diffMinutesNum = diffMinutes.toDouble()
def intervalNum = BUILD_INTERVAL_MINUTES.toDouble()
echo "Condition result: ${diffMinutesNum < intervalNum}"
if (diffMinutesNum < intervalNum) {
currentBuild.result = 'ABORTED'
error("Build aborted: Only ${diffMinutes} minutes since last build. Must wait at least ${BUILD_INTERVAL_MINUTES} minutes.")
}
}
}
}
}
stage('Auto Build Notification') {
steps {
script {
if (AUTO_BUILD_NOTIFICATION) {
withCredentials([string(credentialsId: 'Discord-Webhook-URL', variable: 'DISCORD_WEBHOOK_URL')]) {
def payload = """
{
"username": "${WEBHOOK_NAME}",
"avatar_url": "${WEBHOOK_AVATAR_URL}",
"embeds": [{
"title": "自動ビルド通知",
"description": "**ビルド情報**",
"color": ${WEBHOOK_AUTO_BUILD_COLOR},
"author": {
"name": "${USERNAME}",
"icon_url": "https://github.com/${USERNAME}.png",
"url": "https://github.com/${USERNAME}"
},
"thumbnail": {
"url": "${AUTO_BUILD_THUMBNAIL_URL}"
},
"fields": [
{
"name": "ビルドURL",
"value": "${BUILD_URL}",
"inline": true
},
{
"name": "ビルド対象のレポジトリ",
"value": "${REPO_URL}",
"inline": true
}
],
"timestamp": "${new Date().format("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone('Asia/Tokyo'))}"
}]
}
"""
sh """
curl -H "Content-Type: application/json" \\
-d '${payload}' \\
${DISCORD_WEBHOOK_URL}
"""
}
}
}
}
}
stage('Checkout') {
steps {
git branch: "${BRANCH_NAME}",
url: "${REPO_URL}"
}
}
stage('Build') {
steps {
sh './gradlew build'
}
}
stage('Test') {
steps {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh './gradlew test'
}
}
}
stage('Configure Git') {
steps {
withCredentials([
string(credentialsId: 'GitUserName', variable: 'GIT_USER_NAME'),
string(credentialsId: 'GitUserEmail', variable: 'GIT_USER_EMAIL')
]) {
sh '''
git config --global user.name "${GIT_USER_NAME}"
git config --global user.email "${GIT_USER_EMAIL}"
'''
}
}
}
stage('Tag and Push') {
steps {
script {
withCredentials([string(credentialsId: 'GitHub-Token', variable: 'GIT_TOKEN')]) {
sh '''
if [ -d "${REPO_NAME}" ]; then
rm -rf ${REPO_NAME}
fi
if git rev-parse "refs/tags/${TAG_NAME}" >/dev/null 2>&1; then
echo "Tag ${TAG_NAME} already exists. Skipping tag creation."
else
git tag -a "${TAG_NAME}" -m "Jenkins Build #${BUILD_NUMBER}"
git push "https://oauth2:${GIT_TOKEN}@github.com/${USERNAME}/${REPO_NAME}.git" --tags
fi
'''
}
}
}
}
stage('Create Release on GitHub') {
steps {
script {
withCredentials([string(credentialsId: 'GitHub-Token', variable: 'GIT_TOKEN')]) {
def response = httpRequest(
acceptType: 'APPLICATION_JSON',
contentType: 'APPLICATION_JSON',
httpMode: 'POST',
url: "https://api.github.com/repos/${USERNAME}/${REPO_NAME}/releases",
requestBody: """{
"tag_name": "${TAG_NAME}",
"name": "Release ${BUILD_NUMBER}",
"body": "Release description",
"draft": false,
"prerelease": false
}""",
customHeaders: [[name: 'Authorization', value: "token ${GIT_TOKEN}"]]
)
echo "GitHub Release response content: ${response.content}"
def releaseId = new groovy.json.JsonSlurper().parseText(response.content).id
echo "GitHub Release ID: ${releaseId}"
def jarFilePath = "${JAR_PATH}"
if (!fileExists(jarFilePath)) {
error "JAR file not found: ${jarFilePath}"
}
def jarFileBytes = readFile(file: jarFilePath, encoding: 'ISO-8859-1').getBytes('ISO-8859-1')
def uploadUrl = "https://uploads.github.com/repos/${USERNAME}/${REPO_NAME}/releases/${releaseId}/assets?name=${jarFilePath.split('/').last()}"
def uploadResponse = httpRequest(
acceptType: 'APPLICATION_JSON',
contentType: 'APPLICATION_OCTETSTREAM',
httpMode: 'POST',
url: uploadUrl,
requestBody: new String(jarFileBytes, 'ISO-8859-1'),
customHeaders: [[name: 'Authorization', value: "token ${GIT_TOKEN}"],
[name: 'Content-Type', value: 'application/java-archive']]
)
echo "GitHub Upload Response: ${uploadResponse}"
}
}
}
}
stage('Discord Result Notification') {
steps {
script {
if (BUILD_RESULT_NOTIFICATION) {
withCredentials([string(credentialsId: 'Discord-Webhook-URL', variable: 'DISCORD_WEBHOOK_URL')]) {
def releaseUrl = "https://github.com/${USERNAME}/${REPO_NAME}/releases/tag/${TAG_NAME}"
def payload = """
{
"username": "${WEBHOOK_NAME}",
"avatar_url": "${WEBHOOK_AVATAR_URL}",
"embeds": [{
"title": "新しいリリースが作成されました",
"description": "**リリース情報**",
"color": ${WEBHOOK_BUILD_RESULT_COLOR},
"author": {
"name": "${USERNAME}",
"icon_url": "https://github.com/${USERNAME}.png",
"url": "https://github.com/${USERNAME}"
},
"thumbnail": {
"url": "${BUILD_RESULT_THUMBNAIL_URL}"
},
"fields": [
{
"name": "リリースタグ",
"value": "${TAG_NAME}",
"inline": true
},
{
"name": "ビルド番号",
"value": "#${BUILD_NUMBER}",
"inline": true
},
{
"name": "リリースURL",
"value": "${releaseUrl}"
}
],
"timestamp": "${new Date().format("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone('Asia/Tokyo'))}"
}]
}
"""
sh """
curl -H "Content-Type: application/json" \\
-d '${payload}' \\
${DISCORD_WEBHOOK_URL}
"""
}
}
}
}
}
}
post {
failure {
script {
// Check if the failure is not from 'Check Build Interval' stage
def abortedByIntervalCheck = currentBuild.rawBuild.getCauses().any { cause ->
cause.toString().contains('Build aborted: Only')
}
if (!abortedByIntervalCheck) {
withCredentials([string(credentialsId: 'Discord-Webhook-URL', variable: 'DISCORD_WEBHOOK_URL')]) {
def payload = """
{
"username": "${WEBHOOK_NAME}",
"avatar_url": "${WEBHOOK_AVATAR_URL}",
"embeds": [{
"title": "ビルド失敗通知",
"description": "ビルドが失敗しました。詳細は [コンソールログ](${BUILD_URL}pipeline-console/ ) を確認してください。",
"color": ${WEBHOOK_BUILD_RESULT_FAILDED_COLOR},
"thumbnail": {
"url": "${BUILD_FAILED_THUMBNAIL_URL}"
},
"fields": [
{
"name": "ビルドURL",
"value": "${BUILD_URL}",
"inline": true
},
{
"name": "リポジトリ",
"value": "${REPO_URL}",
"inline": true
}
],
"timestamp": "${new Date().format("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone('Asia/Tokyo'))}"
}]
}
"""
sh """
curl -H "Content-Type: application/json" \\
-d '${payload}' \\
${DISCORD_WEBHOOK_URL}
"""
}
}
}
}
}
}
Copyright © 2025 bella2391
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment