idea 插件开发自动发布到 nexus 私服中(脚本实例)

如下脚本内容为 idea 插件开发项目中的 build.gradle.kts 文件示例,其中自定了 updatePluginsXmlToNexusuploadPluginToNexus 两个任务,一个用来自动修改 nexus 中的配置文件,一个用来自动将当前插件打包后的 zip 文件上传到 nexus 私服中。

脚本仅供参考:

import okhttp3.Credentials
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import org.dom4j.io.OutputFormat
import org.dom4j.io.SAXReader
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.w3c.dom.Element
import java.io.StringReader
import java.io.StringWriter
import java.nio.file.Files
import java.nio.file.Paths
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult
import javax.xml.xpath.XPathConstants
import javax.xml.xpath.XPathFactoryplugins {id("java") // 使用Java插件id("org.jetbrains.kotlin.jvm") version "2.1.20-RC" // 使用Kotlin JVM插件,指定版本id("org.jetbrains.intellij.platform") version "2.4.0" // 使用IntelliJ插件,用于开发JetBrains插件,指定版本id("com.github.node-gradle.node") version "7.1.0" // Node.js插件,集成Node.js和npm到Gradle构建中
}
buildscript {repositories {mavenCentral()}dependencies {// 添加 OkHttp 依赖classpath("com.squareup.okhttp3:okhttp:4.12.0")classpath("org.dom4j:dom4j:2.1.4")}
}
repositories {// mavenCentral() // 定义Maven中央仓库作为依赖库来源mavenLocal()intellijPlatform {defaultRepositories()}maven(url = "https://plugins.gradle.org/m2/")maven(url = "https://oss.sonatype.org/content/repositories/releases/")
}dependencies {compileOnly("org.projectlombok:lombok:1.18.36") // 提供Lombok支持,仅在编译时使用annotationProcessor("org.projectlombok:lombok:1.18.36") // Lombok注解处理器intellijPlatform {// 获取在gradle.properties中配置的参数platformVersionval version = providers.gradleProperty("platformVersion") // 2023.2.6// IntellijIdeaCommunity=IC, IntellijIdeaUltimate=IUcreate(org.jetbrains.intellij.platform.gradle.IntelliJPlatformType.IntellijIdeaCommunity, version)// 使用bundledPlugin设置捆绑的插件//bundledPlugin("com.intellij.java")// 使用plugin设置当前插件依赖的其他插件//plugin("org.intellij.scala", "2024.1.4")pluginVerifier()testFramework(org.jetbrains.intellij.platform.gradle.TestFrameworkType.Platform)}
}group = "com.shanhy.plugin"
version = "1.0.0-20250520"// 文档 https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform
intellijPlatform {pluginConfiguration {id = "demo-idea-apiflow-designer"name = "demo ApiFlow Designer" // 名称// 直接使用项目级别的 version 属性version = project.version.toString()description ="该插件时一个为 IntelliJ IDEA 打造的 demo Http 接口设计器插件。它提供了一个 `.apiflow` 文件编辑器,具有可视化设计快速开发 Http 接口的能力。"// 读取 CHANGELOG.md 文件的内容changeNotes = Files.readString(Paths.get(project.projectDir.path, "CHANGELOG.md"))// 适用的idea版本ideaVersion {sinceBuild = "232" // 插件支持的最低IDE构建号untilBuild = "251.*" // 插件支持的最高IDE构建号}// 产品描述productDescriptor {// ...}// 开发者信息vendor {name = "shanhy"email = "shanhy@shanhy.com"url = "http://www.shanhy.com"}}// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-signingsigning {// 配置在gradle.properties中certificateChain = providers.gradleProperty("certificateChain") // 签名证书链privateKey = providers.gradleProperty("privateKey") // 签名私钥password = providers.gradleProperty("privateKeyPassword") // 签名私钥的密码}// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-publishingpublishing {token = providers.gradleProperty("publishToken") // 发布插件时使用的令牌}// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-extension.html#intellijPlatform-pluginVerificationpluginVerification {// ...}
}node {version.set("20.11.1") // Node.js版本设置download.set(true) // 自动下载Node.js
}tasks {// 设置JVM兼容性版本withType<JavaCompile> {sourceCompatibility = JvmTarget.JVM_17.target // Java源代码兼容版本targetCompatibility = JvmTarget.JVM_17.target // 编译目标版本}withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {compilerOptions {jvmTarget.set(JvmTarget.JVM_17) // Kotlin编译目标版本}}val buildNpm = register<com.github.gradle.node.npm.task.NpmTask>("buildNpm") {args.set(listOf("run", "build")) // NPM任务参数,运行npm buildworkingDir.set(file("${project.projectDir}/editor-web")) // 设置工作目录}buildPlugin {// 构建插件前先执行NPM构建任务// 如果你的前端页面没有做修改,在反复进行插件开发打包测试,可以注释掉这个构建依赖,不然每次都重复构建没有修改的前端浪费时间dependsOn(buildNpm)}// 定义 Nexus 相关配置val nexusBaseUrl = providers.gradleProperty("nexusBaseUrl").getOrElse("https://nexus.shanhy.com")val nexusUsername = providers.gradleProperty("nexusUsername").getOrElse("")val nexusPassword = providers.gradleProperty("nexusPassword").getOrElse("")// 更新 plugins.xml 的任务val updatePluginsXmlToNexus = register("updatePluginsXmlToNexus") {group = "deployment"description = "更新 Nexus 上的 pluginsXml 文件版本信息"// 使用 Provider 延迟获取所有值val pluginId = providers.provider { intellijPlatform.pluginConfiguration.id.get() }val pluginVersion = providers.provider { project.version.toString() }val sinceBuild = providers.provider { intellijPlatform.pluginConfiguration.ideaVersion.sinceBuild.get() }val changelogFile = providers.provider { layout.projectDirectory.file("CHANGELOG.md").asFile }val projectName = providers.provider { project.name }outputs.upToDateWhen { false } // 总是执行更新doLast {// 在任务执行时获取所有需要的值val pluginIdValue = pluginId.get()val pluginVersionValue = pluginVersion.get()val sinceBuildValue = sinceBuild.get()val changelogFileValue = changelogFile.get()val projectNameValue = projectName.get()// 读取 CHANGELOG.md 内容val changelogContent = changelogFileValue.readText()val client = OkHttpClient.Builder().followRedirects(true).followSslRedirects(true).build()// 创建认证信息val credentials = Credentials.basic(nexusUsername, nexusPassword)// 下载现有的 plugins.xmlval tempFile = File.createTempFile("plugins", ".xml")try {client.newCall(Request.Builder().url("$nexusBaseUrl/repository/raw-public/idea-plugin/plugins.xml").build()).execute().use { response ->if (!response.isSuccessful) {val errorBody = response.body?.string() ?: "无错误详情"throw GradleException("下载文件失败: ${response.code} ${response.message}\n错误详情: $errorBody")}val contentType = response.header("Content-Type", "")if (contentType != null) {if (!contentType.contains("xml") && !contentType.contains("text/plain")) {println("警告: 响应Content-Type不是XML: $contentType")}}response.body?.byteStream()?.use { input ->tempFile.outputStream().use { output ->input.copyTo(output)}} ?: throw GradleException("下载文件失败: 响应体为空")}// 检查下载的文件if (!tempFile.exists() || tempFile.length() == 0L) {throw GradleException("下载文件失败,文件不存在或为空")}// 读取文件内容进行检查val content = tempFile.readText()// 验证下载的文件是否为有效的 XMLtry {DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(tempFile)} catch (e: Exception) {throw GradleException("下载的文件不是有效的 XML: ${e.message}\n文件内容: ${content}...")}// 读取并解析 XMLval document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(tempFile)val xpath = XPathFactory.newInstance().newXPath()// 查找对应的插件节点val pluginNode = xpath.evaluate("//plugin[@id='$pluginIdValue']", document, XPathConstants.NODE)as? Element?: throw GradleException("插件 '$pluginIdValue' 在 plugins.xml 中不存在,请先在 plugins.xml 中手动添加插件配置")// 更新 plugin 节点的属性pluginNode.setAttribute("url", "$nexusBaseUrl/repository/raw-hosted/idea-plugin/files/$projectNameValue-$pluginVersionValue.zip")pluginNode.setAttribute("version", pluginVersionValue)// 更新 idea-version 节点val ideaVersionNode = xpath.evaluate(".//idea-version", pluginNode, XPathConstants.NODE) as? ElementideaVersionNode?.setAttribute("since-build", sinceBuildValue)// 更新或创建 change-notes 节点val changeNotesNode = xpath.evaluate(".//change-notes", pluginNode, XPathConstants.NODE) as? Elementif (changeNotesNode != null) {while (changeNotesNode.hasChildNodes()) {changeNotesNode.removeChild(changeNotesNode.firstChild)}val cdataNode = document.createCDATASection(changelogContent)changeNotesNode.appendChild(cdataNode)}// 保存更新后的文件val transformer = TransformerFactory.newInstance().newTransformer()transformer.setOutputProperty(OutputKeys.INDENT, "yes")transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8")transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2")// 使用 StringWriter 先获取转换后的内容val writer = StringWriter()transformer.transform(DOMSource(document),StreamResult(writer))// 使用 dom4j 重新格式化 XML,去除xml中的多余空行和美化xml格式val xmlContent = writer.toString()val reader = SAXReader()val dom4jDocument = reader.read(StringReader(xmlContent))// 创建格式化配置val format = OutputFormat.createPrettyPrint().apply {encoding = "UTF-8"              // 设置输出编码isNewlines = true              // 每个节点换行isTrimText = true              // 是否去除元素中的文本前后空白isPadText = true               // 补齐空白以对齐标签isExpandEmptyElements = true   // <tag/> 变成 <tag></tag>isSuppressDeclaration = false  // 是否省略头部 <?xml ...?> 声明isOmitEncoding = false         // 是否省略 encoding 属性}// 将格式化后的 XML 写入字符串val stringWriter = StringWriter()val xmlWriter = org.dom4j.io.XMLWriter(stringWriter, format)xmlWriter.write(dom4jDocument)xmlWriter.close()// 将清理后的内容写入临时文件tempFile.writeText(stringWriter.toString())// 上传新文件println("正在上传新文件...")val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("raw.directory", "/idea-plugin").addFormDataPart("raw.asset1.filename", "plugins.xml").addFormDataPart("raw.asset1","plugins.xml","application/xml".toMediaTypeOrNull()?.let { mediaType ->tempFile.asRequestBody(mediaType)} ?: throw GradleException("无法创建请求体")).build()val uploadRequest = Request.Builder().url("$nexusBaseUrl/service/rest/v1/components?repository=raw-hosted").post(requestBody).header("Authorization", credentials).build()client.newCall(uploadRequest).execute().use { response ->if (!response.isSuccessful) {throw GradleException("上传文件失败: ${response.code} ${response.message}")}}} finally {tempFile.delete()}}}// 上传 zip 文件到 Nexus 的任务val uploadPluginToNexus = register("uploadPluginToNexus") {group = "deployment"description = "上传插件 zip 文件到 Nexus"// 声明任务的输入和输出,以支持配置缓存val zipFileName = "${project.name}-${project.version}.zip"val zipFile = layout.buildDirectory.file("distributions/$zipFileName").get().asFileinputs.file(zipFile)outputs.upToDateWhen { false } // 总是执行上传dependsOn(buildPlugin)doLast {if (!zipFile.exists()) {throw GradleException("插件 zip 文件未找到: ${zipFile.absolutePath}")}val client = okhttp3.OkHttpClient.Builder().followRedirects(true).followSslRedirects(true).build()// 创建认证信息val credentials = okhttp3.Credentials.basic(nexusUsername, nexusPassword)// 构建上传请求val requestBody = okhttp3.MultipartBody.Builder().setType(okhttp3.MultipartBody.FORM).addFormDataPart("raw.directory", "/idea-plugin/files").addFormDataPart("raw.asset1.filename", zipFileName).addFormDataPart("raw.asset1",zipFileName,"application/zip".toMediaTypeOrNull()?.let { mediaType ->zipFile.asRequestBody(mediaType)} ?: throw GradleException("无法创建请求体")).build()val uploadRequest = Request.Builder().url("$nexusBaseUrl/service/rest/v1/components?repository=raw-hosted").post(requestBody).header("Authorization", credentials).build()client.newCall(uploadRequest).execute().use { response ->if (!response.isSuccessful) {val errorBody = response.body?.string() ?: "无错误详情"throw GradleException("上传文件失败: ${response.code} ${response.message}\n错误详情: $errorBody")}println("插件文件上传成功: $zipFileName")}}}// 组合任务:部署到 Nexusregister("deployToNexus") {group = "deployment"description = "部署插件到 Nexus 仓库"dependsOn(uploadPluginToNexus, updatePluginsXmlToNexus)}
}sourceSets {main {resources {// 指定内容区的前端静态资源目录,这个决定了最终被作为资源打包到插件中srcDir("${project.projectDir}/editor-web/dist")}}
}

(END)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/82251.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot-11-基于注解和XML方式的SpringBoot应用场景对比

文章目录 1 基于注解的方式1.1 @Mapper1.2 @select1.3 @insert1.4 @update1.5 @delete2 基于XML的方式2.1 namespace2.2 resultMap2.3 select2.4 insert2.5 update2.6 delete3 service和controller3.1 service3.2 controller4 注解和xml的选择如果SQL简单且项目规模较小,推荐使…

C++复习核心精华

一、内存管理与智能指针 内存管理是C区别于其他高级语言的关键特性&#xff0c;掌握好它就掌握了C的灵魂。 1. 原始指针与内存泄漏 先来看看传统C的内存管理方式&#xff1a; void oldWay() {int* p new int(42); // 分配内存// 如果这里发生异常或提前return&#xff0c…

期货反向跟单软件—提高盘手杠杆的方式及剖析

在期货反向跟单领域&#xff0c;期货跟单软件对盘手杠杆的调节&#xff0c;是整个策略运作的核心环节之一。其背后蕴含着科学的金融逻辑。​ 期货跟单软件提高盘手杠杆主要通过两种方式。第一种是降低期货保证金。在盘手资金总量固定的情况下&#xff0c;保证金降低&#xff0…

【计算机网络】基于UDP进行socket编程——实现服务端与客户端业务

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;Linux &#x1f339;往期回顾&#x1f339;&#xff1a; 【Linux笔记】——网络基础 &#x1f516;流水不争&#xff0c;争的是滔滔不息 一、UDPsocket编程UDPsocket编…

ae卡通打架烟雾特效

1、创建一个合成&#xff08;合成1&#xff09;&#xff0c;右键创建形状图层&#xff0c;使用椭圆工具&#xff0c;长按shift键拖动鼠标左键画出圆形&#xff0c;同时按ctrlalthome三个键使圆形中心锚点对齐圆心&#xff0c;关闭描边&#xff0c;圆形图层填充白色。 2、选择形…

UE5 Va Res发送请求、处理请求、json使用

文章目录 介绍发送一个Get请求发送Post请求设置请求头请求体带添json发送请求完整的发送蓝图 处理收到的数据常用的json处理节点 介绍 UE5 自带的Http插件&#xff0c;插件内自带json解析功能 发送一个Get请求 只能写在事件图表里 发送Post请求 只能写在事件图表里 设置…

SQL 结构化模型设计与现代技术融合深度解读

摘要 本文系统展示了基于 JSON Schema 的 SQL 结构化模型设计&#xff0c;包括通用定义、四大基本操作&#xff08;SELECT、INSERT、UPDATE、DELETE&#xff09;的模型规范&#xff0c;以及面向现代场景的设计扩展。重点结合数据权限控制、乐观锁并发控制、表单自动化、自定义…

el-dialog 组件 多层嵌套 被遮罩问题

<el-dialog title"提示" :visible.sync"dialogBindUserVisible" width"30%" append-to-body :before-close"handleClose"> <span>这是一段信息</span> <span slot"footer" class"dialog-footer&q…

【KWDB 2025 创作者计划】_KWDB时序数据库特性及跨模查询

一、概述 数据库的类型多种多样&#xff0c;关系型数据库、时序型数据库、非关系型数据库、内存数据库、分布式数据库、图数据库等等&#xff0c;每种类型都有其特定的使用场景和优势&#xff0c;KaiwuDB 是一款面向 AIoT 场景的分布式、多模融合、支持原生 AI 的数据库…

学习心得(12-13)HTML 是什么 abort函数and自定义异常

一. abort函数 将后端的数据给到前端 二. 自定义异常 要结合abort函数使用 1.编写的时候都在abort的函数这个文件里面 错误信息在前端页面的展示&#xff1a; 如果想要在出现异常的时候返回一个页面&#xff1a; 1. 新建一个HTML文件 例如命名为404 2.将图库里的图片拖入…

理解全景图像拼接

1 3D到2D透视投影 三维空间上点 p 投影到二维空间 q 有两种方式&#xff1a;1&#xff09;正交投影&#xff0c;2&#xff09;透视投影。 正交投影直接舍去 z 轴信息&#xff0c;该模型仅在远心镜头上是合理的&#xff0c;或者对于物体深度远小于其到摄像机距离时的近似模型。…

Linux基本指令篇 —— whoami指令

whoami 是 Linux 和 Unix 系统中一个简单但实用的命令&#xff0c;全称 Who Am I&#xff08;我是谁&#xff09;。它的功能是显示当前登录用户的用户名。以下是关于 whoami 的详细解析&#xff1a; 目录 1. 基本用法 2. 命令特点 3. 实际应用场景 场景 1&#xff1a;脚本中…

华为OD机试真题——仿LISP运算(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 B卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

创建dummy

访客_dc1fc4 class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int left0;int right0;int n nums.size(); int sum0;int ans100001; for(right0;right<n;right) { sumnums[right]; //每次更新右端点之后&#xff0c;立即向右移动…

面向恶劣条件的道路交通目标检测----大创自用(当然你也可以在里面学到很多东西)

全部内容梳理 目标检测的两个任务&#xff1a; 预测标签 边界框 语义分割 实力分割 一个是类别 一个是实例级别 分类任务把每个图像当作一张图片看待 所有解决方法是先生成候选区域 再进行分类 置信度&#xff1a; 包括对类别和边界框预测的自信程度 输出分类和IOU分数的…

需求管理工具使用不当,如何优化?

要优化需求管理工具的使用&#xff0c;需从选择合适工具、规范使用流程、加强用户培训、统一数据结构、定期审查与优化使用配置五个方面着手。其中&#xff0c;选择合适工具是前提。错误的工具选择往往会导致项目沟通效率低、需求追踪失效甚至造成交付物偏离客户预期。因此&…

openwrt虚拟机安装调试

分类 lienol lean immortalwrt 一、获取固件 &#xff08;1&#xff09;下载地址 1.官网构建下载 OpenWrt Firmware Selector 官网 OpenWrt Firmware Selector 2.第三方构建网站 ImmortalWrt Firmware Selector ImmortalWrt Firmware Selector 3.第三方构建下载 ht…

Apache OFBiz 17.12.01 的远程命令执行漏洞 -Java 反序列化 + XML-RPC 请求机制

目录 漏洞原理 &#x1f3af; 一、漏洞背景&#xff08;CVE-2020-9496&#xff09; ⚙️ 二、攻击原理简述 &#x1f9f1; 三、完整攻击流程步骤详解 &#x1f50e; 1. 信息收集 &#x1f6e0;️ 2. 工具准备 &#x1f9ea; 3. 构造初始 payload&#xff1a;下载恶意脚本…

最好用的wordpress外贸主题

产品展示独立站wordpress主题 橙色的首页大banner外贸英文wordpress主题&#xff0c;适合用于产品展示型的外贸网站。 https://www.jianzhanpress.com/?p8556 Machine机器wordpress模板 宽屏简洁实用的wordpress外贸建站模板&#xff0c;适合工业机器生产、加工、制造的外贸…

Q1:Go协程、Channel通道 被close后,读会带来什么问题?

在 Go 语言中&#xff0c;Channel&#xff08;通道&#xff09;关闭后读取的行为是一个常见但需要谨慎处理的问题。以下是详细的分析和注意事项&#xff1a; 1. 关闭 Channel 后读取的行为 (1) 读取已关闭的 Channel 剩余数据仍可读取&#xff1a; 关闭 Channel 后&#xff0…