summaryrefslogtreecommitdiffstats
path: root/src/qtmoduleupdater/moduleupdatebatch.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/qtmoduleupdater/moduleupdatebatch.go')
-rw-r--r--src/qtmoduleupdater/moduleupdatebatch.go227
1 files changed, 227 insertions, 0 deletions
diff --git a/src/qtmoduleupdater/moduleupdatebatch.go b/src/qtmoduleupdater/moduleupdatebatch.go
new file mode 100644
index 00000000..b79949cb
--- /dev/null
+++ b/src/qtmoduleupdater/moduleupdatebatch.go
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the repo tools module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "os"
+ "strings"
+)
+
+// PendingUpdate describes that a module needs an updated dependencies.yaml and we are waiting for the change
+// to succeed/fail
+type PendingUpdate struct {
+ Module *Module
+ ChangeID string
+}
+
+// ModuleUpdateBatch is used to serialize and de-serialize the module updating state, used for debugging.
+type ModuleUpdateBatch struct {
+ Product string
+ Branch string
+ Todo map[string]*Module
+ Done map[string]*Module
+ Pending []*PendingUpdate
+ FailedModuleCount int
+}
+
+func (batch *ModuleUpdateBatch) scheduleUpdates(pushUserName string, manualStage bool) error {
+ for _, moduleToUpdate := range batch.Todo {
+ update, err := moduleToUpdate.updateDependenciesForModule(batch.Done)
+ if err != nil {
+ return fmt.Errorf("fatal error proposing module update: %s", err)
+ }
+ log.Printf("Attempting update for module %s resulted in %v\n", moduleToUpdate.RepoPath, update.result)
+ if update.result == DependenciesUpdateContentUpToDate {
+ batch.Done[moduleToUpdate.RepoPath] = moduleToUpdate
+ delete(batch.Todo, moduleToUpdate.RepoPath)
+ } else if update.result == DependenciesUpdateDependencyMissing {
+ // Nothing to be done, we are waiting for indirect dependencies
+ } else if update.result == DependenciesUpdateUpdateScheduled {
+ // push and stage
+ if err = pushAndStageChange(moduleToUpdate.RepoPath, moduleToUpdate.Branch, update.commitID, update.summary, pushUserName, manualStage); err != nil {
+ return fmt.Errorf("error pushing change upate: %s", err)
+ }
+ batch.Pending = append(batch.Pending, &PendingUpdate{moduleToUpdate, update.changeID})
+ delete(batch.Todo, moduleToUpdate.RepoPath)
+ } else {
+ return fmt.Errorf("invalid state returned by updateDependenciesForModule for %s", moduleToUpdate.RepoPath)
+ }
+ }
+
+ return nil
+}
+
+func removeAllDirectAndIndirectDependencies(allModules *map[string]*Module, moduleToRemove string) {
+ for moduleName, module := range *allModules {
+ if module.hasDependency(moduleToRemove) {
+ delete(*allModules, moduleName)
+ removeAllDirectAndIndirectDependencies(allModules, module.RepoPath)
+ }
+ }
+}
+
+func (batch *ModuleUpdateBatch) checkPendingModules() {
+ var newPending []*PendingUpdate
+ for _, pendingUpdate := range batch.Pending {
+ module := pendingUpdate.Module
+ status, err := getGerritChangeStatus(module.RepoPath, module.Branch, pendingUpdate.ChangeID)
+ if err != nil || status == "STAGED" || status == "INTEGRATING" || status == "STAGING" {
+ // no change yet
+ newPending = append(newPending, pendingUpdate)
+ continue
+ } else if status == "MERGED" {
+ module.refreshTip()
+ batch.Done[module.RepoPath] = module
+ } else {
+ // Open or abandoned, not sure -- either way an error integrating the update
+ removeAllDirectAndIndirectDependencies(&batch.Todo, module.RepoPath)
+ batch.FailedModuleCount++
+ }
+ }
+ batch.Pending = newPending
+}
+
+func loadTodoAndDoneModuleMapFromSubModules(branch string, submodules map[string]*submodule) (todo map[string]*Module, done map[string]*Module, err error) {
+ todoModules := make(map[string]*Module)
+ doneModules := make(map[string]*Module)
+
+ for name, submodule := range submodules {
+ // Erase modules that don't follow the qt5 branching scheme and don't need
+ // dependencies.yaml
+ if submodule.branch == "master" {
+ continue
+ }
+
+ module, err := NewModule(name, branch, submodules)
+ if err != nil {
+ return nil, nil, fmt.Errorf("could not create internal module structure: %s", err)
+ }
+
+ if submodule.repoType == "inherited" || name == "qt/qtbase" {
+ doneModules[module.RepoPath] = module
+ } else {
+ todoModules[module.RepoPath] = module
+ }
+ }
+
+ return todoModules, doneModules, nil
+}
+
+func (batch *ModuleUpdateBatch) loadTodoList(qt5FetchRef string) error {
+ qt5Modules, err := getQt5ProductModules(batch.Product, batch.Branch, qt5FetchRef)
+ if err != nil {
+ return fmt.Errorf("Error listing qt5 product modules: %s", err)
+ }
+
+ batch.Todo, batch.Done, err = loadTodoAndDoneModuleMapFromSubModules(batch.Branch, qt5Modules)
+ return err
+}
+
+func sanitizeBranchOrRepo(s string) string {
+ s = strings.ToLower(s)
+ s = strings.ReplaceAll(s, "/", "_")
+ s = strings.ReplaceAll(s, "-", "_")
+ return s
+}
+
+func (batch *ModuleUpdateBatch) stateFileName() string {
+ return fmt.Sprintf("state_%s_%s.json", sanitizeBranchOrRepo(batch.Product), sanitizeBranchOrRepo(batch.Branch))
+}
+
+func (batch *ModuleUpdateBatch) saveState() error {
+ fileName := batch.stateFileName()
+ outputFile, err := os.Create(fileName)
+ if err != nil {
+ return fmt.Errorf("failed to create state file %s: %s", fileName, err)
+ }
+ defer outputFile.Close()
+
+ encoder := json.NewEncoder(outputFile)
+ encoder.SetIndent("", " ")
+ return encoder.Encode(batch)
+}
+
+func (batch *ModuleUpdateBatch) loadState() error {
+ fileName := batch.stateFileName()
+ inputFile, err := os.Open(fileName)
+ if err != nil {
+ return err
+ }
+ defer inputFile.Close()
+
+ decoder := json.NewDecoder(inputFile)
+ err = decoder.Decode(batch)
+ if err != nil {
+ return fmt.Errorf("Error decoding JSON state file: %s", err)
+ }
+ return nil
+}
+
+func (batch *ModuleUpdateBatch) isDone() bool {
+ return len(batch.Todo) == 0 && len(batch.Pending) == 0
+}
+
+func (batch *ModuleUpdateBatch) PrintSummary() {
+ fmt.Fprintf(os.Stdout, "Summary of git repository dependency update for target branch %s based off of %s\n", batch.Branch, batch.Product)
+
+ if batch.isDone() {
+ if batch.FailedModuleCount > 0 {
+ fmt.Fprintf(os.Stdout, " %v modules failed to be updated. Check Gerrit for the %s branch\n", batch.FailedModuleCount, batch.Branch)
+ } else {
+ fmt.Fprintf(os.Stdout, " No updates are necessary for any modules - everything is up-to-date\n")
+ }
+ return
+ }
+
+ if len(batch.Done) > 0 {
+ fmt.Fprintf(os.Stdout, "The following modules have been brought up-to-date:\n")
+
+ for name := range batch.Done {
+ fmt.Println(" " + name)
+ }
+ }
+
+ if len(batch.Pending) > 0 {
+ fmt.Fprintf(os.Stdout, "The following modules are current in-progress:\n")
+
+ for _, pending := range batch.Pending {
+ fmt.Println(" " + pending.Module.RepoPath)
+ }
+ }
+
+ fmt.Fprintf(os.Stdout, "The following modules are outdated and are either waiting for one of their dependencies or are ready for an update:\n")
+ for name := range batch.Todo {
+ fmt.Println(" " + name)
+ }
+
+ fmt.Println()
+ fmt.Println()
+}