Skip to content

Commit 282b891

Browse files
committed
release: 7.0.4
1 parent d1eaca0 commit 282b891

12 files changed

Lines changed: 1696 additions & 2 deletions

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
## Unreleased
44

5+
## 7.0.4 - 2026-04-23
6+
7+
### New Features
8+
- Added a `Publish to pub.dev` action in the `pubspec.yaml` notification bar with a release-notes popup, optional publish-date insertion, background publishing, and an output viewer.
9+
- Added batch publishing for child packages so multi-package directories can update versions, preview changelog changes, and publish in dependency-aware order from the IDE.
10+
11+
### Improvements
12+
- Automatically hides publish actions for packages that declare `publish_to: none`, and reports skipped packages in the batch publishing flow.
13+
14+
### Localization
15+
- Added zh/en/hk/ja/ko translations for the pub.dev publish workflow and the new batch child-package publishing UI.
16+
517
## 7.0.3 - 2026-04-18
618

719
### New Features

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
kotlin.stdlib.default.dependency=false
2-
pluginVersion=7.0.3
2+
pluginVersion=7.0.4
33
dartVersion=503.0.0
44
sinceBuildVersion=252
55
kotlin.daemon.jvmargs=-Xmx5024m
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package shop.itbug.flutterx.actions
2+
3+
import com.intellij.icons.AllIcons
4+
import com.intellij.notification.NotificationGroupManager
5+
import com.intellij.notification.NotificationType
6+
import com.intellij.openapi.actionSystem.ActionUpdateThread
7+
import com.intellij.openapi.actionSystem.AnActionEvent
8+
import com.intellij.openapi.actionSystem.CommonDataKeys
9+
import com.intellij.openapi.application.ApplicationManager
10+
import com.intellij.openapi.fileEditor.FileDocumentManager
11+
import com.intellij.openapi.progress.ProcessCanceledException
12+
import com.intellij.openapi.progress.ProgressIndicator
13+
import com.intellij.openapi.progress.Task
14+
import com.intellij.execution.configurations.GeneralCommandLine
15+
import com.intellij.execution.process.OSProcessHandler
16+
import com.intellij.execution.process.ProcessHandlerFactory
17+
import com.intellij.execution.process.ProcessListener
18+
import com.intellij.execution.process.ProcessTerminatedListener
19+
import shop.itbug.flutterx.common.MyAction
20+
import shop.itbug.flutterx.dialog.BatchPublishChildPackagesDialog
21+
import shop.itbug.flutterx.dialog.BatchPublishPackageRequest
22+
import shop.itbug.flutterx.dialog.CommandOutputDialog
23+
import shop.itbug.flutterx.i18n.PluginBundle
24+
import shop.itbug.flutterx.icons.MyIcons
25+
import shop.itbug.flutterx.util.PubPackagePublishUtil
26+
import shop.itbug.flutterx.util.toastWithError
27+
28+
class BatchPublishChildPackagesAction : MyAction() {
29+
30+
override fun actionPerformed(e: AnActionEvent) {
31+
val project = e.project ?: return
32+
val rootDirectory = e.getData(CommonDataKeys.VIRTUAL_FILE) ?: return
33+
val allPackages = PubPackagePublishUtil.collectChildPackages(project, rootDirectory)
34+
val publishablePackages = PubPackagePublishUtil.sortForPublish(allPackages.filter { it.publishable })
35+
if (publishablePackages.isEmpty()) {
36+
project.toastWithError(PluginBundle.get("batch_publish_child_packages_no_publishable"))
37+
return
38+
}
39+
40+
val dialog = BatchPublishChildPackagesDialog(
41+
currentProject = project,
42+
rootDirectory = rootDirectory,
43+
packages = publishablePackages,
44+
skippedPackages = allPackages.count { !it.publishable }
45+
)
46+
if (!dialog.showAndGet()) {
47+
return
48+
}
49+
val request = dialog.getPublishRequest() ?: return
50+
51+
ApplicationManager.getApplication().runWriteAction {
52+
FileDocumentManager.getInstance().saveAllDocuments()
53+
}
54+
startBatchPublish(project, request.packages, request.includePublishDate)
55+
}
56+
57+
override fun update(e: AnActionEvent) {
58+
val rootDirectory = e.getData(CommonDataKeys.VIRTUAL_FILE)
59+
e.presentation.isEnabledAndVisible = e.project != null &&
60+
rootDirectory != null &&
61+
rootDirectory.isDirectory &&
62+
PubPackagePublishUtil.hasMultipleChildPackages(rootDirectory)
63+
e.presentation.text = PluginBundle.get("batch_publish_child_packages_action")
64+
e.presentation.icon = AllIcons.Actions.Upload
65+
}
66+
67+
override fun getActionUpdateThread(): ActionUpdateThread {
68+
return ActionUpdateThread.BGT
69+
}
70+
71+
private fun startBatchPublish(
72+
project: com.intellij.openapi.project.Project,
73+
packages: List<BatchPublishPackageRequest>,
74+
includePublishDate: Boolean
75+
) {
76+
object : Task.Backgroundable(
77+
project,
78+
PluginBundle.get("batch_publish_child_packages_task_title"),
79+
true
80+
) {
81+
private val output = StringBuilder()
82+
private val failures = mutableListOf<String>()
83+
private var successCount = 0
84+
private var cancelled = false
85+
private var currentProcessHandler: OSProcessHandler? = null
86+
87+
override fun run(indicator: ProgressIndicator) {
88+
indicator.isIndeterminate = false
89+
90+
packages.forEachIndexed { index, request ->
91+
indicator.checkCanceled()
92+
indicator.fraction = index.toDouble() / packages.size.coerceAtLeast(1)
93+
indicator.text = PluginBundle.get("batch_publish_child_packages_task_running")
94+
indicator.text2 = "${index + 1}/${packages.size} · ${request.packageInfo.name}"
95+
appendPackageOutputHeader(request)
96+
97+
try {
98+
PubPackagePublishUtil.updatePubspecVersion(
99+
request.packageInfo.workDirectory,
100+
request.packageInfo.name,
101+
request.version
102+
)
103+
PubPackagePublishUtil.updateChangelogForPublish(
104+
request.packageInfo.workDirectory,
105+
request.version,
106+
request.changelog,
107+
includePublishDate
108+
)
109+
val exitCode = runPublishCommand(indicator, request)
110+
if (exitCode == 0) {
111+
successCount += 1
112+
} else {
113+
failures += "${request.packageInfo.name} (exit code: $exitCode)"
114+
}
115+
} catch (_: ProcessCanceledException) {
116+
cancelled = true
117+
currentProcessHandler?.destroyProcess()
118+
throw ProcessCanceledException()
119+
} catch (error: Exception) {
120+
failures += "${request.packageInfo.name}: ${error.message ?: "unknown error"}"
121+
synchronized(output) {
122+
output.append(error.stackTraceToString()).appendLine()
123+
}
124+
}
125+
}
126+
127+
indicator.fraction = 1.0
128+
}
129+
130+
override fun onSuccess() {
131+
val notification = NotificationGroupManager.getInstance()
132+
.getNotificationGroup("dio_socket_notify")
133+
.createNotification(
134+
if (failures.isEmpty()) {
135+
PluginBundle.get("batch_publish_child_packages_success", successCount.toString())
136+
} else {
137+
PluginBundle.get("batch_publish_child_packages_failed", failures.size.toString())
138+
},
139+
if (failures.isEmpty()) NotificationType.INFORMATION else NotificationType.ERROR
140+
)
141+
notification.icon = MyIcons.flutter
142+
notification.addAction(object : com.intellij.openapi.project.DumbAwareAction(
143+
PluginBundle.get("pubspec_notification_view_output")
144+
) {
145+
override fun actionPerformed(e: AnActionEvent) {
146+
val outputText = synchronized(output) {
147+
output.toString().ifBlank { PluginBundle.get("pubspec_notification_no_output") }
148+
}
149+
CommandOutputDialog(
150+
project,
151+
PluginBundle.get("batch_publish_child_packages_output_title"),
152+
outputText
153+
).show()
154+
notification.hideBalloon()
155+
notification.expire()
156+
}
157+
158+
override fun getActionUpdateThread(): ActionUpdateThread {
159+
return ActionUpdateThread.BGT
160+
}
161+
})
162+
notification.notify(project)
163+
}
164+
165+
override fun onCancel() {
166+
if (cancelled) {
167+
NotificationGroupManager.getInstance()
168+
.getNotificationGroup("dio_socket_notify")
169+
.createNotification(
170+
PluginBundle.get("batch_publish_child_packages_cancelled"),
171+
NotificationType.WARNING
172+
)
173+
.notify(project)
174+
}
175+
}
176+
177+
private fun runPublishCommand(
178+
indicator: ProgressIndicator,
179+
request: BatchPublishPackageRequest
180+
): Int {
181+
val commandLine =
182+
GeneralCommandLine("dart", "pub", "publish", "--force").withWorkDirectory(request.packageInfo.workDirectory)
183+
val handler = ProcessHandlerFactory.getInstance().createColoredProcessHandler(commandLine)
184+
currentProcessHandler = handler
185+
ProcessTerminatedListener.attach(handler)
186+
handler.addProcessListener(object : ProcessListener {
187+
override fun startNotified(event: com.intellij.execution.process.ProcessEvent) = Unit
188+
189+
override fun processTerminated(event: com.intellij.execution.process.ProcessEvent) = Unit
190+
191+
override fun processWillTerminate(
192+
event: com.intellij.execution.process.ProcessEvent,
193+
willBeDestroyed: Boolean
194+
) = Unit
195+
196+
override fun onTextAvailable(
197+
event: com.intellij.execution.process.ProcessEvent,
198+
outputType: com.intellij.openapi.util.Key<*>
199+
) {
200+
synchronized(output) {
201+
output.append(event.text)
202+
}
203+
}
204+
})
205+
handler.startNotify()
206+
207+
while (!handler.waitFor(500)) {
208+
indicator.checkCanceled()
209+
}
210+
return handler.exitCode ?: -1
211+
}
212+
213+
private fun appendPackageOutputHeader(request: BatchPublishPackageRequest) {
214+
synchronized(output) {
215+
output.appendLine("===== ${request.packageInfo.name} =====")
216+
output.appendLine("version: ${request.version}")
217+
output.appendLine("directory: ${request.packageInfo.workDirectory.absolutePath}")
218+
output.appendLine("command: dart pub publish --force")
219+
output.appendLine()
220+
}
221+
}
222+
}.queue()
223+
}
224+
}

0 commit comments

Comments
 (0)