Files
tranZPUter/Jenkinsfile
Philip Smart 9ea41d34ad Drop the licence commentary from the pipeline
The environment variable is enough on its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 23:16:55 +01:00

457 lines
24 KiB
Groovy

// Build pipeline for tranZPUter.
//
// This lives in the repository so that it changes together with the build it drives, rather than
// being pasted into the Jenkins job where it could not follow the repository.
//
// The build clones the repository itself into a tranZPUter/ subdirectory, which is what the rest of
// the stages expect, so the job's own checkout is skipped and only this file is read from SCM.
//
// The Gitea API token is bound from the Jenkins credential 'gitea-token', which also makes Jenkins
// mask it in the build log.
pipeline {
agent any
environment {
GITEA_URL = "https://git.eaw.app"
REPO_URL = "https://git.eaw.app/eaw/tranZPUter.git"
GITEA_TOKEN = credentials('gitea-token')
GITEA_OWNER = "eaw"
GITEA_REPO = "tranZPUter"
QUARTUS_171 = "quartus-ii-17.1.1"
QUARTUS_130 = "quartus-ii-13.0.1"
QUARTUS_BIN_171 = "/opt/altera/quartus/bin"
QUARTUS_BIN_130 = "/opt/altera/quartus/bin"
QUARTUS_MAC = "02:50:dd:72:03:01"
QUARTUS_LIC = "/opt/altera/license.dat"
BUILD_BRANCH = "v2.3-tranZPUter-SW-HW_700_v1.3"
}
options {
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '20'))
// The build does its own clone below; this stops Jenkins also checking the repository out
// over the top of the workspace.
skipDefaultCheckout()
}
stages {
stage('Checkout') {
steps {
cleanWs()
sh 'git clone --branch ${BUILD_BRANCH} --depth 1 ${REPO_URL} tranZPUter'
script {
def commitHash = sh(script: "cd tranZPUter && git rev-parse --short HEAD", returnStdout: true).trim()
def buildDate = sh(script: "date +%Y%m%d", returnStdout: true).trim()
env.COMMIT_HASH = commitHash
env.BUILD_DATE = buildDate
env.RELEASE_TAG = "v2.3-build-${buildDate}-${env.BUILD_NUMBER}"
echo "Commit: ${commitHash}, Date: ${buildDate}"
}
}
}
stage('Build FPGA - SW700 v1.3') {
steps {
script {
def workspace = pwd()
def hostWorkspace = workspace.replace('/var/jenkins_home', '/srv/jenkins/data')
def fpgaBuilds = [
[host: 'MZ700', rev: 'coreMZ_E115', desc: 'MZ-700 E115 Video'],
[host: 'MZ700', rev: 'coreMZ_E75', desc: 'MZ-700 E75 Video'],
[host: 'MZ700', rev: 'coreMZ_E115_emuMZ', desc: 'MZ-700 E115 Emulator'],
[host: 'MZ700', rev: 'coreMZ_E75_emuMZ', desc: 'MZ-700 E75 Emulator'],
[host: 'MZ700', rev: 'coreMZ_E115_SoftCPU', desc: 'MZ-700 E115 SoftCPU'],
[host: 'MZ700', rev: 'coreMZ_E75_SoftCPU', desc: 'MZ-700 E75 SoftCPU'],
[host: 'MZ80A', rev: 'coreMZ_E115', desc: 'MZ-80A E115 Video'],
[host: 'MZ80A', rev: 'coreMZ_E75', desc: 'MZ-80A E75 Video'],
[host: 'MZ80A', rev: 'coreMZ_E115_emuMZ', desc: 'MZ-80A E115 Emulator'],
[host: 'MZ80A', rev: 'coreMZ_E75_emuMZ', desc: 'MZ-80A E75 Emulator'],
[host: 'MZ80A', rev: 'coreMZ_E115_SoftCPU', desc: 'MZ-80A E115 SoftCPU'],
[host: 'MZ80A', rev: 'coreMZ_E75_SoftCPU', desc: 'MZ-80A E75 SoftCPU'],
[host: 'MZ2000', rev: 'coreMZ_E115', desc: 'MZ-2000 E115 Video'],
[host: 'MZ2000', rev: 'coreMZ_E75', desc: 'MZ-2000 E75 Video'],
[host: 'MZ2000', rev: 'coreMZ_E115_emuMZ', desc: 'MZ-2000 E115 Emulator'],
[host: 'MZ2000', rev: 'coreMZ_E75_emuMZ', desc: 'MZ-2000 E75 Emulator'],
[host: 'MZ2000', rev: 'coreMZ_E115_SoftCPU', desc: 'MZ-2000 E115 SoftCPU'],
[host: 'MZ2000', rev: 'coreMZ_E75_SoftCPU', desc: 'MZ-2000 E75 SoftCPU']
]
sh 'mkdir -p release/fpga/SW700_v1.3'
def successCount = 0
def failCount = 0
for (build in fpgaBuilds) {
def host = build.host
def rev = build.rev
def desc = build.desc
def buildDir = "tranZPUter/FPGA/SW700/v1.3/${host}/build"
def outName = "${host}_${rev}"
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
echo "==> Building FPGA: ${desc} (${rev})"
def rc = sh(script: """
RC=0
docker run --rm \
--mac-address '${QUARTUS_MAC}' \
-e 'LM_LICENSE_FILE=${QUARTUS_LIC}' \
-v '/srv2:/srv2:ro' \
-v '/run/udev:/run/udev:ro' \
-v '/sys:/sys:ro' \
-v '${hostWorkspace}/tranZPUter:${workspace}/tranZPUter' \
-w '${workspace}/${buildDir}' \
${QUARTUS_171} \
${QUARTUS_BIN_171}/quartus_sh --flow compile coreMZ -c ${rev} 2>&1 || RC=\$?
exit \$RC
""", returnStatus: true)
if (rc == 0) {
sh """
if [ -f '${buildDir}/output_files/${rev}.sof' ]; then
cp '${buildDir}/output_files/${rev}.sof' 'release/fpga/SW700_v1.3/${outName}.sof'
echo ' [OK] ${desc}: ${outName}.sof'
fi
if [ -f '${buildDir}/output_files/${rev}.rbf' ]; then
cp '${buildDir}/output_files/${rev}.rbf' 'release/fpga/SW700_v1.3/${outName}.rbf'
elif [ -f '${buildDir}/output_files/${rev}.pof' ]; then
cp '${buildDir}/output_files/${rev}.pof' 'release/fpga/SW700_v1.3/${outName}.pof'
fi
"""
} else {
echo " [FAIL] ${desc}: compilation failed (rc=${rc})"
}
sh "rm -rf '${buildDir}/db' '${buildDir}/incremental_db' '${buildDir}/output_files' 2>/dev/null || true"
}
}
}
}
}
stage('Build FPGA - Fusion v1.0') {
steps {
script {
def workspace = pwd()
def hostWorkspace = workspace.replace('/var/jenkins_home', '/srv/jenkins/data')
def fusionBuilds = [
[rev: 'coreMZ_E115', desc: 'Fusion E115 Video'],
[rev: 'coreMZ_E75', desc: 'Fusion E75 Video'],
[rev: 'coreMZ_E115_emuMZ', desc: 'Fusion E115 Emulator'],
[rev: 'coreMZ_E75_emuMZ', desc: 'Fusion E75 Emulator'],
[rev: 'coreMZ_E115_SoftCPU', desc: 'Fusion E115 SoftCPU'],
[rev: 'coreMZ_E75_SoftCPU', desc: 'Fusion E75 SoftCPU']
]
sh 'mkdir -p release/fpga/Fusion_v1.0'
def buildDir = 'tranZPUter/FPGA/Fusion/v1.0/build'
for (build in fusionBuilds) {
def rev = build.rev
def desc = build.desc
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
echo "==> Building FPGA: ${desc} (${rev})"
def rc = sh(script: """
RC=0
docker run --rm \
--mac-address '${QUARTUS_MAC}' \
-e 'LM_LICENSE_FILE=${QUARTUS_LIC}' \
-v '/srv2:/srv2:ro' \
-v '/run/udev:/run/udev:ro' \
-v '/sys:/sys:ro' \
-v '${hostWorkspace}/tranZPUter:${workspace}/tranZPUter' \
-w '${workspace}/${buildDir}' \
${QUARTUS_171} \
${QUARTUS_BIN_171}/quartus_sh --flow compile coreMZ -c ${rev} 2>&1 || RC=\$?
exit \$RC
""", returnStatus: true)
if (rc == 0) {
sh """
if [ -f '${buildDir}/output_files/${rev}.sof' ]; then
cp '${buildDir}/output_files/${rev}.sof' 'release/fpga/Fusion_v1.0/${rev}.sof'
echo ' [OK] ${desc}: ${rev}.sof'
fi
if [ -f '${buildDir}/output_files/${rev}.rbf' ]; then
cp '${buildDir}/output_files/${rev}.rbf' 'release/fpga/Fusion_v1.0/${rev}.rbf'
fi
"""
} else {
echo " [FAIL] ${desc}: compilation failed (rc=${rc})"
}
sh "rm -rf '${buildDir}/db' '${buildDir}/incremental_db' '${buildDir}/output_files' 2>/dev/null || true"
}
}
}
}
}
stage('Build CPLD - SW700 v1.3') {
steps {
script {
def workspace = pwd()
def hostWorkspace = workspace.replace('/var/jenkins_home', '/srv/jenkins/data')
def cpldBuilds = [
[host: 'MZ700', qpf: 'tranZPUterSW700', desc: 'SW700 CPLD MZ-700'],
[host: 'MZ80A', qpf: 'tzpuSW700_MZ80A', desc: 'SW700 CPLD MZ-80A'],
[host: 'MZ2000', qpf: 'tranZPUterSW700', desc: 'SW700 CPLD MZ-2000']
]
sh 'mkdir -p release/cpld/SW700_v1.3'
for (build in cpldBuilds) {
def host = build.host
def qpf = build.qpf
def desc = build.desc
def buildDir = "tranZPUter/CPLD/SW700/v1.3/${host}/build"
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
echo "==> Building CPLD: ${desc} (${qpf})"
def rc = sh(script: """
RC=0
docker run --rm \
--mac-address '${QUARTUS_MAC}' \
-e 'LM_LICENSE_FILE=${QUARTUS_LIC}' \
-v '/srv2:/srv2:ro' \
-v '/run/udev:/run/udev:ro' \
-v '/sys:/sys:ro' \
-v '${hostWorkspace}/tranZPUter:${workspace}/tranZPUter' \
-w '${workspace}/${buildDir}' \
${QUARTUS_130} \
${QUARTUS_BIN_130}/quartus_sh --flow compile ${qpf} 2>&1 || RC=\$?
exit \$RC
""", returnStatus: true)
if (rc == 0) {
sh """
if [ -f '${buildDir}/output_files/${qpf}.pof' ]; then
cp '${buildDir}/output_files/${qpf}.pof' 'release/cpld/SW700_v1.3/${host}_${qpf}.pof'
echo ' [OK] ${desc}: ${host}_${qpf}.pof'
fi
"""
} else {
echo " [FAIL] ${desc}: compilation failed (rc=${rc})"
}
sh "rm -rf '${buildDir}/db' '${buildDir}/incremental_db' '${buildDir}/output_files' 2>/dev/null || true"
}
}
}
}
}
stage('Build CPLD - FusionX v1.0') {
steps {
script {
def workspace = pwd()
def hostWorkspace = workspace.replace('/var/jenkins_home', '/srv/jenkins/data')
def cpldBuilds = [
[host: 'MZ700', qpf: 'tranZPUterSW700', desc: 'FusionX CPLD MZ-700'],
[host: 'MZ80A', qpf: 'tzpuSW700_MZ80A', desc: 'FusionX CPLD MZ-80A'],
[host: 'MZ2000', qpf: 'tranZPUterSW700', desc: 'FusionX CPLD MZ-2000']
]
sh 'mkdir -p release/cpld/FusionX_v1.0'
for (build in cpldBuilds) {
def host = build.host
def qpf = build.qpf
def desc = build.desc
def buildDir = "tranZPUter/CPLD/FusionX/v1.0/${host}/build"
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
echo "==> Building CPLD: ${desc} (${qpf})"
def rc = sh(script: """
RC=0
docker run --rm \
--mac-address '${QUARTUS_MAC}' \
-e 'LM_LICENSE_FILE=${QUARTUS_LIC}' \
-v '/srv2:/srv2:ro' \
-v '/run/udev:/run/udev:ro' \
-v '/sys:/sys:ro' \
-v '${hostWorkspace}/tranZPUter:${workspace}/tranZPUter' \
-w '${workspace}/${buildDir}' \
${QUARTUS_130} \
${QUARTUS_BIN_130}/quartus_sh --flow compile ${qpf} 2>&1 || RC=\$?
exit \$RC
""", returnStatus: true)
if (rc == 0) {
sh """
if [ -f '${buildDir}/output_files/${qpf}.pof' ]; then
cp '${buildDir}/output_files/${qpf}.pof' 'release/cpld/FusionX_v1.0/${host}_${qpf}.pof'
echo ' [OK] ${desc}: ${host}_${qpf}.pof'
fi
"""
} else {
echo " [FAIL] ${desc}: compilation failed (rc=${rc})"
}
sh "rm -rf '${buildDir}/db' '${buildDir}/incremental_db' '${buildDir}/output_files' 2>/dev/null || true"
}
}
}
}
}
stage('Build CPLD - Legacy') {
steps {
script {
def workspace = pwd()
def hostWorkspace = workspace.replace('/var/jenkins_home', '/srv/jenkins/data')
sh 'mkdir -p release/cpld/Legacy'
// tranZPUterSW original: QSF in SW/build/ refs ../tranZPUterSW_*.vhd
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
def swBuildDir = 'tranZPUter/CPLD/SW/build'
echo "==> Building CPLD: tranZPUterSW Original (tranZPUterSW)"
def rc = sh(script: """
RC=0
docker run --rm \
--mac-address '${QUARTUS_MAC}' \
-e 'LM_LICENSE_FILE=${QUARTUS_LIC}' \
-v '/srv2:/srv2:ro' \
-v '/run/udev:/run/udev:ro' \
-v '/sys:/sys:ro' \
-v '${hostWorkspace}/tranZPUter:${workspace}/tranZPUter' \
-w '${workspace}/${swBuildDir}' \
${QUARTUS_130} \
${QUARTUS_BIN_130}/quartus_sh --flow compile tranZPUterSW 2>&1 || RC=\$?
exit \$RC
""", returnStatus: true)
if (rc == 0) {
sh """
if [ -f '${swBuildDir}/output_files/tranZPUterSW.pof' ]; then
cp '${swBuildDir}/output_files/tranZPUterSW.pof' 'release/cpld/Legacy/tranZPUterSW.pof'
echo ' [OK] tranZPUterSW Original: tranZPUterSW.pof'
fi
"""
} else {
echo " [FAIL] tranZPUterSW Original: compilation failed (rc=${rc})"
}
sh "rm -rf '${swBuildDir}/db' '${swBuildDir}/incremental_db' '${swBuildDir}/output_files' 2>/dev/null || true"
}
// EPM7160: QSF in EPM7160/ refs ../tranZPUterSW_*_EPM7160.vhd
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
def epmDir = 'tranZPUter/CPLD/EPM7160'
echo "==> Building CPLD: EPM7160 Variant (tranZPUterSW_EPM7160)"
def rc = sh(script: """
RC=0
docker run --rm \
--mac-address '${QUARTUS_MAC}' \
-e 'LM_LICENSE_FILE=${QUARTUS_LIC}' \
-v '/srv2:/srv2:ro' \
-v '/run/udev:/run/udev:ro' \
-v '/sys:/sys:ro' \
-v '${hostWorkspace}/tranZPUter:${workspace}/tranZPUter' \
-w '${workspace}/${epmDir}' \
${QUARTUS_130} \
${QUARTUS_BIN_130}/quartus_sh --flow compile tranZPUterSW_EPM7160 2>&1 || RC=\$?
exit \$RC
""", returnStatus: true)
if (rc == 0) {
sh """
if [ -f '${epmDir}/output_files/tranZPUterSW_EPM7160.pof' ]; then
cp '${epmDir}/output_files/tranZPUterSW_EPM7160.pof' 'release/cpld/Legacy/tranZPUterSW_EPM7160.pof'
echo ' [OK] EPM7160 Variant: tranZPUterSW_EPM7160.pof'
fi
"""
} else {
echo " [FAIL] EPM7160 Variant: compilation failed (rc=${rc})"
}
sh "rm -rf '${epmDir}/db' '${epmDir}/incremental_db' '${epmDir}/output_files' 2>/dev/null || true"
}
}
}
}
stage('Package Results') {
steps {
script {
sh """
cd ${WORKSPACE}
echo '=== Build Summary ==='
FPGA_COUNT=\$(find release/fpga -name '*.sof' 2>/dev/null | wc -l)
CPLD_COUNT=\$(find release/cpld -name '*.pof' 2>/dev/null | wc -l)
echo "FPGA SOF files: \${FPGA_COUNT}"
echo "CPLD POF files: \${CPLD_COUNT}"
if [ "\$FPGA_COUNT" -gt 0 ] || [ "\$CPLD_COUNT" -gt 0 ]; then
cd release
tar czf ../tranZPUter-${BUILD_DATE}-build${BUILD_NUMBER}.tar.gz .
cd ..
echo "[OK] tranZPUter-${BUILD_DATE}-build${BUILD_NUMBER}.tar.gz"
fi
"""
archiveArtifacts artifacts: "tranZPUter-*.tar.gz", allowEmptyArchive: true
archiveArtifacts artifacts: "release/fpga/**/*.sof", allowEmptyArchive: true
archiveArtifacts artifacts: "release/fpga/**/*.rbf", allowEmptyArchive: true
archiveArtifacts artifacts: "release/cpld/**/*.pof", allowEmptyArchive: true
}
}
}
stage('Create Gitea Release') {
steps {
script {
def tarball = "tranZPUter-${BUILD_DATE}-build${BUILD_NUMBER}.tar.gz"
def releaseBody = "tranZPUter Build ${BUILD_DATE} #${BUILD_NUMBER}\\n\\nBranch: ${BUILD_BRANCH}\\nCommit: ${COMMIT_HASH}\\n\\nFPGA builds (Quartus 17.1.1):\\n- SW700 v1.3: MZ-700, MZ-80A, MZ-2000 x E115/E75 x Video/Emulator/SoftCPU\\n- Fusion v1.0: E115/E75 x Video/Emulator/SoftCPU\\n\\nCPLD builds (Quartus 13.0 SP1):\\n- SW700 v1.3: MZ-700, MZ-80A, MZ-2000\\n- FusionX v1.0: MZ-700, MZ-80A, MZ-2000\\n- Legacy: tranZPUterSW, EPM7160"
def createRelease = sh(script: """
curl -sf -X POST \
-H 'Authorization: token ${GITEA_TOKEN}' \
-H 'Content-Type: application/json' \
-d '{"tag_name": "${RELEASE_TAG}", "name": "tranZPUter ${BUILD_DATE}", "body": "${releaseBody}", "draft": false, "prerelease": false, "target_commitish": "${BUILD_BRANCH}"}' \
'${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases' \
| grep -o '"id":[0-9]*' | head -1 | grep -o '[0-9]*'
""", returnStdout: true).trim()
if (createRelease) {
echo "Release created with ID: ${createRelease}"
if (fileExists(tarball)) {
sh """
curl -sf -X POST \
-H 'Authorization: token ${GITEA_TOKEN}' \
-F 'attachment=@${tarball}' \
'${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}/releases/${createRelease}/assets?name=${tarball}'
"""
echo "Tarball uploaded to release"
}
} else {
echo "Warning: Could not create Gitea release"
}
}
}
}
}
post {
success {
echo "tranZPUter build completed."
mail to: 'support@net2net-ips.com',
subject: "tranZPUter Build ${BUILD_DATE} - SUCCESS",
body: "tranZPUter build completed.\n\nJob: ${env.JOB_NAME} #${env.BUILD_NUMBER}\nURL: ${env.BUILD_URL}\nCommit: ${env.COMMIT_HASH}\n\nCheck artifacts for built FPGA/CPLD files."
}
failure {
echo "tranZPUter build failed."
mail to: 'support@net2net-ips.com',
subject: "tranZPUter Build ${BUILD_DATE} - FAILED",
body: "tranZPUter build failed.\n\nJob: ${env.JOB_NAME} #${env.BUILD_NUMBER}\nURL: ${env.BUILD_URL}"
}
always {
cleanWs()
}
}
}