summaryrefslogtreecommitdiff
path: root/util/dependency_updater/README.md
blob: 70c8a733cb3c67ef7e6ebf09d52dff874c26804f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# Dependency Update Utility

### What is this for?

*This utility replaces the script at qt/qtqa/src/qtmoduleupdater*

Every submodule of qt6 includes a "dependencies.yaml" file which
specifies a revision of the other modules it directly depends on.
If checked out together, the given module should be guaranteed to
build and pass tests. In this model, a "dependency tree" forms,
where "leaf" modules depend on "trunk" or "root" modules.

In order to update a leaf module's dependencies, the tree must be
walked down to the root-most module it depends on which itself
has no dependencies. Then, starting from the root (often qtbase),
each module's dependencies.yaml file is updated with the latest SHA.
When a trunk module passes CI after being updated with new root SHAs,
leaf modules can be updated with the latest trunk SHAs.

### How does it work?
This script/bot takes a list of repositories, either explicitly or
gathered from qt5.git's .gitmodules file, and discovers the
dependency tree by cross-checking every module's dependencies.yaml file.
During this process, it collects the latest SHAs for each module, and
also checks for modules which need to be updated due to inconsistent
SHAs across modules for the same dependency. **Further, if a dependency
is discovered that was not explicitly passed to the script, it is
added to internal memory and will be updated as well if necessary.**

The script provides a number of modes of operation. See the script's
usage manual below.

In typical usage, the script will need to be run multiple times to
complete a round, as it creates change requests on
codereview.qt-project.org which must be integrated. When a tracked
change is integrated, the script should be re-run to progress the
update round. When all required module changes are successfully
integrated, if run with options to do so, the script will perform
an update to qt5.git and/or yocto/meta-qt6 with the updated submodule
SHAs collected during the round. When a round completes, the state
is cleared and a new round will begin when the script is run again.

Note: It is safe to run the script on a timer/trigger. Since the script
keeps track of actions it is taking, it should never duplicate work
if a dependency update is in-progress. If there is no work to be done,
such a message will be printed and the script will exit.

### Usage
A number of python modules are required. Usage of a Python virtual
environment such as pipenv is strongly suggested.

```
pipenv install
pipenv run python3 main.py [args]
```

See the script's --help output for a full list of options. The
example scenarios below cover some of the most commonly used situations.

Note that most scenarios require the utility to be run multiple times
to complete an update "round" since multiple modules in the dependency
tree often need to be updated separately, and in a specific order.

1. Clean and clear the utility's state and forget previous runs for a branch.
   1. `python3 main.py --branch dev --reset`
   2. This action clears state for the specified branch. You should always
      clear the working state before starting a fresh round/operation.
   3. Note that simply deleting the local __state/state.bin_ file will
      not necessarily clear the state. This utility also stores state
      data in codereview personal branches if ssh credentials are configured
      in ~/.ssh/config. Always run the utility with `--reset` to clear state.

2. Run the utility to perform a one-time sync of a module with qt/qt5.git
   submodule SHAs.
   1. `python3 main.py --branch dev --noState --repos
       qt-extensions/qthttpserver`
   2. This creates a new change for `qt-extensions/qthttpserver` in
      codereview which needs to go through the normal review process.
      The dependencies.yaml file is updated to the latest SHAs in qt5.git
   3. This scenario does not usually require more than a single run of
      the tool. As such, `--noState` can be used to avoid the need to
      reset the utility before running. This also prevents interference to
      any ongoing rounds since no state data is written.

3. Run the utility to simulate an action
   1. `python3 main.py --sim --branch dev --repos qt-extensions/qthttpserver`
   2. Performing a dry-run prints to the console which actions the script
      would take. No actions are actually performed on gerrit, and no
      changes will be created. Further, using --sim will never update the
      local persistent state of the tool.

4. Perform an update to one or more repos using the latest available
   SHAs from each dependency's own repo.
   1. `python3 main.py --head --branch dev --repos qt-extensions/qthttpserver`
   2. This assembles a full dependency map for the given repo(s) and updates
      each one, starting from the most base module.
   3. Performing an update with `--head` should be used with a clean state.
   4. When the first updates are merged, run the utility again with the same
      arguments to continue the "round".
   5. Continue to run the utility repeatedly to progress the round until
      the target module is updated with new SHAs for its dependencies.

5. Perform an update for one or more modules, then update qt/qt5.git and
   yocto/meta-qt6 with the new module SHA(s).
   1. `python3 main.py --head --branch dev --qt5Update --yoctoUpdate --repos
       qt-extensions/qthttpserver`
   2. Performs as (4) above, but when the target module(s) have been updated,
      a further run of the utility will update qt/qt5.git and/or
      yocto/meta-qt6.git with the merged SHAs of the target modules. Only
      modules which already exist in the super-repos will be updated.

6. Rewind an ongoing round to a specific dependency to pull in additional
   changes and continue the update round.
   1. `python3 main.py --head --branch dev --rewind qt/qtdeclarative --repos
       qt-extensions/qthttpserver`
   2. In this example, if the round has already successfully merged an update
      for _qt/qtdeclarative_ but _qt/qtwebsockets_ is broken until a further
      fix can be picked up in _qt/qtdeclarative_, using
      `--rewind qt/qtdeclarative` will clear the state for modules which
      depend on _qt/qtdeclarative_ and pull the new head of _qt/qtdeclarative_,
      then continue from there.

7. Remove a dependency from a module
   1. `python3 main.py --head --branch dev --dropDependency
       qt/qtsvg:qt-extensions/qthttpserver --repos qt-extensions/qthttpserver`
   2. If a repo no longer needs a dependency, it can be removed in this way.
   3. Combine `--dropDependency` with `--qt5Update` to remove a dependency
      from a module and ensure that qt/qt5.git's .gitmodules reference file
      also gets updated appropriately.
   4. _**Note:**_ This action is destructive! See the `--help` output for
      important information and detailed usage instructions

8. Update all current modules in qt/qt5.git
   1. `python3 main.py --default-repos --branch dev --qt5Update --yoctoUpdate`
   2. This collects a list of all modules in qt5 marked as 'essential',
      'addon', 'deprecated', 'ignore', or 'preview' in the _.gitmodules_
      file of _qt/qt5.git_ and updates the dependency tree to the latest
      branch HEAD of each module.
   3. When finished, qt/qt5.git and yocto/meta-qt6 are updated with the
      SHAs of all modules updated in the round

9. Include in a round additional repos/modules which should be considered
   "non-blocking" by the utility.
    1. `python3 main.py --default-repos --branch dev --qt5Update --yoctoUpdate
        --nonBlockingRepos qt-extensions/qthttpserver`
       1. This will perform an update round as requested, but if a non-blocking
          module update fails, it will be ignored for the rest of the round,
          allowing all other repos to continue normally.
       2. If any repos specified by `--repos` or gathered automatically by
          `--default-repos` require a repo specified as non-blocking, that
          non-blocking repo will be converted to a blocking-status since
          failure in it would lead to the failure of a normally blocking
          module.

11. Auto-approve and stage module updates
    1. `python3 main.py --stage --default-repos --branch dev --qt5Update
        --yoctoUpdate`
    2. If the user running the tool has provided codereview credentials which
       have Approver and QtStage rights access, the utility will self-approve
       created changes and automatically stage them.
    3. **_Note:_** Use of this option is generally discouraged outside of
       automation.


### Further Notes
- The script may take some time to run, depending on internet speed,
  responsiveness of Gerrit, and how many repositories it is updating.

- Unless run with `--noState`, a run of the script will attempt to use
  saved information about repositories and their dependencies/SHAs.
  If you experience trouble when trying to start a round, try running
  the script with --reset to clear data for the target branch before
  continuing.

- Use of `--noState` is intended to allow the user to perform atomic
  operations without the need to reset or save state, potentially
  contaminating an ongoing round for a given branch. Generally,
  this would be used when syncing a repo's dependencies.yaml
  with qt5.git's current submodule SHAs, as this usually does not
  require more than one run of the script.

- `--sweepChanges` will sweep which have the script's GERRIT_USERNAME
  as a reviewer on the change. In practice, this option is usually
  reserved for the Qt Submodule Update bot, but can be enabled ***if you
  know what you are doing.***

- Repo prefixes are fuzzy. The script defaults to "qt/", but in theory
  any prefix can be used. If you set the prefix to "playground/", the
  script will prefer dependencies which exist there. If a dependency
  cannot be found in the preferred namespace, a fuzzy-match search is
  performed and the best match is attempted. The user will be notified
  of any fuzzy-match repo selections which are made.

- Running the utility at-will during an ongoing round is a safe operation.
  The utility will simply update its internal state of ongoing changes
  in codereview and exit if no further action can be taken.