This is an automated email from the ASF dual-hosted git repository.
Startrekzky pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/devlake.git
The following commit(s) were added to refs/heads/main by this push:
new 869256888 chore(python): deprecate Python plugin framework (PyDevLake)
(#9097)
869256888 is described below
commit 869256888618c315fb03f1a267add37bdd662efd
Author: Klesh Wong <[email protected]>
AuthorDate: Fri Sep 4 17:44:15 2026 +0800
chore(python): deprecate Python plugin framework (PyDevLake) (#9097)
Phase 1 of the deprecation plan in #9092. Adds deprecation warnings across
the codebase to inform users that the Python plugin framework will be
removed in 3 months in favor of Go-based plugins.
Changes:
- backend/python/README.md: add DEPRECATED banner + migration guidance
- backend/python/plugins/azuredevops/README.md: mark plugin deprecated,
point users to azuredevops_go
- backend/core/runner/loader.go: emit a WARN log line at server startup
when remote (Python) plugins are being loaded
- backend/core/plugin/plugin_meta.go: add PluginDeprecation interface
- backend/server/services/remote/plugin/plugin_impl.go: implement
PluginDeprecation on RemotePlugin so all Python plugins are flagged
- backend/server/api/plugininfo/plugininifo.go: expose field
on GET /plugins so config-ui can detect deprecated plugins
- config-ui: add isDeprecated/deprecationMessage to IPluginConfig, mark
the azuredevops (Python) plugin deprecated, and render a 'Deprecated'
badge + message in the Connections page
---
backend/core/plugin/plugin_meta.go | 8 ++++++++
backend/core/runner/loader.go | 1 +
backend/python/README.md | 22 ++++++++++++++++++++++
backend/python/plugins/azuredevops/README.md | 15 ++++++++++++++-
backend/server/api/plugininfo/plugininifo.go | 9 +++++++--
.../server/services/remote/plugin/plugin_impl.go | 12 ++++++++++++
config-ui/src/plugins/register/azure/config.tsx | 3 +++
config-ui/src/routes/connection/connections.tsx | 8 ++++++++
config-ui/src/routes/connection/styled.ts | 20 ++++++++++++++++++++
config-ui/src/types/plugin.ts | 2 ++
10 files changed, 97 insertions(+), 3 deletions(-)
diff --git a/backend/core/plugin/plugin_meta.go
b/backend/core/plugin/plugin_meta.go
index 4047f7e54..1807f3833 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 f05a78632..9c6a32af9 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 17bf0cee7..5c2f12885 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 ed5366bfa..172bc64f5 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 de32fbc94..285b6fd2c 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 1cf768305..fe8e42970 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 978a6af09..3818226a2 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 }) => <Icon fill={color} />,
sort: 2,
+ isDeprecated: true,
+ deprecationMessage:
+ 'This Python-based Azure DevOps plugin is DEPRECATED and will be removed
in 3 months. Please migrate to the Go-based "Azure DevOps Go" plugin. See
https://github.com/apache/devlake/issues/9092 for the deprecation plan and
migration guide.',
connection: {
docLink: DOC_URL.PLUGIN.AZUREDEVOPS.BASIS,
initialValues: {},
diff --git a/config-ui/src/routes/connection/connections.tsx
b/config-ui/src/routes/connection/connections.tsx
index ce6a3e0d9..b660b070d 100644
--- a/config-ui/src/routes/connection/connections.tsx
+++ b/config-ui/src/routes/connection/connections.tsx
@@ -95,8 +95,12 @@ export const Connections = () => {
return (
<li key={plugin} onClick={() => handleShowListDialog(plugin)}>
{pluginConfig.isBeta && <span className="beta">Beta</span>}
+ {pluginConfig.isDeprecated && <span
className="deprecated">Deprecated</span>}
<span className="logo">{pluginConfig.icon({ color: colorPrimary
})}</span>
<span className="name">{pluginConfig.name}</span>
+ {pluginConfig.isDeprecated && pluginConfig.deprecationMessage &&
(
+ <span
className="deprecation-note">{pluginConfig.deprecationMessage}</span>
+ )}
<span className="count">
{connectionCount ? (
<Badge color={colorPrimary} text={`${connectionCount}
connections`} />
@@ -116,8 +120,12 @@ export const Connections = () => {
return (
<li key={plugin} onClick={() => handleShowListDialog(plugin)}>
{pluginConfig.isBeta && <span className="beta">Beta</span>}
+ {pluginConfig.isDeprecated && <span
className="deprecated">Deprecated</span>}
<span className="logo">{pluginConfig.icon({ color: colorPrimary
})}</span>
<span className="name">{pluginConfig.name}</span>
+ {pluginConfig.isDeprecated && pluginConfig.deprecationMessage &&
(
+ <span
className="deprecation-note">{pluginConfig.deprecationMessage}</span>
+ )}
<span className="count">
{connectionCount ? (
<Badge color={colorPrimary} text={`${connectionCount}
connections`} />
diff --git a/config-ui/src/routes/connection/styled.ts
b/config-ui/src/routes/connection/styled.ts
index 2bc88b8ca..5c53a1c4f 100644
--- a/config-ui/src/routes/connection/styled.ts
+++ b/config-ui/src/routes/connection/styled.ts
@@ -82,6 +82,26 @@ export const Wrapper = styled.div`
border-radius: 8px;
}
+ & > .deprecated {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 4px 8px;
+ font-size: 12px;
+ color: ${({ theme }) => theme.colors.textInverse};
+ background-color: ${({ theme }) => theme.colors.error};
+ border-radius: 8px;
+ }
+
+ & > .deprecation-note {
+ margin-top: 4px;
+ padding: 0 8px;
+ font-size: 11px;
+ line-height: 1.4;
+ color: ${({ theme }) => theme.colors.error};
+ text-align: center;
+ }
+
& > .logo {
width: 60px;
height: 60px;
diff --git a/config-ui/src/types/plugin.ts b/config-ui/src/types/plugin.ts
index 3639fc960..4748276ad 100644
--- a/config-ui/src/types/plugin.ts
+++ b/config-ui/src/types/plugin.ts
@@ -22,6 +22,8 @@ export interface IPluginConfig {
icon: ({ color }: { color: string }) => React.ReactNode;
sort: number;
isBeta?: boolean;
+ isDeprecated?: boolean;
+ deprecationMessage?: string;
connection: {
docLink: string;
initialValues?: Record<string, any>;