diff --git a/backend/core/plugin/plugin_meta.go b/backend/core/plugin/plugin_meta.go
index 4047f7e54d3..1807f3833b3 100644
--- a/backend/core/plugin/plugin_meta.go
+++ b/backend/core/plugin/plugin_meta.go
@@ -44,6 +44,14 @@ type PluginIcon interface {
SvgIcon() string
}
+// PluginDeprecation is implemented by plugins that have been deprecated.
+// The DeprecationMessage should describe why the plugin is deprecated and
+// what users should migrate to. Returns an empty string for non-deprecated
+// plugins.
+type PluginDeprecation interface {
+ DeprecationMessage() string
+}
+
// PluginSource abstracts data sources
type PluginSource interface {
Connection() dal.Tabler
diff --git a/backend/core/runner/loader.go b/backend/core/runner/loader.go
index f05a78632dc..9c6a32af9c5 100644
--- a/backend/core/runner/loader.go
+++ b/backend/core/runner/loader.go
@@ -91,6 +91,7 @@ func LoadGoPlugins(basicRes context.BasicRes) errors.Error {
func LoadRemotePlugins(basicRes context.BasicRes) errors.Error {
remotePluginDir := basicRes.GetConfig("REMOTE_PLUGIN_DIR")
if remotePluginDir != "" {
+ basicRes.GetLogger().Warn(nil, "DEPRECATION WARNING: The Python plugin framework (PyDevLake) is deprecated and will be removed in 3 months. See https://github.com/apache/devlake/issues/9092 for the deprecation plan and migration guide. Please migrate Python plugins (e.g. azuredevops) to the Go-based equivalents (e.g. azuredevops_go).")
basicRes.GetLogger().Info("Loading remote plugins")
remote.Init(basicRes)
walkErr := filepath.WalkDir(remotePluginDir, func(path string, d fs.DirEntry, err error) error {
diff --git a/backend/python/README.md b/backend/python/README.md
index 17bf0cee7b3..5c2f1288556 100644
--- a/backend/python/README.md
+++ b/backend/python/README.md
@@ -16,6 +16,28 @@ limitations under the License.
-->
# Pydevlake
+> **⚠️ DEPRECATED — Scheduled for removal.**
+> The Python plugin framework (PyDevLake) is deprecated and will be removed from
+> Apache DevLake in 3 months per the deprecation plan in
+> [#9092](https://github.com/apache/devlake/issues/9092).
+>
+> Reasons:
+> - Only one plugin (`azuredevops`) ever shipped on this framework; the `dbt`
+> plugin was already removed (#8970).
+> - No feature/fix commits to Python plugins since Dec 2025; only dependency
+> refreshes and runtime bumps land.
+> - A fully-featured Go implementation, `azuredevops_go`, now supersedes the
+> Python `azuredevops` plugin (including on-premises support, #9014).
+>
+> **What you should do:**
+> - New plugins: write them in **Go**. See `backend/plugins/` for 40+ reference
+> implementations.
+> - Existing `azuredevops` (Python) users: migrate to `azuredevops_go` (a
+> migration guide will be published during the deprecation window).
+>
+> No new Python plugins will be accepted. Only critical security fixes will land
+> during the deprecation window.
+
Pydevlake is a framework for writing plugins for [DevLake](https://devlake.apache.org/) in Python. The framework source code
can be found in [here](./pydevlake).
diff --git a/backend/python/plugins/azuredevops/README.md b/backend/python/plugins/azuredevops/README.md
index ed5366bfa1c..172bc64f574 100644
--- a/backend/python/plugins/azuredevops/README.md
+++ b/backend/python/plugins/azuredevops/README.md
@@ -14,4 +14,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
-# Azure Devops Python Plugin
\ No newline at end of file
+# Azure Devops Python Plugin
+
+> **⚠️ DEPRECATED — Scheduled for removal in 3 months.**
+> This Python `azuredevops` plugin is deprecated in favor of the Go-based
+> [`azuredevops_go`](../../plugins/azuredevops_go) plugin, which has broader
+> feature coverage (including Azure DevOps Server / on-premises support, #9014).
+>
+> Please migrate existing connections and scopes to `azuredevops_go`. A migration
+> guide will be published during the deprecation window — see
+> [#9092](https://github.com/apache/devlake/issues/9092) for the full plan and
+> timeline.
+>
+> No new features or fixes (other than critical security fixes) will be accepted
+> on this plugin.
\ No newline at end of file
diff --git a/backend/server/api/plugininfo/plugininifo.go b/backend/server/api/plugininfo/plugininifo.go
index de32fbc9476..285b6fd2ca2 100644
--- a/backend/server/api/plugininfo/plugininifo.go
+++ b/backend/server/api/plugininfo/plugininifo.go
@@ -75,8 +75,9 @@ type PluginMetric struct {
}
type PluginMeta struct {
- Plugin string `json:"plugin"`
- Metric PluginMetric `json:"metric"`
+ Plugin string `json:"plugin"`
+ Metric PluginMetric `json:"metric"`
+ Deprecated bool `json:"deprecated"`
}
type PluginMetas []PluginMeta
@@ -207,6 +208,10 @@ func GetPluginMetas(c *gin.Context) {
Plugin: name,
}
+ if dp, ok := p.(plugin.PluginDeprecation); ok {
+ pluginMeta.Deprecated = dp.DeprecationMessage() != ""
+ }
+
// if this plugin has the plugin task info
if pt, ok := p.(plugin.PluginMetric); ok {
var err1 errors.Error
diff --git a/backend/server/services/remote/plugin/plugin_impl.go b/backend/server/services/remote/plugin/plugin_impl.go
index 1cf768305d3..fe8e429709a 100644
--- a/backend/server/services/remote/plugin/plugin_impl.go
+++ b/backend/server/services/remote/plugin/plugin_impl.go
@@ -238,4 +238,16 @@ func (p *remotePluginImpl) MigrationScripts() []plugin.MigrationScript {
return append(p.migrationScripts, migrationscripts.All(p.name)...)
}
+// DeprecationMessage implements plugin.PluginDeprecation. All remote (Python)
+// plugins are deprecated and scheduled for removal in 3 months per the
+// deprecation plan in issue #9092.
+func (p *remotePluginImpl) DeprecationMessage() string {
+ return fmt.Sprintf(
+ "This Python plugin (%s) is DEPRECATED and will be removed in 3 months. "+
+ "Please migrate to the Go-based equivalent. "+
+ "See https://github.com/apache/devlake/issues/9092 for the deprecation plan and migration guide.",
+ p.name,
+ )
+}
+
var _ models.RemotePlugin = (*remotePluginImpl)(nil)
diff --git a/config-ui/src/plugins/register/azure/config.tsx b/config-ui/src/plugins/register/azure/config.tsx
index 978a6af0934..3818226a2df 100644
--- a/config-ui/src/plugins/register/azure/config.tsx
+++ b/config-ui/src/plugins/register/azure/config.tsx
@@ -28,6 +28,9 @@ export const AzureConfig: IPluginConfig = {
name: 'Azure DevOps',
icon: ({ color }) =>