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

klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new cc39ef25a refactor: add subtask register for github plugin (#5411)
cc39ef25a is described below

commit cc39ef25a5931be9479e742c12299a9c30f5e400
Author: chenggui53 <[email protected]>
AuthorDate: Thu Jul 20 15:29:02 2023 +0800

    refactor: add subtask register for github plugin (#5411)
    
    * refactor: add subtask registert for github
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask meta register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: Update remote.go
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: update subtask register
    
    * refactor: update subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
    
    * refactor: add subtask register
---
 backend/core/plugin/plugin_task.go                 |  3 +
 .../pluginhelper/api/api_collector_with_state.go   |  2 +-
 backend/helpers/pluginhelper/api/batch_save.go     |  2 +-
 .../helpers/pluginhelper/api/connection_auths.go   |  3 +-
 .../sorter}/interface.go                           |  9 ++-
 .../sorter/subtask.go}                             | 76 ++++++--------------
 .../sorter/subtask_test.go}                        | 11 +--
 .../pluginhelper/subtaskmeta/sorter/table.go       | 80 +++++++++++++++++++++
 .../pluginhelper/subtaskmeta/sorter/table_test.go  | 81 ++++++++++++++++++++++
 .../pluginhelper/subtaskmeta/sorter/utils.go       | 72 +++++++++++++++++++
 .../pluginhelper/subtaskmeta/sorter/utils_test.go  | 73 +++++++++++++++++++
 backend/plugins/github/impl/impl.go                | 63 ++++-------------
 .../github/impl/impl_test.go}                      | 24 +++++--
 backend/plugins/github/tasks/account_collector.go  | 24 ++++---
 backend/plugins/github/tasks/account_convertor.go  | 14 +++-
 backend/plugins/github/tasks/account_extractor.go  |  9 ++-
 .../plugins/github/tasks/account_org_collector.go  | 32 ++++++---
 .../plugins/github/tasks/account_org_extractor.go  |  9 ++-
 backend/plugins/github/tasks/api_client.go         |  7 --
 backend/plugins/github/tasks/cicd_job_collector.go |  6 ++
 backend/plugins/github/tasks/cicd_job_convertor.go | 16 ++++-
 backend/plugins/github/tasks/cicd_job_extractor.go |  6 ++
 backend/plugins/github/tasks/cicd_run_collector.go |  6 ++
 backend/plugins/github/tasks/cicd_run_convertor.go | 13 ++++
 backend/plugins/github/tasks/cicd_run_extractor.go |  6 ++
 backend/plugins/github/tasks/comment_collector.go  | 22 +++---
 backend/plugins/github/tasks/comment_extractor.go  | 12 +++-
 backend/plugins/github/tasks/commit_collector.go   |  6 ++
 backend/plugins/github/tasks/commit_convertor.go   | 17 ++++-
 backend/plugins/github/tasks/commit_extractor.go   |  9 +++
 .../plugins/github/tasks/commit_stats_collector.go |  9 +++
 .../plugins/github/tasks/commit_stats_extractor.go |  9 +++
 backend/plugins/github/tasks/event_collector.go    |  6 ++
 backend/plugins/github/tasks/event_extractor.go    |  7 ++
 .../github/tasks/issue_assignee_convertor.go       | 11 +++
 backend/plugins/github/tasks/issue_collector.go    |  6 ++
 .../github/tasks/issue_comment_convertor.go        | 10 +++
 backend/plugins/github/tasks/issue_convertor.go    | 19 ++++-
 backend/plugins/github/tasks/issue_extractor.go    | 10 +++
 .../plugins/github/tasks/issue_label_convertor.go  | 12 +++-
 .../plugins/github/tasks/milestone_collector.go    |  6 ++
 .../plugins/github/tasks/milestone_converter.go    | 16 ++++-
 .../plugins/github/tasks/milestone_extractor.go    |  7 ++
 backend/plugins/github/tasks/pr_collector.go       |  6 ++
 .../plugins/github/tasks/pr_comment_convertor.go   | 14 +++-
 .../plugins/github/tasks/pr_commit_collector.go    |  6 ++
 .../plugins/github/tasks/pr_commit_convertor.go    | 10 +++
 .../plugins/github/tasks/pr_commit_extractor.go    |  9 +++
 backend/plugins/github/tasks/pr_convertor.go       | 10 +++
 backend/plugins/github/tasks/pr_extractor.go       |  9 +++
 backend/plugins/github/tasks/pr_issue_convertor.go | 14 +++-
 backend/plugins/github/tasks/pr_issue_enricher.go  | 16 ++++-
 backend/plugins/github/tasks/pr_label_convertor.go | 12 +++-
 .../plugins/github/tasks/pr_review_collector.go    |  6 ++
 .../github/tasks/pr_review_comment_collector.go    | 22 +++---
 .../github/tasks/pr_review_comment_extractor.go    | 10 ++-
 .../plugins/github/tasks/pr_review_convertor.go    | 13 +++-
 .../plugins/github/tasks/pr_review_extractor.go    | 12 +++-
 .../github/tasks/register.go}                      |  8 ++-
 backend/plugins/github/tasks/repo_convertor.go     | 26 +++++--
 backend/plugins/github/tasks/shared.go             |  7 ++
 backend/plugins/github/tasks/task_data.go          |  1 -
 backend/plugins/gitlab/impl/impl.go                |  6 +-
 63 files changed, 857 insertions(+), 201 deletions(-)

diff --git a/backend/core/plugin/plugin_task.go 
b/backend/core/plugin/plugin_task.go
index 9baf274e3..d7dbc14bc 100644
--- a/backend/core/plugin/plugin_task.go
+++ b/backend/core/plugin/plugin_task.go
@@ -19,6 +19,7 @@ package plugin
 
 import (
        "context"
+
        corecontext "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/errors"
 )
@@ -98,6 +99,8 @@ type SubTaskMeta struct {
        Description      string
        DomainTypes      []string
        Dependencies     []*SubTaskMeta
+       DependencyTables []string
+       ProductTables    []string
 }
 
 // PluginTask Implement this interface to let framework run tasks for you
diff --git a/backend/helpers/pluginhelper/api/api_collector_with_state.go 
b/backend/helpers/pluginhelper/api/api_collector_with_state.go
index 65d4e2176..b3282459b 100644
--- a/backend/helpers/pluginhelper/api/api_collector_with_state.go
+++ b/backend/helpers/pluginhelper/api/api_collector_with_state.go
@@ -41,7 +41,7 @@ type ApiCollectorStateManager struct {
        ExecuteStart time.Time
 }
 
-// NewApiCollectorWithState create a new ApiCollectorStateManager
+// NewStatefulApiCollector create a new ApiCollectorStateManager
 func NewStatefulApiCollector(args RawDataSubTaskArgs, timeAfter *time.Time) 
(*ApiCollectorStateManager, errors.Error) {
        db := args.Ctx.GetDal()
 
diff --git a/backend/helpers/pluginhelper/api/batch_save.go 
b/backend/helpers/pluginhelper/api/batch_save.go
index bf4cf06bb..f89b93106 100644
--- a/backend/helpers/pluginhelper/api/batch_save.go
+++ b/backend/helpers/pluginhelper/api/batch_save.go
@@ -29,7 +29,7 @@ import (
        "github.com/apache/incubator-devlake/core/log"
 )
 
-// BatchSave performs mulitple records persistence of a specific type in one 
sql query to improve the performance
+// BatchSave performs multiple records persistence of a specific type in one 
sql query to improve the performance
 type BatchSave struct {
        basicRes context.BasicRes
        log      log.Logger
diff --git a/backend/helpers/pluginhelper/api/connection_auths.go 
b/backend/helpers/pluginhelper/api/connection_auths.go
index acb4da04d..6219b9a5d 100644
--- a/backend/helpers/pluginhelper/api/connection_auths.go
+++ b/backend/helpers/pluginhelper/api/connection_auths.go
@@ -24,9 +24,8 @@ import (
        "reflect"
        "strings"
 
-       "github.com/apache/incubator-devlake/core/plugin"
-
        "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
        "github.com/go-playground/validator/v10"
 )
 
diff --git a/backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/interface.go
similarity index 82%
copy from backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go
copy to backend/helpers/pluginhelper/subtaskmeta/sorter/interface.go
index fb4e30b5e..61a51f5b7 100644
--- a/backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/interface.go
@@ -15,10 +15,13 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package subtaskmeta_sorter
+package sorter
 
-import "github.com/apache/incubator-devlake/core/plugin"
+import (
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
+)
 
 type SubTaskMetaSorter interface {
-       Sort() ([]plugin.SubTaskMeta, error)
+       Sort() ([]plugin.SubTaskMeta, errors.Error)
 }
diff --git a/backend/helpers/pluginhelper/subtaskmeta_sorter/dependency.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/subtask.go
similarity index 53%
rename from backend/helpers/pluginhelper/subtaskmeta_sorter/dependency.go
rename to backend/helpers/pluginhelper/subtaskmeta/sorter/subtask.go
index d310c1fe5..a3873b8d4 100644
--- a/backend/helpers/pluginhelper/subtaskmeta_sorter/dependency.go
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/subtask.go
@@ -15,12 +15,14 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package subtaskmeta_sorter
+package sorter
 
 import (
        "fmt"
+
+       "github.com/apache/incubator-devlake/core/errors"
+
        "github.com/apache/incubator-devlake/core/plugin"
-       "sort"
 )
 
 type DependencySorter struct {
@@ -31,12 +33,12 @@ func NewDependencySorter(metas []*plugin.SubTaskMeta) 
SubTaskMetaSorter {
        return &DependencySorter{metas: metas}
 }
 
-func (d *DependencySorter) Sort() ([]plugin.SubTaskMeta, error) {
-       return topologicalSort(d.metas)
+func (d *DependencySorter) Sort() ([]plugin.SubTaskMeta, errors.Error) {
+       return dependenciesTopologicalSort(d.metas)
 }
 
 // stable topological sort
-func topologicalSort(metas []*plugin.SubTaskMeta) ([]plugin.SubTaskMeta, 
error) {
+func dependenciesTopologicalSort(metas []*plugin.SubTaskMeta) 
([]plugin.SubTaskMeta, errors.Error) {
        // which state will make a cycle
        dependenciesMap := make(map[string][]string)
        nameMetaMap := make(map[string]*plugin.SubTaskMeta)
@@ -56,63 +58,25 @@ func topologicalSort(metas []*plugin.SubTaskMeta) 
([]plugin.SubTaskMeta, error)
                                dependenciesMap[item.Name] = make([]string, 0)
                        }
                } else {
-                       return nil, fmt.Errorf("duplicate subtaskmetas detected 
in list: %s", item.Name)
+                       return nil, errors.Convert(fmt.Errorf("duplicate 
subtaskmetas detected in list: %s", item.Name))
                }
        }
 
-       orderedSubtaskList := make([]plugin.SubTaskMeta, 0)
-       for {
-               if len(dependenciesMap) == 0 {
-                       break
-               }
-
-               tmpList := make([]string, 0)
-               for key, item := range dependenciesMap {
-                       if len(item) == 0 {
-                               tmpList = append(tmpList, key)
-                       }
-               }
-               if len(tmpList) == 0 {
-                       return nil, fmt.Errorf("cyclic dependency detected: 
%v", dependenciesMap)
-               }
-
-               // remove item in dependencies map
-               for key, value := range dependenciesMap {
-                       if contains(tmpList, key) {
-                               delete(dependenciesMap, key)
-                       } else {
-                               dependenciesMap[key] = removeElements(value, 
tmpList)
-                       }
-               }
-
-               sort.Strings(tmpList)
-               // convert item to subtaskmeta by name, and append to 
orderedSubtaskList
-               for _, item := range tmpList {
-                       value, ok := nameMetaMap[item]
-                       if !ok {
-                               return nil, fmt.Errorf("illeagal subtaskmeta 
detected %s", item)
-                       }
-                       orderedSubtaskList = append(orderedSubtaskList, *value)
-               }
+       // sort
+       orderStrList, err := topologicalSortSameElements(dependenciesMap)
+       if err != nil {
+               return nil, errors.Convert(err)
        }
-       return orderedSubtaskList, nil
-}
 
-func contains[T comparable](itemList []T, item T) bool {
-       for _, newItem := range itemList {
-               if item == newItem {
-                       return true
+       // gen list by sorted name list and return
+       orderedSubtaskList := make([]plugin.SubTaskMeta, 0)
+       for _, item := range orderStrList {
+               value, ok := nameMetaMap[item]
+               if !ok {
+                       return nil, errors.Convert(fmt.Errorf("illeagal 
subtaskmeta detected %s", item))
                }
+               orderedSubtaskList = append(orderedSubtaskList, *value)
        }
-       return false
-}
 
-func removeElements[T comparable](raw, toRemove []T) []T {
-       newList := make([]T, 0)
-       for _, item := range raw {
-               if !contains(toRemove, item) {
-                       newList = append(newList, item)
-               }
-       }
-       return newList
+       return orderedSubtaskList, nil
 }
diff --git a/backend/helpers/pluginhelper/subtaskmeta_sorter/dependency_test.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/subtask_test.go
similarity index 90%
rename from backend/helpers/pluginhelper/subtaskmeta_sorter/dependency_test.go
rename to backend/helpers/pluginhelper/subtaskmeta/sorter/subtask_test.go
index 451837ff2..0bf43bcff 100644
--- a/backend/helpers/pluginhelper/subtaskmeta_sorter/dependency_test.go
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/subtask_test.go
@@ -15,12 +15,13 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package subtaskmeta_sorter
+package sorter
 
 import (
-       "github.com/apache/incubator-devlake/core/plugin"
        "reflect"
        "testing"
+
+       "github.com/apache/incubator-devlake/core/plugin"
 )
 
 func Test_topologicalSort(t *testing.T) {
@@ -85,13 +86,13 @@ func Test_topologicalSort(t *testing.T) {
        }
        for _, tt := range tests {
                t.Run(tt.name, func(t *testing.T) {
-                       got, err := topologicalSort(tt.args.metas)
+                       got, err := dependenciesTopologicalSort(tt.args.metas)
                        if (err != nil) != tt.wantErr {
-                               t.Errorf("topologicalSort() error = %v, wantErr 
%v", err, tt.wantErr)
+                               t.Errorf("dependenciesTopologicalSort() error = 
%v, wantErr %v", err, tt.wantErr)
                                return
                        }
                        if !reflect.DeepEqual(got, tt.want) {
-                               t.Errorf("topologicalSort() got = %v, want %v", 
got, tt.want)
+                               t.Errorf("dependenciesTopologicalSort() got = 
%v, want %v", got, tt.want)
                        }
                })
        }
diff --git a/backend/helpers/pluginhelper/subtaskmeta/sorter/table.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/table.go
new file mode 100644
index 000000000..9468b5c95
--- /dev/null
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/table.go
@@ -0,0 +1,80 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+*/
+
+package sorter
+
+import (
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
+)
+
+type TableSorter struct {
+       metas []*plugin.SubTaskMeta
+}
+
+func NewTableSorter(metas []*plugin.SubTaskMeta) SubTaskMetaSorter {
+       return &TableSorter{metas: metas}
+}
+
+func (d *TableSorter) Sort() ([]plugin.SubTaskMeta, errors.Error) {
+       return tableTopologicalSort(d.metas)
+}
+
+func tableTopologicalSort(metas []*plugin.SubTaskMeta) ([]plugin.SubTaskMeta, 
errors.Error) {
+       constructedMetas := constructDependenciesByTable(metas)
+       return dependenciesTopologicalSort(constructedMetas)
+}
+
+func constructDependenciesByTable(metas []*plugin.SubTaskMeta) 
[]*plugin.SubTaskMeta {
+       // construct map by metas and their produced tables, the key is table, 
and value is metas
+       tableMetasMap := make(map[string][]*plugin.SubTaskMeta)
+       for _, item := range metas {
+               for _, tableItem := range item.ProductTables {
+                       if value, ok := tableMetasMap[tableItem]; ok {
+                               tableMetasMap[tableItem] = append(value, item)
+                       } else {
+                               tableMetasMap[tableItem] = 
[]*plugin.SubTaskMeta{item}
+                       }
+               }
+       }
+       // construct meta dependencies by meta.TableDependencies
+       // use noDupMap to deduplicate dependencies of meta
+       noDupMap := make(map[*plugin.SubTaskMeta]map[*plugin.SubTaskMeta]any)
+       for _, metaItem := range metas {
+               // convert dependency tables to dependency metas
+               dependenciesMap, ok := noDupMap[metaItem]
+               if !ok {
+                       noDupMap[metaItem] = make(map[*plugin.SubTaskMeta]any)
+                       dependenciesMap = noDupMap[metaItem]
+               }
+               for _, tableItem := range metaItem.DependencyTables {
+                       for _, item := range tableMetasMap[tableItem] {
+                               dependenciesMap[item] = ""
+                       }
+               }
+               metaItem.Dependencies = keys(dependenciesMap)
+       }
+       return metas
+}
+
+func keys[T comparable](raw map[T]any) []T {
+       list := make([]T, 0)
+       for key := range raw {
+               list = append(list, key)
+       }
+       return list
+}
diff --git a/backend/helpers/pluginhelper/subtaskmeta/sorter/table_test.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/table_test.go
new file mode 100644
index 000000000..4503d1eb0
--- /dev/null
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/table_test.go
@@ -0,0 +1,81 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+*/
+
+package sorter
+
+import (
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/plugin"
+       "reflect"
+       "testing"
+)
+
+func Test_tableTopologicalSort(t *testing.T) {
+       pluginA := plugin.SubTaskMeta{
+               Name:             "A",
+               DependencyTables: []string{},
+               ProductTables:    []string{"_TOOL_TEST_TABLE", 
"_TOOL_TEST_TABLE2"},
+       }
+       pluginB := plugin.SubTaskMeta{
+               Name:             "B",
+               DependencyTables: []string{"_TOOL_TEST_TABLE"},
+               ProductTables:    []string{"_TOOL_TEST_TABLE2", 
"_TOOL_TEST_TABLE3"},
+       }
+       pluginC := plugin.SubTaskMeta{
+               Name:             "C",
+               DependencyTables: []string{"_TOOL_TEST_TABLE"},
+               ProductTables:    []string{"_TOOL_TEST_TABLE3"},
+       }
+       pluginD := plugin.SubTaskMeta{
+               Name:             "D",
+               DependencyTables: []string{"_TOOL_TEST_TABLE2", 
"_TOOL_TEST_TABLE3"},
+               ProductTables:    []string{"_TOOL_TEST_TABLE4"},
+       }
+
+       type args struct {
+               metas []*plugin.SubTaskMeta
+       }
+       tests := []struct {
+               name  string
+               args  args
+               want  []plugin.SubTaskMeta
+               want1 errors.Error
+       }{
+               {
+                       name:  "test sorter",
+                       args:  args{[]*plugin.SubTaskMeta{&pluginA, &pluginB, 
&pluginC, &pluginD}},
+                       want:  []plugin.SubTaskMeta{pluginA, pluginB, pluginC, 
pluginD},
+                       want1: nil,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, got1 := tableTopologicalSort(tt.args.metas)
+                       if len(got) != len(tt.want) {
+                               t.Errorf("tableTopologicalSort() got = %v, want 
%v", got, tt.want)
+                       }
+                       for index, item := range got {
+                               if item.Name != tt.want[index].Name {
+                                       t.Errorf("tableTopologicalSort() got = 
%v, want %v, not equal with index = %d", got, tt.want, index)
+                               }
+                       }
+                       if !reflect.DeepEqual(got1, tt.want1) {
+                               t.Errorf("tableTopologicalSort() got1 = %v, 
want %v", got1, tt.want1)
+                       }
+               })
+       }
+}
diff --git a/backend/helpers/pluginhelper/subtaskmeta/sorter/utils.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/utils.go
new file mode 100644
index 000000000..d2d6c9fea
--- /dev/null
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/utils.go
@@ -0,0 +1,72 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+*/
+
+package sorter
+
+import (
+       "fmt"
+       "sort"
+)
+
+// topologicalSortSameElements
+func topologicalSortSameElements(sameElementsDependencyMap 
map[string][]string) ([]string, error) {
+       sortedKeyList := make([]string, 0)
+       for {
+               if len(sameElementsDependencyMap) == 0 {
+                       break
+               }
+               tmpList := make([]string, 0)
+               for key, item := range sameElementsDependencyMap {
+                       if len(item) == 0 {
+                               tmpList = append(tmpList, key)
+                       }
+               }
+               if len(tmpList) == 0 {
+                       return nil, fmt.Errorf("cyclic dependency detected: 
%v", sameElementsDependencyMap)
+               }
+               // remove item in dependencies map
+               for key, value := range sameElementsDependencyMap {
+                       if contains(tmpList, key) {
+                               delete(sameElementsDependencyMap, key)
+                       } else {
+                               sameElementsDependencyMap[key] = 
removeElements(value, tmpList)
+                       }
+               }
+               sort.Strings(tmpList)
+               sortedKeyList = append(sortedKeyList, tmpList...)
+       }
+       return sortedKeyList, nil
+}
+
+func contains[T comparable](itemList []T, item T) bool {
+       for _, newItem := range itemList {
+               if item == newItem {
+                       return true
+               }
+       }
+       return false
+}
+
+func removeElements[T comparable](raw, toRemove []T) []T {
+       newList := make([]T, 0)
+       for _, item := range raw {
+               if !contains(toRemove, item) {
+                       newList = append(newList, item)
+               }
+       }
+       return newList
+}
diff --git a/backend/helpers/pluginhelper/subtaskmeta/sorter/utils_test.go 
b/backend/helpers/pluginhelper/subtaskmeta/sorter/utils_test.go
new file mode 100644
index 000000000..175d143ad
--- /dev/null
+++ b/backend/helpers/pluginhelper/subtaskmeta/sorter/utils_test.go
@@ -0,0 +1,73 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+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.
+*/
+
+package sorter
+
+import (
+       "reflect"
+       "testing"
+)
+
+func Test_topologicalSortSameElements(t *testing.T) {
+       type args struct {
+               dependenciesMap map[string][]string
+       }
+       tests := []struct {
+               name    string
+               args    args
+               want    []string
+               wantErr bool
+       }{
+               {
+                       name: "correct stable sort",
+                       args: args{
+                               map[string][]string{
+                                       "Aa": {"B", "C"},
+                                       "Ac": {"B", "C"},
+                                       "B":  {"C"},
+                                       "C":  {},
+                               },
+                       },
+                       want:    []string{"C", "B", "Aa", "Ac"},
+                       wantErr: false,
+               },
+               {
+                       name: "cyclic error",
+                       args: args{
+                               map[string][]string{
+                                       "A": {"B", "C"},
+                                       "B": {"C"},
+                                       "C": {"A"},
+                               },
+                       },
+                       want:    nil,
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, err := 
topologicalSortSameElements(tt.args.dependenciesMap)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("topologicalSortSameElements() error = 
%v, wantErr %v", err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("topologicalSortSameElements() got = 
%v, want %v", got, tt.want)
+                       }
+               })
+       }
+}
diff --git a/backend/plugins/github/impl/impl.go 
b/backend/plugins/github/impl/impl.go
index 08f2ef986..0151d81de 100644
--- a/backend/plugins/github/impl/impl.go
+++ b/backend/plugins/github/impl/impl.go
@@ -19,13 +19,13 @@ package impl
 
 import (
        "fmt"
+       
"github.com/apache/incubator-devlake/helpers/pluginhelper/subtaskmeta/sorter"
        "time"
 
-       "github.com/apache/incubator-devlake/core/models/domainlayer/devops"
-
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/devops"
        "github.com/apache/incubator-devlake/core/plugin"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/api"
@@ -45,8 +45,19 @@ var _ interface {
        plugin.CloseablePluginTask
 } = (*Github)(nil)
 
+var sortedSubtaskMetas []plugin.SubTaskMeta
+
 type Github struct{}
 
+func init() {
+       var err error
+       // check subtask meta loop and gen subtask list when plugin init
+       sortedSubtaskMetas, err = 
sorter.NewTableSorter(tasks.SubTaskMetaList).Sort()
+       if err != nil {
+               panic(err)
+       }
+}
+
 func (p Github) Connection() dal.Tabler {
        return &models.GithubConnection{}
 }
@@ -103,53 +114,7 @@ func (p Github) Name() string {
 }
 
 func (p Github) SubTaskMetas() []plugin.SubTaskMeta {
-       return []plugin.SubTaskMeta{
-               tasks.CollectApiIssuesMeta,
-               tasks.ExtractApiIssuesMeta,
-               tasks.CollectApiPullRequestsMeta,
-               tasks.ExtractApiPullRequestsMeta,
-               tasks.CollectApiCommentsMeta,
-               tasks.ExtractApiCommentsMeta,
-               tasks.CollectApiEventsMeta,
-               tasks.ExtractApiEventsMeta,
-               tasks.CollectApiPullRequestCommitsMeta,
-               tasks.ExtractApiPullRequestCommitsMeta,
-               tasks.CollectApiPullRequestReviewsMeta,
-               tasks.ExtractApiPullRequestReviewsMeta,
-               tasks.CollectApiPrReviewCommentsMeta,
-               tasks.ExtractApiPrReviewCommentsMeta,
-               tasks.CollectApiCommitsMeta,
-               tasks.ExtractApiCommitsMeta,
-               tasks.CollectApiCommitStatsMeta,
-               tasks.ExtractApiCommitStatsMeta,
-               tasks.CollectMilestonesMeta,
-               tasks.ExtractMilestonesMeta,
-               tasks.CollectAccountsMeta,
-               tasks.ExtractAccountsMeta,
-               tasks.CollectAccountOrgMeta,
-               tasks.ExtractAccountOrgMeta,
-               tasks.CollectRunsMeta,
-               tasks.ExtractRunsMeta,
-               tasks.ConvertRunsMeta,
-               tasks.CollectJobsMeta,
-               tasks.ExtractJobsMeta,
-               tasks.ConvertJobsMeta,
-               tasks.EnrichPullRequestIssuesMeta,
-               tasks.ConvertRepoMeta,
-               tasks.ConvertIssuesMeta,
-               tasks.ConvertIssueAssigneeMeta,
-               tasks.ConvertCommitsMeta,
-               tasks.ConvertIssueLabelsMeta,
-               tasks.ConvertPullRequestCommitsMeta,
-               tasks.ConvertPullRequestsMeta,
-               tasks.ConvertPullRequestReviewsMeta,
-               tasks.ConvertPullRequestLabelsMeta,
-               tasks.ConvertPullRequestIssuesMeta,
-               tasks.ConvertIssueCommentsMeta,
-               tasks.ConvertPullRequestCommentsMeta,
-               tasks.ConvertMilestonesMeta,
-               tasks.ConvertAccountsMeta,
-       }
+       return sortedSubtaskMetas
 }
 
 func (p Github) PrepareTaskData(taskCtx plugin.TaskContext, options 
map[string]interface{}) (interface{}, errors.Error) {
diff --git a/backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go 
b/backend/plugins/github/impl/impl_test.go
similarity index 55%
copy from backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go
copy to backend/plugins/github/impl/impl_test.go
index fb4e30b5e..63af43b09 100644
--- a/backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go
+++ b/backend/plugins/github/impl/impl_test.go
@@ -6,7 +6,7 @@ The ASF licenses this file to You under the Apache License, 
Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at
 
-    http://www.apache.org/licenses/LICENSE-2.0
+       http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,10 +15,24 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package subtaskmeta_sorter
+package impl
 
-import "github.com/apache/incubator-devlake/core/plugin"
+import (
+       "testing"
 
-type SubTaskMetaSorter interface {
-       Sort() ([]plugin.SubTaskMeta, error)
+       
"github.com/apache/incubator-devlake/helpers/pluginhelper/subtaskmeta/sorter"
+       "github.com/apache/incubator-devlake/plugins/github/tasks"
+)
+
+func Test_genSubtaskList(t *testing.T) {
+       testSortedSubtaskList, err := 
sorter.NewTableSorter(tasks.SubTaskMetaList).Sort()
+       if err != nil {
+               t.Error(err)
+               return
+       }
+       subtaskNameList := make([]string, len(testSortedSubtaskList))
+       for index, item := range testSortedSubtaskList {
+               subtaskNameList[index] = item.Name
+       }
+       t.Logf("got subtask list %s", subtaskNameList)
 }
diff --git a/backend/plugins/github/tasks/account_collector.go 
b/backend/plugins/github/tasks/account_collector.go
index 2aa8beada..931a7ae7c 100644
--- a/backend/plugins/github/tasks/account_collector.go
+++ b/backend/plugins/github/tasks/account_collector.go
@@ -30,12 +30,28 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectAccountsMeta)
+}
+
 const RAW_ACCOUNT_TABLE = "github_api_accounts"
 
 type SimpleAccount struct {
        Login string
 }
 
+var CollectAccountsMeta = plugin.SubTaskMeta{
+       Name:             "collectAccounts",
+       EntryPoint:       CollectAccounts,
+       EnabledByDefault: true,
+       Description:      "Collect accounts data from Github api, does not 
support either timeFilter or diffSync.",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{
+               //models.GithubRepoAccount{}.TableName() // cursor, config will 
not regard as dependency
+       },
+       ProductTables: []string{RAW_ACCOUNT_TABLE},
+}
+
 func CollectAccounts(taskCtx plugin.SubTaskContext) errors.Error {
        db := taskCtx.GetDal()
        data := taskCtx.GetData().(*GithubTaskData)
@@ -85,11 +101,3 @@ func CollectAccounts(taskCtx plugin.SubTaskContext) 
errors.Error {
        }
        return collector.Execute()
 }
-
-var CollectAccountsMeta = plugin.SubTaskMeta{
-       Name:             "collectAccounts",
-       EntryPoint:       CollectAccounts,
-       EnabledByDefault: true,
-       Description:      "Collect accounts data from Github api, does not 
support either timeFilter or diffSync.",
-       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
-}
diff --git a/backend/plugins/github/tasks/account_convertor.go 
b/backend/plugins/github/tasks/account_convertor.go
index 53ad1563d..c93c80407 100644
--- a/backend/plugins/github/tasks/account_convertor.go
+++ b/backend/plugins/github/tasks/account_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+       "strings"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/common"
@@ -27,16 +30,23 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
-       "strings"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertAccountsMeta)
+}
+
 var ConvertAccountsMeta = plugin.SubTaskMeta{
        Name:             "convertAccounts",
        EntryPoint:       ConvertAccounts,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_accounts into  
domain layer table accounts",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{
+               models.GithubAccount{}.TableName(),     // cursor
+               models.GithubRepoAccount{}.TableName(), // cursor
+               models.GithubAccountOrg{}.TableName()}, // account id gen
+       ProductTables: []string{crossdomain.Account{}.TableName()},
 }
 
 type GithubAccountWithOrg struct {
diff --git a/backend/plugins/github/tasks/account_extractor.go 
b/backend/plugins/github/tasks/account_extractor.go
index 3c2f6896b..7fa2c0f13 100644
--- a/backend/plugins/github/tasks/account_extractor.go
+++ b/backend/plugins/github/tasks/account_extractor.go
@@ -19,19 +19,26 @@ package tasks
 
 import (
        "encoding/json"
+       "time"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "time"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractAccountsMeta)
+}
+
 var ExtractAccountsMeta = plugin.SubTaskMeta{
        Name:             "extractAccounts",
        EntryPoint:       ExtractAccounts,
        EnabledByDefault: true,
        Description:      "Extract raw account data  into tool layer table 
github_accounts",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{RAW_ACCOUNT_TABLE},
+       ProductTables:    []string{models.GithubAccount{}.TableName()},
 }
 
 type DetailGithubAccountResponse struct {
diff --git a/backend/plugins/github/tasks/account_org_collector.go 
b/backend/plugins/github/tasks/account_org_collector.go
index ae8c569ad..86889fdb4 100644
--- a/backend/plugins/github/tasks/account_org_collector.go
+++ b/backend/plugins/github/tasks/account_org_collector.go
@@ -19,16 +19,21 @@ package tasks
 
 import (
        "encoding/json"
+       "io"
+       "net/http"
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "io"
-       "net/http"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectAccountOrgMeta)
+}
+
 const RAW_ACCOUNT_ORG_TABLE = "github_api_account_orgs"
 
 type SimpleAccountWithId struct {
@@ -36,6 +41,19 @@ type SimpleAccountWithId struct {
        AccountId int
 }
 
+var CollectAccountOrgMeta = plugin.SubTaskMeta{
+       Name:             "collectAccountOrg",
+       EntryPoint:       CollectAccountOrg,
+       EnabledByDefault: true,
+       Description:      "Collect accounts org data from Github api, does not 
support either timeFilter or diffSync.",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{
+               models.GithubRepoAccount{}.TableName(), // cursor
+               models.GithubAccount{}.TableName(),     // cursor
+       },
+       ProductTables: []string{RAW_ACCOUNT_ORG_TABLE},
+}
+
 func CollectAccountOrg(taskCtx plugin.SubTaskContext) errors.Error {
        db := taskCtx.GetDal()
        data := taskCtx.GetData().(*GithubTaskData)
@@ -85,11 +103,3 @@ func CollectAccountOrg(taskCtx plugin.SubTaskContext) 
errors.Error {
        }
        return collector.Execute()
 }
-
-var CollectAccountOrgMeta = plugin.SubTaskMeta{
-       Name:             "collectAccountOrg",
-       EntryPoint:       CollectAccountOrg,
-       EnabledByDefault: true,
-       Description:      "Collect accounts org data from Github api, does not 
support either timeFilter or diffSync.",
-       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
-}
diff --git a/backend/plugins/github/tasks/account_org_extractor.go 
b/backend/plugins/github/tasks/account_org_extractor.go
index 1f37c1041..60d93ce7f 100644
--- a/backend/plugins/github/tasks/account_org_extractor.go
+++ b/backend/plugins/github/tasks/account_org_extractor.go
@@ -19,18 +19,25 @@ package tasks
 
 import (
        "encoding/json"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractAccountOrgMeta)
+}
+
 var ExtractAccountOrgMeta = plugin.SubTaskMeta{
-       Name:             "ExtractAccountOrg",
+       Name:             "extractAccountOrg",
        EntryPoint:       ExtractAccountOrg,
        EnabledByDefault: true,
        Description:      "Extract raw account org data into tool layer table 
github_account_orgs",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{RAW_ACCOUNT_ORG_TABLE},
+       ProductTables:    []string{models.GithubAccountOrg{}.TableName()},
 }
 
 type GithubAccountOrgsResponse struct {
diff --git a/backend/plugins/github/tasks/api_client.go 
b/backend/plugins/github/tasks/api_client.go
index 3e1440e71..c9bfa852c 100644
--- a/backend/plugins/github/tasks/api_client.go
+++ b/backend/plugins/github/tasks/api_client.go
@@ -83,10 +83,3 @@ func CreateApiClient(taskCtx plugin.TaskContext, connection 
*models.GithubConnec
        }
        return asyncApiClient, nil
 }
-
-func ignoreHTTPStatus422(res *http.Response) errors.Error {
-       if res.StatusCode == http.StatusUnprocessableEntity {
-               return api.ErrIgnoreAndContinue
-       }
-       return nil
-}
diff --git a/backend/plugins/github/tasks/cicd_job_collector.go 
b/backend/plugins/github/tasks/cicd_job_collector.go
index 4dd95c5c5..787476c31 100644
--- a/backend/plugins/github/tasks/cicd_job_collector.go
+++ b/backend/plugins/github/tasks/cicd_job_collector.go
@@ -31,6 +31,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectJobsMeta)
+}
+
 const RAW_JOB_TABLE = "github_api_jobs"
 
 var CollectJobsMeta = plugin.SubTaskMeta{
@@ -39,6 +43,8 @@ var CollectJobsMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect Jobs data from Github action api, supports 
both timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       DependencyTables: []string{models.GithubRun{}.TableName()},
+       ProductTables:    []string{RAW_JOB_TABLE},
 }
 
 func CollectJobs(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/cicd_job_convertor.go 
b/backend/plugins/github/tasks/cicd_job_convertor.go
index b59ae50da..b1fd214f3 100644
--- a/backend/plugins/github/tasks/cicd_job_convertor.go
+++ b/backend/plugins/github/tasks/cicd_job_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+       "strings"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,16 +29,25 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
-       "strings"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertJobsMeta)
+}
+
 var ConvertJobsMeta = plugin.SubTaskMeta{
        Name:             "convertJobs",
        EntryPoint:       ConvertJobs,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_jobs into  domain 
layer table cicd_tasks",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       DependencyTables: []string{
+               RAW_JOB_TABLE,
+               models.GithubJob{}.TableName(), // cursor and generator
+               models.GithubRun{}.TableName(), // id generator
+               //models.GithubRepo{}.TableName(), // id generator, but config 
will not regard as dependency
+       },
+       ProductTables: []string{devops.CICDTask{}.TableName()},
 }
 
 type SimpleBranch struct {
diff --git a/backend/plugins/github/tasks/cicd_job_extractor.go 
b/backend/plugins/github/tasks/cicd_job_extractor.go
index edb96cccf..9193b0342 100644
--- a/backend/plugins/github/tasks/cicd_job_extractor.go
+++ b/backend/plugins/github/tasks/cicd_job_extractor.go
@@ -28,12 +28,18 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractJobsMeta)
+}
+
 var ExtractJobsMeta = plugin.SubTaskMeta{
        Name:             "extractJobs",
        EntryPoint:       ExtractJobs,
        EnabledByDefault: true,
        Description:      "Extract raw run data into tool layer table 
github_jobs",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       DependencyTables: []string{RAW_JOB_TABLE},
+       ProductTables:    []string{models.GithubJob{}.TableName()},
 }
 
 func ExtractJobs(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/cicd_run_collector.go 
b/backend/plugins/github/tasks/cicd_run_collector.go
index 3af4a66ce..78f9da18c 100644
--- a/backend/plugins/github/tasks/cicd_run_collector.go
+++ b/backend/plugins/github/tasks/cicd_run_collector.go
@@ -33,6 +33,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectRunsMeta)
+}
+
 const RAW_RUN_TABLE = "github_api_runs"
 
 // Although the API accepts a maximum of 100 entries per page, sometimes
@@ -56,6 +60,8 @@ var CollectRunsMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect Runs data from Github action api, supports 
both timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_RUN_TABLE},
 }
 
 func CollectRuns(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/cicd_run_convertor.go 
b/backend/plugins/github/tasks/cicd_run_convertor.go
index 09bccc84f..151ebb4f2 100644
--- a/backend/plugins/github/tasks/cicd_run_convertor.go
+++ b/backend/plugins/github/tasks/cicd_run_convertor.go
@@ -31,12 +31,25 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertRunsMeta)
+}
+
 var ConvertRunsMeta = plugin.SubTaskMeta{
        Name:             "convertRuns",
        EntryPoint:       ConvertRuns,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_runs into  domain 
layer table cicd_pipeline",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       DependencyTables: []string{
+               //models.GithubRepo{}.TableName(), // config will not regard as 
dependency
+               models.GithubRun{}.TableName(),
+               RAW_RUN_TABLE,
+       },
+       ProductTables: []string{
+               devops.CICDPipeline{}.TableName(),
+               devops.CiCDPipelineCommit{}.TableName(),
+       },
 }
 
 func ConvertRuns(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/cicd_run_extractor.go 
b/backend/plugins/github/tasks/cicd_run_extractor.go
index de3326f09..a62edef91 100644
--- a/backend/plugins/github/tasks/cicd_run_extractor.go
+++ b/backend/plugins/github/tasks/cicd_run_extractor.go
@@ -27,12 +27,18 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractRunsMeta)
+}
+
 var ExtractRunsMeta = plugin.SubTaskMeta{
        Name:             "extractRuns",
        EntryPoint:       ExtractRuns,
        EnabledByDefault: true,
        Description:      "Extract raw run data into tool layer table 
github_runs",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CICD},
+       DependencyTables: []string{RAW_RUN_TABLE},
+       ProductTables:    []string{models.GithubRun{}.TableName()},
 }
 
 func ExtractRuns(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/comment_collector.go 
b/backend/plugins/github/tasks/comment_collector.go
index 709a7dc35..402bfa4e8 100644
--- a/backend/plugins/github/tasks/comment_collector.go
+++ b/backend/plugins/github/tasks/comment_collector.go
@@ -28,8 +28,22 @@ import (
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiCommentsMeta)
+}
+
 const RAW_COMMENTS_TABLE = "github_api_comments"
 
+var CollectApiCommentsMeta = plugin.SubTaskMeta{
+       Name:             "collectApiComments",
+       EntryPoint:       CollectApiComments,
+       EnabledByDefault: true,
+       Description:      "Collect comments data from Github api, supports both 
timeFilter and diffSync.",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE_REVIEW, 
plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_COMMENTS_TABLE},
+}
+
 func CollectApiComments(taskCtx plugin.SubTaskContext) errors.Error {
        data := taskCtx.GetData().(*GithubTaskData)
        collectorWithState, err := 
helper.NewStatefulApiCollector(helper.RawDataSubTaskArgs{
@@ -87,11 +101,3 @@ func CollectApiComments(taskCtx plugin.SubTaskContext) 
errors.Error {
 
        return collectorWithState.Execute()
 }
-
-var CollectApiCommentsMeta = plugin.SubTaskMeta{
-       Name:             "collectApiComments",
-       EntryPoint:       CollectApiComments,
-       EnabledByDefault: true,
-       Description:      "Collect comments data from Github api, supports both 
timeFilter and diffSync.",
-       DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE_REVIEW, 
plugin.DOMAIN_TYPE_TICKET},
-}
diff --git a/backend/plugins/github/tasks/comment_extractor.go 
b/backend/plugins/github/tasks/comment_extractor.go
index d0bc66a3e..9f18b0091 100644
--- a/backend/plugins/github/tasks/comment_extractor.go
+++ b/backend/plugins/github/tasks/comment_extractor.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
        "encoding/json"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
@@ -27,13 +28,22 @@ import (
        githubUtils "github.com/apache/incubator-devlake/plugins/github/utils"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiCommentsMeta)
+}
+
 var ExtractApiCommentsMeta = plugin.SubTaskMeta{
        Name:             "extractApiComments",
        EntryPoint:       ExtractApiComments,
        EnabledByDefault: true,
        Description: "Extract raw comment data  into tool layer table 
github_pull_request_comments" +
                "and github_issue_comments",
-       DomainTypes: []string{plugin.DOMAIN_TYPE_CODE_REVIEW, 
plugin.DOMAIN_TYPE_TICKET},
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE_REVIEW, 
plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{RAW_COMMENTS_TABLE},
+       ProductTables: []string{
+               models.GithubPrComment{}.TableName(),
+               models.GithubIssueComment{}.TableName(),
+               models.GithubRepoAccount{}.TableName()},
 }
 
 type IssueComment struct {
diff --git a/backend/plugins/github/tasks/commit_collector.go 
b/backend/plugins/github/tasks/commit_collector.go
index 35f7e95da..33e005331 100644
--- a/backend/plugins/github/tasks/commit_collector.go
+++ b/backend/plugins/github/tasks/commit_collector.go
@@ -28,6 +28,10 @@ import (
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiCommitsMeta)
+}
+
 const RAW_COMMIT_TABLE = "github_api_commits"
 
 var CollectApiCommitsMeta = plugin.SubTaskMeta{
@@ -36,6 +40,8 @@ var CollectApiCommitsMeta = plugin.SubTaskMeta{
        EnabledByDefault: false,
        Description:      "Collect commits data from Github api, supports both 
timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_COMMIT_TABLE},
 }
 
 func CollectApiCommits(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/commit_convertor.go 
b/backend/plugins/github/tasks/commit_convertor.go
index 5b8589374..e8dcfa5e9 100644
--- a/backend/plugins/github/tasks/commit_convertor.go
+++ b/backend/plugins/github/tasks/commit_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer/code"
@@ -25,15 +27,26 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertCommitsMeta)
+}
+
 var ConvertCommitsMeta = plugin.SubTaskMeta{
        Name:             "convertCommits",
        EntryPoint:       ConvertCommits,
        EnabledByDefault: false,
        Description:      "Convert tool layer table github_commits into  domain 
layer table commits",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE},
+       DependencyTables: []string{
+               models.GithubCommit{}.TableName(),     // cursor
+               models.GithubRepoCommit{}.TableName(), // cursor
+               //models.GithubRepo{}.TableName(),       // id generator, but 
config not regard as dependency
+               RAW_COMMIT_TABLE},
+       ProductTables: []string{
+               code.Commit{}.TableName(),
+               code.RepoCommit{}.TableName()},
 }
 
 func ConvertCommits(taskCtx plugin.SubTaskContext) errors.Error {
@@ -64,7 +77,7 @@ func ConvertCommits(taskCtx plugin.SubTaskContext) 
errors.Error {
                                ConnectionId: data.Options.ConnectionId,
                                Name:         data.Options.Name,
                        },
-                       Table: RAW_COMMENTS_TABLE,
+                       Table: RAW_COMMIT_TABLE,
                },
                InputRowType: reflect.TypeOf(models.GithubCommit{}),
                Input:        cursor,
diff --git a/backend/plugins/github/tasks/commit_extractor.go 
b/backend/plugins/github/tasks/commit_extractor.go
index b1d486105..51a71640e 100644
--- a/backend/plugins/github/tasks/commit_extractor.go
+++ b/backend/plugins/github/tasks/commit_extractor.go
@@ -19,18 +19,27 @@ package tasks
 
 import (
        "encoding/json"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiCommitsMeta)
+}
+
 var ExtractApiCommitsMeta = plugin.SubTaskMeta{
        Name:             "extractApiCommits",
        EntryPoint:       ExtractApiCommits,
        EnabledByDefault: false,
        Description:      "Extract raw commit data into tool layer table 
github_commits",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE},
+       DependencyTables: []string{RAW_COMMIT_TABLE},
+       ProductTables: []string{
+               models.GithubCommit{}.TableName(),
+               models.GithubRepoCommit{}.TableName()},
 }
 
 type CommitsResponse struct {
diff --git a/backend/plugins/github/tasks/commit_stats_collector.go 
b/backend/plugins/github/tasks/commit_stats_collector.go
index 6d2cbc410..3aa71635f 100644
--- a/backend/plugins/github/tasks/commit_stats_collector.go
+++ b/backend/plugins/github/tasks/commit_stats_collector.go
@@ -32,6 +32,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiCommitStatsMeta)
+}
+
 const RAW_COMMIT_STATS_TABLE = "github_api_commit_stats"
 
 var CollectApiCommitStatsMeta = plugin.SubTaskMeta{
@@ -40,6 +44,11 @@ var CollectApiCommitStatsMeta = plugin.SubTaskMeta{
        EnabledByDefault: false,
        Description:      "Collect commitStats data from Github api, does not 
support either timeFilter or diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE},
+       DependencyTables: []string{
+               //models.GithubRepoCommit{}.TableName(), // cursor, config will 
not regard as dependency
+               //models.GithubCommit{}.TableName()}, // cursor
+       },
+       ProductTables: []string{RAW_COMMIT_STATS_TABLE},
 }
 
 func CollectApiCommitStats(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/commit_stats_extractor.go 
b/backend/plugins/github/tasks/commit_stats_extractor.go
index 0cbe30993..e6011f5c0 100644
--- a/backend/plugins/github/tasks/commit_stats_extractor.go
+++ b/backend/plugins/github/tasks/commit_stats_extractor.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
        "encoding/json"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
@@ -26,12 +27,20 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiCommitStatsMeta)
+}
+
 var ExtractApiCommitStatsMeta = plugin.SubTaskMeta{
        Name:             "extractApiCommitStats",
        EntryPoint:       ExtractApiCommitStats,
        EnabledByDefault: false,
        Description:      "Extract raw commit stats data into tool layer table 
github_commit_stats",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE},
+       DependencyTables: []string{RAW_COMMIT_STATS_TABLE},
+       ProductTables: []string{
+               models.GithubCommit{}.TableName(),
+               models.GithubCommitStat{}.TableName()},
 }
 
 type ApiSingleCommitResponse struct {
diff --git a/backend/plugins/github/tasks/event_collector.go 
b/backend/plugins/github/tasks/event_collector.go
index da15a98de..a26d5ef64 100644
--- a/backend/plugins/github/tasks/event_collector.go
+++ b/backend/plugins/github/tasks/event_collector.go
@@ -33,6 +33,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiEventsMeta)
+}
+
 const RAW_EVENTS_TABLE = "github_api_events"
 
 type SimpleGithubApiEvents struct {
@@ -46,6 +50,8 @@ var CollectApiEventsMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect Events data from Github api, supports both 
timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_EVENTS_TABLE},
 }
 
 func CollectApiEvents(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/event_extractor.go 
b/backend/plugins/github/tasks/event_extractor.go
index 94e3ddeb7..7868874d2 100644
--- a/backend/plugins/github/tasks/event_extractor.go
+++ b/backend/plugins/github/tasks/event_extractor.go
@@ -19,18 +19,25 @@ package tasks
 
 import (
        "encoding/json"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiEventsMeta)
+}
+
 var ExtractApiEventsMeta = plugin.SubTaskMeta{
        Name:             "extractApiEvents",
        EntryPoint:       ExtractApiEvents,
        EnabledByDefault: true,
        Description:      "Extract raw Events data into tool layer table 
github_issue_events",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{RAW_EVENTS_TABLE},
+       ProductTables:    []string{models.GithubIssueEvent{}.TableName()},
 }
 
 type IssueEvent struct {
diff --git a/backend/plugins/github/tasks/issue_assignee_convertor.go 
b/backend/plugins/github/tasks/issue_assignee_convertor.go
index 373bec2b5..24c3c0f16 100644
--- a/backend/plugins/github/tasks/issue_assignee_convertor.go
+++ b/backend/plugins/github/tasks/issue_assignee_convertor.go
@@ -29,12 +29,23 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertIssueAssigneeMeta)
+}
+
 var ConvertIssueAssigneeMeta = plugin.SubTaskMeta{
        Name:             "convertIssueAssignee",
        EntryPoint:       ConvertIssueAssignee,
        EnabledByDefault: true,
        Description:      "Convert tool layer table 
_tool_github_issue_assignees into  domain layer table issue_assignees",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{
+               //models.GithubIssueAssignee{}.TableName(), // cursor, not 
regard as dependency
+               models.GithubIssue{}.TableName(), // id generator
+               RAW_ISSUE_TABLE,
+               //models.GithubAccount{}.TableName(),       // id generator, 
config will not regard as dependency
+       },
+       ProductTables: []string{models.GithubIssueAssignee{}.TableName()},
 }
 
 func ConvertIssueAssignee(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/issue_collector.go 
b/backend/plugins/github/tasks/issue_collector.go
index e646f7e08..f88942319 100644
--- a/backend/plugins/github/tasks/issue_collector.go
+++ b/backend/plugins/github/tasks/issue_collector.go
@@ -30,12 +30,18 @@ import (
 
 const RAW_ISSUE_TABLE = "github_api_issues"
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiIssuesMeta)
+}
+
 var CollectApiIssuesMeta = plugin.SubTaskMeta{
        Name:             "collectApiIssues",
        EntryPoint:       CollectApiIssues,
        EnabledByDefault: true,
        Description:      "Collect issues data from Github api, supports both 
timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_ISSUE_TABLE},
 }
 
 func CollectApiIssues(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/issue_comment_convertor.go 
b/backend/plugins/github/tasks/issue_comment_convertor.go
index d1d7f70e4..860a5fc14 100644
--- a/backend/plugins/github/tasks/issue_comment_convertor.go
+++ b/backend/plugins/github/tasks/issue_comment_convertor.go
@@ -30,12 +30,22 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertIssueCommentsMeta)
+}
+
 var ConvertIssueCommentsMeta = plugin.SubTaskMeta{
        Name:             "convertIssueComments",
        EntryPoint:       ConvertIssueComments,
        EnabledByDefault: true,
        Description:      "ConvertIssueComments data from Github api",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{
+               models.GithubIssueComment{}.TableName(), // cursor
+               models.GithubIssue{}.TableName(),        // cursor and id 
generator
+               models.GithubAccount{}.TableName(),      // id generator
+               RAW_COMMENTS_TABLE},
+       ProductTables: []string{ticket.IssueComment{}.TableName()},
 }
 
 func ConvertIssueComments(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/issue_convertor.go 
b/backend/plugins/github/tasks/issue_convertor.go
index 9aa6cccf4..165f21835 100644
--- a/backend/plugins/github/tasks/issue_convertor.go
+++ b/backend/plugins/github/tasks/issue_convertor.go
@@ -18,6 +18,10 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+       "strconv"
+       "strings"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,17 +30,26 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
-       "strconv"
-       "strings"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertIssuesMeta)
+}
+
 var ConvertIssuesMeta = plugin.SubTaskMeta{
        Name:             "convertIssues",
        EntryPoint:       ConvertIssues,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_issues into  domain 
layer table issues",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{
+               models.GithubIssue{}.TableName(),   // cursor
+               models.GithubAccount{}.TableName(), // id generator
+               //models.GithubRepo{}.TableName(),    // id generator, but 
config not regard as dependency
+               RAW_ISSUE_TABLE},
+       ProductTables: []string{
+               ticket.Issue{}.TableName(),
+               ticket.BoardIssue{}.TableName()},
 }
 
 func ConvertIssues(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/issue_extractor.go 
b/backend/plugins/github/tasks/issue_extractor.go
index 29f6c43e2..c48eeab18 100644
--- a/backend/plugins/github/tasks/issue_extractor.go
+++ b/backend/plugins/github/tasks/issue_extractor.go
@@ -29,12 +29,22 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiIssuesMeta)
+}
+
 var ExtractApiIssuesMeta = plugin.SubTaskMeta{
        Name:             "extractApiIssues",
        EntryPoint:       ExtractApiIssues,
        EnabledByDefault: true,
        Description:      "Extract raw Issues data into tool layer table 
github_issues",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{RAW_ISSUE_TABLE},
+       ProductTables: []string{
+               models.GithubIssue{}.TableName(),
+               models.GithubIssueLabel{}.TableName(),
+               models.GithubRepoAccount{}.TableName(),
+               models.GithubIssueAssignee{}.TableName()},
 }
 
 type IssuesResponse struct {
diff --git a/backend/plugins/github/tasks/issue_label_convertor.go 
b/backend/plugins/github/tasks/issue_label_convertor.go
index 87ac3ba8b..8643d48cf 100644
--- a/backend/plugins/github/tasks/issue_label_convertor.go
+++ b/backend/plugins/github/tasks/issue_label_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
@@ -25,15 +27,23 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertIssueLabelsMeta)
+}
+
 var ConvertIssueLabelsMeta = plugin.SubTaskMeta{
        Name:             "convertIssueLabels",
        EntryPoint:       ConvertIssueLabels,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_issue_labels into  
domain layer table issue_labels",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{
+               models.GithubIssueLabel{}.TableName(), // cursor
+               models.GithubIssue{}.TableName(),      // cursor
+               RAW_ISSUE_TABLE},
+       ProductTables: []string{ticket.IssueLabel{}.TableName()},
 }
 
 func ConvertIssueLabels(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/milestone_collector.go 
b/backend/plugins/github/tasks/milestone_collector.go
index 51d4d3439..03d4ff586 100644
--- a/backend/plugins/github/tasks/milestone_collector.go
+++ b/backend/plugins/github/tasks/milestone_collector.go
@@ -28,6 +28,10 @@ import (
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectMilestonesMeta)
+}
+
 const RAW_MILESTONE_TABLE = "github_milestones"
 
 var CollectMilestonesMeta = plugin.SubTaskMeta{
@@ -36,6 +40,8 @@ var CollectMilestonesMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect milestone data from Github api, does not 
support either timeFilter or diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_MILESTONE_TABLE},
 }
 
 func CollectApiMilestones(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/milestone_converter.go 
b/backend/plugins/github/tasks/milestone_converter.go
index 57adddd73..8b97beee5 100644
--- a/backend/plugins/github/tasks/milestone_converter.go
+++ b/backend/plugins/github/tasks/milestone_converter.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/common"
@@ -27,15 +29,27 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertMilestonesMeta)
+}
+
 var ConvertMilestonesMeta = plugin.SubTaskMeta{
        Name:             "convertMilestones",
        EntryPoint:       ConvertMilestones,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_milestones into  
domain layer table milestones",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{
+               models.GithubIssue{}.TableName(),     // cursor
+               models.GithubMilestone{}.TableName(), // cursor
+               //models.GithubRepo{}.TableName(),      // id generator, but 
config not regard as dependency
+               RAW_MILESTONE_TABLE},
+       ProductTables: []string{
+               ticket.Sprint{}.TableName(),
+               ticket.BoardSprint{}.TableName(),
+               ticket.SprintIssue{}.TableName()},
 }
 
 type MilestoneConverterModel struct {
diff --git a/backend/plugins/github/tasks/milestone_extractor.go 
b/backend/plugins/github/tasks/milestone_extractor.go
index f5dd8f16d..b4ca218b4 100644
--- a/backend/plugins/github/tasks/milestone_extractor.go
+++ b/backend/plugins/github/tasks/milestone_extractor.go
@@ -19,18 +19,25 @@ package tasks
 
 import (
        "encoding/json"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractMilestonesMeta)
+}
+
 var ExtractMilestonesMeta = plugin.SubTaskMeta{
        Name:             "extractMilestones",
        EntryPoint:       ExtractMilestones,
        EnabledByDefault: true,
        Description:      "Extract raw milestone data into tool layer table 
github_milestones",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+       DependencyTables: []string{RAW_MILESTONE_TABLE},
+       ProductTables:    []string{models.GithubMilestone{}.TableName()},
 }
 
 type MilestonesResponse struct {
diff --git a/backend/plugins/github/tasks/pr_collector.go 
b/backend/plugins/github/tasks/pr_collector.go
index d15bf3db1..02ab884d9 100644
--- a/backend/plugins/github/tasks/pr_collector.go
+++ b/backend/plugins/github/tasks/pr_collector.go
@@ -33,6 +33,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiPullRequestsMeta)
+}
+
 const RAW_PULL_REQUEST_TABLE = "github_api_pull_requests"
 
 var CollectApiPullRequestsMeta = plugin.SubTaskMeta{
@@ -41,6 +45,8 @@ var CollectApiPullRequestsMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect PullRequests data from Github api, supports 
both timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_PULL_REQUEST_TABLE},
 }
 
 type SimpleGithubPr struct {
diff --git a/backend/plugins/github/tasks/pr_comment_convertor.go 
b/backend/plugins/github/tasks/pr_comment_convertor.go
index 0b7b77bca..7df730c92 100644
--- a/backend/plugins/github/tasks/pr_comment_convertor.go
+++ b/backend/plugins/github/tasks/pr_comment_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,15 +28,25 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertPullRequestCommentsMeta)
+}
+
 var ConvertPullRequestCommentsMeta = plugin.SubTaskMeta{
        Name:             "convertPullRequestComments",
        EntryPoint:       ConvertPullRequestComments,
        EnabledByDefault: true,
        Description:      "ConvertPullRequestComments data from Github api",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{
+               models.GithubPrComment{}.TableName(),   // cursor
+               models.GithubPullRequest{}.TableName(), // cursor
+               models.GithubAccount{}.TableName(),     // id generator
+               models.GithubPrReview{}.TableName(),    // id generator
+               RAW_COMMENTS_TABLE},
+       ProductTables: []string{code.PullRequestComment{}.TableName()},
 }
 
 func ConvertPullRequestComments(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_commit_collector.go 
b/backend/plugins/github/tasks/pr_commit_collector.go
index 2ee08cf76..30ac310e8 100644
--- a/backend/plugins/github/tasks/pr_commit_collector.go
+++ b/backend/plugins/github/tasks/pr_commit_collector.go
@@ -34,6 +34,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiPullRequestCommitsMeta)
+}
+
 const RAW_PR_COMMIT_TABLE = "github_api_pull_request_commits"
 
 // this struct should be moved to `gitub_api_common.go`
@@ -44,6 +48,8 @@ var CollectApiPullRequestCommitsMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect PullRequestCommits data from Github api, 
supports both timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{models.GithubPullRequest{}.TableName()},
+       ProductTables:    []string{RAW_PR_COMMIT_TABLE},
 }
 
 type SimplePr struct {
diff --git a/backend/plugins/github/tasks/pr_commit_convertor.go 
b/backend/plugins/github/tasks/pr_commit_convertor.go
index 267d343e7..5b6ab7042 100644
--- a/backend/plugins/github/tasks/pr_commit_convertor.go
+++ b/backend/plugins/github/tasks/pr_commit_convertor.go
@@ -29,12 +29,22 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertPullRequestCommitsMeta)
+}
+
 var ConvertPullRequestCommitsMeta = plugin.SubTaskMeta{
        Name:             "convertPullRequestCommits",
        EntryPoint:       ConvertPullRequestCommits,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_pull_request_commits 
into  domain layer table pull_request_commits",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{
+               models.GithubPrCommit{}.TableName(),    // cursor
+               models.GithubPullRequest{}.TableName(), // cursor
+               models.GithubPullRequest{}.TableName(), // id generator
+               RAW_PR_COMMIT_TABLE},
+       ProductTables: []string{code.PullRequestCommit{}.TableName()},
 }
 
 func ConvertPullRequestCommits(taskCtx plugin.SubTaskContext) (err 
errors.Error) {
diff --git a/backend/plugins/github/tasks/pr_commit_extractor.go 
b/backend/plugins/github/tasks/pr_commit_extractor.go
index 8418bbc8e..6bf025c8d 100644
--- a/backend/plugins/github/tasks/pr_commit_extractor.go
+++ b/backend/plugins/github/tasks/pr_commit_extractor.go
@@ -28,12 +28,21 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiPullRequestCommitsMeta)
+}
+
 var ExtractApiPullRequestCommitsMeta = plugin.SubTaskMeta{
        Name:             "extractApiPullRequestCommits",
        EntryPoint:       ExtractApiPullRequestCommits,
        EnabledByDefault: true,
        Description:      "Extract raw PullRequestCommits data into tool layer 
table github_commits",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{RAW_PR_COMMIT_TABLE},
+       ProductTables: []string{
+               models.GithubRepoCommit{}.TableName(),
+               models.GithubCommit{}.TableName(),
+               models.GithubPrCommit{}.TableName()},
 }
 
 type PrCommitsResponse struct {
diff --git a/backend/plugins/github/tasks/pr_convertor.go 
b/backend/plugins/github/tasks/pr_convertor.go
index 4bee55443..672484de0 100644
--- a/backend/plugins/github/tasks/pr_convertor.go
+++ b/backend/plugins/github/tasks/pr_convertor.go
@@ -30,12 +30,22 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertPullRequestsMeta)
+}
+
 var ConvertPullRequestsMeta = plugin.SubTaskMeta{
        Name:             "convertPullRequests",
        EntryPoint:       ConvertPullRequests,
        EnabledByDefault: true,
        Description:      "ConvertPullRequests data from Github api",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{
+               models.GithubPullRequest{}.TableName(), // cursor
+               //models.GithubRepo{}.TableName(),        // id generator, but 
not regard as dependency
+               models.GithubAccount{}.TableName(), // cursor
+               RAW_PULL_REQUEST_TABLE},
+       ProductTables: []string{code.PullRequest{}.TableName()},
 }
 
 func ConvertPullRequests(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_extractor.go 
b/backend/plugins/github/tasks/pr_extractor.go
index 8a03cdabc..8b2d49347 100644
--- a/backend/plugins/github/tasks/pr_extractor.go
+++ b/backend/plugins/github/tasks/pr_extractor.go
@@ -27,12 +27,21 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiPullRequestsMeta)
+}
+
 var ExtractApiPullRequestsMeta = plugin.SubTaskMeta{
        Name:             "extractApiPullRequests",
        EntryPoint:       ExtractApiPullRequests,
        EnabledByDefault: true,
        Description:      "Extract raw PullRequests data into tool layer table 
github_pull_requests",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{RAW_PULL_REQUEST_TABLE},
+       ProductTables: []string{
+               models.GithubRepoAccount{}.TableName(),
+               models.GithubPrLabel{}.TableName(),
+               models.GithubPullRequest{}.TableName()},
 }
 
 type GithubApiPullRequest struct {
diff --git a/backend/plugins/github/tasks/pr_issue_convertor.go 
b/backend/plugins/github/tasks/pr_issue_convertor.go
index e8611c88b..1c5906847 100644
--- a/backend/plugins/github/tasks/pr_issue_convertor.go
+++ b/backend/plugins/github/tasks/pr_issue_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        
"github.com/apache/incubator-devlake/core/models/domainlayer/crossdomain"
@@ -25,15 +27,23 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertPullRequestIssuesMeta)
+}
+
 var ConvertPullRequestIssuesMeta = plugin.SubTaskMeta{
        Name:             "convertPullRequestIssues",
        EntryPoint:       ConvertPullRequestIssues,
        EnabledByDefault: true,
-       Description:      "Convert tool layer table github_pull_request_issues 
into  domain layer table pull_request_issues",
+       Description:      "Convert tool layer table github_pull_request_issues 
into domain layer table pull_request_issues",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{
+               models.GithubPrIssue{}.TableName(),     // cursor and id 
generator
+               models.GithubPullRequest{}.TableName(), // cursor and id 
generator
+               RAW_PULL_REQUEST_TABLE},
+       ProductTables: []string{crossdomain.PullRequestIssue{}.TableName()},
 }
 
 func ConvertPullRequestIssues(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_issue_enricher.go 
b/backend/plugins/github/tasks/pr_issue_enricher.go
index ed0cbacae..f2b8c13fe 100644
--- a/backend/plugins/github/tasks/pr_issue_enricher.go
+++ b/backend/plugins/github/tasks/pr_issue_enricher.go
@@ -30,12 +30,20 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&EnrichPullRequestIssuesMeta)
+}
+
 var EnrichPullRequestIssuesMeta = plugin.SubTaskMeta{
        Name:             "enrichPullRequestIssues",
        EntryPoint:       EnrichPullRequestIssues,
        EnabledByDefault: true,
-       Description:      "Create tool layer table github_pull_request_issues 
from github_pull_reqeusts",
+       Description:      "Create tool layer table github_pull_request_issues 
from github_pull_requests",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{
+               models.GithubPullRequest{}.TableName(), // cursor
+               RAW_PULL_REQUEST_TABLE},
+       ProductTables: []string{models.GithubPrIssue{}.TableName()},
 }
 
 func EnrichPullRequestIssues(taskCtx plugin.SubTaskContext) (err errors.Error) 
{
@@ -54,7 +62,8 @@ func EnrichPullRequestIssues(taskCtx plugin.SubTaskContext) 
(err errors.Error) {
                }
        }
        charPattern := regexp.MustCompile(`[\/a-zA-Z\s,]+`)
-       cursor, err := db.Cursor(dal.From(&models.GithubPullRequest{}), 
dal.Where("repo_id = ? and connection_id = ?", repoId, 
data.Options.ConnectionId))
+       cursor, err := db.Cursor(dal.From(&models.GithubPullRequest{}),
+               dal.Where("repo_id = ? and connection_id = ?", repoId, 
data.Options.ConnectionId))
        if err != nil {
                return err
        }
@@ -101,7 +110,8 @@ func EnrichPullRequestIssues(taskCtx plugin.SubTaskContext) 
(err errors.Error) {
                                }
                                err = db.All(
                                        issue,
-                                       dal.Where("number = ? and repo_id = ? 
and connection_id = ?", issueNumber, repoId, data.Options.ConnectionId),
+                                       dal.Where("number = ? and repo_id = ? 
and connection_id = ?",
+                                               issueNumber, repoId, 
data.Options.ConnectionId),
                                        dal.Limit(1),
                                )
                                if err != nil {
diff --git a/backend/plugins/github/tasks/pr_label_convertor.go 
b/backend/plugins/github/tasks/pr_label_convertor.go
index 2c8b443cc..9053dcd98 100644
--- a/backend/plugins/github/tasks/pr_label_convertor.go
+++ b/backend/plugins/github/tasks/pr_label_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer/code"
@@ -25,15 +27,23 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertPullRequestLabelsMeta)
+}
+
 var ConvertPullRequestLabelsMeta = plugin.SubTaskMeta{
        Name:             "convertPullRequestLabels",
        EntryPoint:       ConvertPullRequestLabels,
        EnabledByDefault: true,
        Description:      "Convert tool layer table github_pull_request_labels 
into  domain layer table pull_request_labels",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{
+               models.GithubPrLabel{}.TableName(),     // cursor
+               models.GithubPullRequest{}.TableName(), // cursor and id 
generator
+               RAW_PULL_REQUEST_TABLE},
+       ProductTables: []string{code.PullRequestLabel{}.TableName()},
 }
 
 func ConvertPullRequestLabels(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_review_collector.go 
b/backend/plugins/github/tasks/pr_review_collector.go
index 888186cea..268f565ce 100644
--- a/backend/plugins/github/tasks/pr_review_collector.go
+++ b/backend/plugins/github/tasks/pr_review_collector.go
@@ -31,6 +31,10 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiPullRequestReviewsMeta)
+}
+
 const RAW_PR_REVIEW_TABLE = "github_api_pull_request_reviews"
 
 // this struct should be moved to `gitub_api_common.go`
@@ -41,6 +45,8 @@ var CollectApiPullRequestReviewsMeta = plugin.SubTaskMeta{
        EnabledByDefault: true,
        Description:      "Collect PullRequestReviews data from Github api, 
supports both timeFilter and diffSync.",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{models.GithubPullRequest{}.TableName()},
+       ProductTables:    []string{RAW_PR_REVIEW_TABLE},
 }
 
 func CollectApiPullRequestReviews(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_review_comment_collector.go 
b/backend/plugins/github/tasks/pr_review_comment_collector.go
index 2468b1b93..a2c817221 100644
--- a/backend/plugins/github/tasks/pr_review_comment_collector.go
+++ b/backend/plugins/github/tasks/pr_review_comment_collector.go
@@ -28,10 +28,24 @@ import (
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
+func init() {
+       RegisterSubtaskMeta(&CollectApiPrReviewCommentsMeta)
+}
+
 const RAW_PR_REVIEW_COMMENTS_TABLE = "github_api_pull_request_review_comments"
 
 // this struct should be moved to `github_api_common.go`
 
+var CollectApiPrReviewCommentsMeta = plugin.SubTaskMeta{
+       Name:             "collectApiPrReviewCommentsMeta",
+       EntryPoint:       CollectPrReviewComments,
+       EnabledByDefault: true,
+       Description:      "Collect pr review comments data from Github api, 
supports both timeFilter and diffSync.",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{},
+       ProductTables:    []string{RAW_PR_REVIEW_COMMENTS_TABLE},
+}
+
 func CollectPrReviewComments(taskCtx plugin.SubTaskContext) errors.Error {
        data := taskCtx.GetData().(*GithubTaskData)
 
@@ -88,11 +102,3 @@ func CollectPrReviewComments(taskCtx plugin.SubTaskContext) 
errors.Error {
 
        return collectorWithState.Execute()
 }
-
-var CollectApiPrReviewCommentsMeta = plugin.SubTaskMeta{
-       Name:             "collectApiPrReviewCommentsMeta",
-       EntryPoint:       CollectPrReviewComments,
-       EnabledByDefault: true,
-       Description:      "Collect pr review comments data from Github api, 
supports both timeFilter and diffSync.",
-       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
-}
diff --git a/backend/plugins/github/tasks/pr_review_comment_extractor.go 
b/backend/plugins/github/tasks/pr_review_comment_extractor.go
index fa7181e7c..575d8a8b7 100644
--- a/backend/plugins/github/tasks/pr_review_comment_extractor.go
+++ b/backend/plugins/github/tasks/pr_review_comment_extractor.go
@@ -30,13 +30,21 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiPrReviewCommentsMeta)
+}
+
 var ExtractApiPrReviewCommentsMeta = plugin.SubTaskMeta{
        Name:             "extractApiPrReviewComments",
        EntryPoint:       ExtractApiPrReviewComments,
        EnabledByDefault: true,
        Description: "Extract raw comment data  into tool layer table 
github_pull_request_comments" +
                "and github_issue_comments",
-       DomainTypes: []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{RAW_PR_REVIEW_COMMENTS_TABLE},
+       ProductTables: []string{
+               models.GithubRepoAccount{}.TableName(),
+               models.GithubPrCommit{}.TableName()},
 }
 
 func ExtractApiPrReviewComments(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_review_convertor.go 
b/backend/plugins/github/tasks/pr_review_convertor.go
index f526c1592..280f204a6 100644
--- a/backend/plugins/github/tasks/pr_review_convertor.go
+++ b/backend/plugins/github/tasks/pr_review_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,15 +28,24 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "reflect"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ConvertPullRequestReviewsMeta)
+}
+
 var ConvertPullRequestReviewsMeta = plugin.SubTaskMeta{
        Name:             "convertPullRequestReviews",
        EntryPoint:       ConvertPullRequestReviews,
        EnabledByDefault: true,
        Description:      "ConvertPullRequestReviews data from Github api",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{
+               models.GithubPrReview{}.TableName(),    // cursor and id 
generator
+               models.GithubPullRequest{}.TableName(), // cursor and id 
generator
+               models.GithubAccount{}.TableName(),     // id generator
+               RAW_PR_REVIEW_TABLE},
+       ProductTables: []string{code.PullRequestComment{}.TableName()},
 }
 
 func ConvertPullRequestReviews(taskCtx plugin.SubTaskContext) errors.Error {
diff --git a/backend/plugins/github/tasks/pr_review_extractor.go 
b/backend/plugins/github/tasks/pr_review_extractor.go
index 665ed3a5d..44be34040 100644
--- a/backend/plugins/github/tasks/pr_review_extractor.go
+++ b/backend/plugins/github/tasks/pr_review_extractor.go
@@ -19,19 +19,29 @@ package tasks
 
 import (
        "encoding/json"
+       "strings"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
-       "strings"
 )
 
+func init() {
+       RegisterSubtaskMeta(&ExtractApiPullRequestReviewsMeta)
+}
+
 var ExtractApiPullRequestReviewsMeta = plugin.SubTaskMeta{
        Name:             "extractApiPullRequestReviews",
        EntryPoint:       ExtractApiPullRequestReviews,
        EnabledByDefault: true,
        Description:      "Extract raw PullRequestReviewers data into tool 
layer table github_reviewers",
        DomainTypes:      []string{plugin.DOMAIN_TYPE_CROSS, 
plugin.DOMAIN_TYPE_CODE_REVIEW},
+       DependencyTables: []string{RAW_PR_REVIEW_TABLE},
+       ProductTables: []string{
+               models.GithubRepoAccount{}.TableName(),
+               models.GithubReviewer{}.TableName(),
+               models.GithubPrReview{}.TableName()},
 }
 
 type PullRequestReview struct {
diff --git a/backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go 
b/backend/plugins/github/tasks/register.go
similarity index 83%
rename from backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go
rename to backend/plugins/github/tasks/register.go
index fb4e30b5e..51b12a76c 100644
--- a/backend/helpers/pluginhelper/subtaskmeta_sorter/interface.go
+++ b/backend/plugins/github/tasks/register.go
@@ -15,10 +15,12 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package subtaskmeta_sorter
+package tasks
 
 import "github.com/apache/incubator-devlake/core/plugin"
 
-type SubTaskMetaSorter interface {
-       Sort() ([]plugin.SubTaskMeta, error)
+var SubTaskMetaList []*plugin.SubTaskMeta
+
+func RegisterSubtaskMeta(meta *plugin.SubTaskMeta) {
+       SubTaskMetaList = append(SubTaskMetaList, meta)
 }
diff --git a/backend/plugins/github/tasks/repo_convertor.go 
b/backend/plugins/github/tasks/repo_convertor.go
index 418ef7a00..9afe4665a 100644
--- a/backend/plugins/github/tasks/repo_convertor.go
+++ b/backend/plugins/github/tasks/repo_convertor.go
@@ -34,9 +34,9 @@ import (
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
 
-const RAW_REPOSITORIES_TABLE = "github_api_repositories"
-
-type ApiRepoResponse GithubApiRepo
+func init() {
+       RegisterSubtaskMeta(&ConvertRepoMeta)
+}
 
 type GithubApiRepo struct {
        Name        string `json:"name"`
@@ -56,8 +56,22 @@ var ConvertRepoMeta = plugin.SubTaskMeta{
        Name:             "convertRepo",
        EntryPoint:       ConvertRepo,
        EnabledByDefault: true,
-       Description:      "Convert tool layer table github_repos into  domain 
layer table repos and boards",
-       DomainTypes:      []string{plugin.DOMAIN_TYPE_CODE, 
plugin.DOMAIN_TYPE_TICKET, plugin.DOMAIN_TYPE_CICD, 
plugin.DOMAIN_TYPE_CODE_REVIEW, plugin.DOMAIN_TYPE_CROSS},
+       Description:      "Convert tool layer table github_repos into domain 
layer table repos and boards",
+       DomainTypes: []string{
+               plugin.DOMAIN_TYPE_CODE,
+               plugin.DOMAIN_TYPE_TICKET,
+               plugin.DOMAIN_TYPE_CICD,
+               plugin.DOMAIN_TYPE_CODE_REVIEW,
+               plugin.DOMAIN_TYPE_CROSS},
+       DependencyTables: []string{
+               //models.GithubRepo{}.TableName(), // config will not regard as 
dependency
+               //RAW_REPOSITORIES_TABLE,
+       },
+       ProductTables: []string{
+               code.Repo{}.TableName(),
+               ticket.Board{}.TableName(),
+               crossdomain.BoardRepo{}.TableName(),
+               devops.CicdScope{}.TableName()},
 }
 
 func ConvertRepo(taskCtx plugin.SubTaskContext) errors.Error {
@@ -85,7 +99,7 @@ func ConvertRepo(taskCtx plugin.SubTaskContext) errors.Error {
                                ConnectionId: data.Options.ConnectionId,
                                Name:         data.Options.Name,
                        },
-                       Table: RAW_REPOSITORIES_TABLE,
+                       Table: models.GithubRepo{}.TableName(),
                },
                Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
                        repository := inputRow.(*models.GithubRepo)
diff --git a/backend/plugins/github/tasks/shared.go 
b/backend/plugins/github/tasks/shared.go
index dbde93a93..823b8b2dd 100644
--- a/backend/plugins/github/tasks/shared.go
+++ b/backend/plugins/github/tasks/shared.go
@@ -42,3 +42,10 @@ func ignoreHTTPStatus404(res *http.Response) errors.Error {
        }
        return nil
 }
+
+func ignoreHTTPStatus422(res *http.Response) errors.Error {
+       if res.StatusCode == http.StatusUnprocessableEntity {
+               return api.ErrIgnoreAndContinue
+       }
+       return nil
+}
diff --git a/backend/plugins/github/tasks/task_data.go 
b/backend/plugins/github/tasks/task_data.go
index ac8d24a90..8dcc777f8 100644
--- a/backend/plugins/github/tasks/task_data.go
+++ b/backend/plugins/github/tasks/task_data.go
@@ -23,7 +23,6 @@ import (
        "time"
 
        "github.com/apache/incubator-devlake/core/errors"
-
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/github/models"
 )
diff --git a/backend/plugins/gitlab/impl/impl.go 
b/backend/plugins/gitlab/impl/impl.go
index 835020c24..7053f7b19 100644
--- a/backend/plugins/gitlab/impl/impl.go
+++ b/backend/plugins/gitlab/impl/impl.go
@@ -19,7 +19,7 @@ package impl
 
 import (
        "fmt"
-       
"github.com/apache/incubator-devlake/helpers/pluginhelper/subtaskmeta_sorter"
+       
"github.com/apache/incubator-devlake/helpers/pluginhelper/subtaskmeta/sorter"
        "time"
 
        "github.com/apache/incubator-devlake/core/context"
@@ -49,7 +49,7 @@ type Gitlab string
 
 func init() {
        // check subtask meta loop when init subtask meta
-       if _, err := 
subtaskmeta_sorter.NewDependencySorter(tasks.SubTaskMetaList).Sort(); err != 
nil {
+       if _, err := sorter.NewDependencySorter(tasks.SubTaskMetaList).Sort(); 
err != nil {
                panic(err)
        }
 }
@@ -109,7 +109,7 @@ func (p Gitlab) Name() string {
 }
 
 func (p Gitlab) SubTaskMetas() []plugin.SubTaskMeta {
-       list, err := 
subtaskmeta_sorter.NewDependencySorter(tasks.SubTaskMetaList).Sort()
+       list, err := sorter.NewDependencySorter(tasks.SubTaskMetaList).Sort()
        if err != nil {
                panic(err)
        }


Reply via email to