Files
jenkins-pipeline/SCM/部署镜像/1.groovy

118 lines
4.5 KiB
Groovy
Raw Normal View History

2025-11-29 19:11:23 +08:00
import groovy.json.JsonSlurper
def resultHtml = '' // 初始化一个变量来存储最终的 HTML
try {
// --- 1. 参数处理 ---
def rawInput = IMAGE_NAME ?: ''
def pureTag = rawInput.split(/\s*\|\s*/)[0].trim()
if (!pureTag) {
resultHtml = '<div style="color: gray; font-style: italic;">(请选择镜像 tag)</div>'
return resultHtml // 立即返回
}
// --- 2. 硬编码凭据 ---
def user = '100038894437' // 你的 TCR 用户名 (UIN)
def pass = 'h8H1o6Fd!HLXn' // 你的 TCR 登录密码
if (!user || !pass) {
resultHtml = '<div style="color: red;">❌ 凭据为空</div>'
return resultHtml // 立即返回
}
// --- 3. API 查询 ---
final String REG = "https://uswccr.ccs.tencentyun.com"
final String REPO = "lessiesit/flymoon-email"
// 3.1. Token
def auth = "Basic " + "${user}:${pass}".bytes.encodeBase64().toString()
def tokenUrl = new URL("${REG}/service/token?service=uswccr&scope=repository:${REPO}:pull")
def tokenConn = tokenUrl.openConnection()
tokenConn.setRequestProperty("Authorization", auth)
tokenConn.connect()
if (tokenConn.responseCode != 200) {
def errorBody = tokenConn.errorStream?.text ?: "No error body"
resultHtml = "<div style='color: red;'>❌ Token 请求失败 (${tokenConn.responseCode}): ${errorBody.take(200)}</div>"
return resultHtml // 立即返回
}
def tokenJson = new JsonSlurper().parse(tokenConn.inputStream)
def token = tokenJson?.token
if (!token) {
resultHtml = '<div style="color: red;">❌ 无法从响应中获取 Token</div>'
return resultHtml // 立即返回
}
// 3.2. Manifest
def manifestUrl = new URL("${REG}/v2/${REPO}/manifests/${pureTag}")
def manifestConn = manifestUrl.openConnection()
manifestConn.setRequestProperty("Authorization", "Bearer ${token}")
manifestConn.setRequestProperty("Accept", "application/vnd.docker.distribution.manifest.v2+json")
manifestConn.connect()
if (manifestConn.responseCode != 200) {
if (manifestConn.responseCode == 404) {
resultHtml = "<div style='color: orange;'>⚠️ Tag '${pureTag}' 不存在</div>"
return resultHtml // 立即返回
}
def errorBody = manifestConn.errorStream?.text ?: "No error body"
resultHtml = "<div style='color: red;'>❌ Manifest 请求失败 (${manifestConn.responseCode}): ${errorBody.take(200)}</div>"
return resultHtml // 立即返回
}
def manifestJson = new JsonSlurper().parse(manifestConn.inputStream)
def configDigest = manifestJson?.config?.digest
if (!configDigest) {
resultHtml = '<div style="color: red;">❌ Manifest 中无 config.digest</div>'
return resultHtml // 立即返回
}
// 3.3. Config Blob (Labels)
def blobUrl = new URL("${REG}/v2/${REPO}/blobs/${configDigest}")
def blobConn = blobUrl.openConnection()
blobConn.setRequestProperty("Authorization", "Bearer ${token}")
blobConn.connect()
if (blobConn.responseCode != 200) {
def errorBody = blobConn.errorStream?.text ?: "No error body"
resultHtml = "<div style='color: red;'>❌ Config Blob 请求失败 (${blobConn.responseCode}): ${errorBody.take(200)}</div>"
return resultHtml // 立即返回
}
def configJson = new JsonSlurper().parse(blobConn.inputStream)
def labels = configJson?.config?.Labels ?: [:]
// --- 4. 格式化输出 ---
// 再次确保 labels 是 Map
if (!(labels instanceof Map)) {
labels = [:]
}
resultHtml = "<div style='font-family: monospace; font-size: 12px; padding: 5px; border: 1px solid #ccc; background-color: #f9f9f9;'><strong>🏷️ Labels for ${pureTag}:</strong><br/>"
if (labels.isEmpty()) {
resultHtml += "<em>(无 Labels</em><br/>"
} else {
labels.each { k, v ->
def key = (k != null) ? k.toString() : 'null_key'
def value = (v != null) ? v.toString().replaceAll(/^'|'$/, '') : 'null_value'
resultHtml += "<strong>${key}:</strong> ${value}<br/>"
}
}
resultHtml += "</div>"
// ✅ 修复:在 try 块结束前,确保 resultHtml 是字符串
resultHtml = resultHtml.toString()
} catch (Exception e) {
def errorMsg = e.message?.toString() ?: '未知错误'
def errorClass = e.class.simpleName
resultHtml = "<div style='color: red;'>🚨 脚本异常: ${errorClass} - ${errorMsg}</div>"
}
// ✅ 修复:确保在所有路径下,最终返回的都是字符串
return resultHtml.toString()