This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch chore/deprecate-python-plugins
in repository https://gitbox.apache.org/repos/asf/devlake.git

commit 51502cca4e9e52c79c5d4a6237e5ff6661047141
Author: Klesh Wong <[email protected]>
AuthorDate: Wed Sep 2 19:44:46 2026 +0800

    chore(python): deprecate Python plugin framework (PyDevLake)
    
    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>;

Reply via email to