|
| 1 | +# Proposal: all, x/build/cmd/relui: automate go directive maintenance in golang.org/x repositories |
| 2 | + |
| 3 | +Author(s): Dmitri Shuralyov |
| 4 | +Thanks to: Russ Cox, Michael Pratt, Robert Findley, Hana Kim, Cody Oss, Tim King, Carlos Amedee, and others for input |
| 5 | + |
| 6 | +Last updated: 2024-08-27 |
| 7 | + |
| 8 | +Discussion at https://go.dev/issue/69095. |
| 9 | + |
| 10 | +## Abstract |
| 11 | + |
| 12 | +The value of the `go` directive in golang.org/x repositories |
| 13 | +is automatically maintained |
| 14 | +to be at least 1.(N-1).0, |
| 15 | +where Go 1.N is the most recent major Go release, |
| 16 | +and Go 1.(N-1) is the previous major Go release. |
| 17 | + |
| 18 | +## Background |
| 19 | + |
| 20 | +In the beginning, there was the GOPATH mode and versions of dependencies |
| 21 | +of golang.org/x repositories weren't explicitly tracked. |
| 22 | +Go 1.11 introduced the module mode, and over time it became the default mode. |
| 23 | +All golang.org/x repositories had an initial go.mod file checked in, and |
| 24 | +that file was maintained manually. |
| 25 | +This meant that a bug fix or a new feature in one golang.org/x repository |
| 26 | +didn't benefit another golang.org/x repository until someone chose to manually |
| 27 | +update that dependency. |
| 28 | +It also meant that eventual updates sometimes jumped many versions at once |
| 29 | +to catch up. |
| 30 | +This was resolved in 2022, when an automated monthly relui workflow began to |
| 31 | +create tags and update golang.org/x dependencies across all golang.org/x |
| 32 | +repositories ([issue 48523](https://go.dev/issue/48523)). |
| 33 | + |
| 34 | +At this point there are upwards of 35 [golang.org/x](https://golang.org/x) |
| 35 | +repositories. |
| 36 | +Owners of each repository update the "go" directive manually, ad-hoc, |
| 37 | +so golang.org/x repositories may receive different levels of "go" directive |
| 38 | +maintenance. |
| 39 | +For example, owners of the golang.org/x/mod module wished to use the |
| 40 | +new-to-Go-1.22 `go/version` package as soon as Go 1.23 came out, and |
| 41 | +so its "go" directive was recently updated to "1.22.0". |
| 42 | +On the other hand, golang.org/x/image hasn't been updated in a while, and |
| 43 | +its "go" directive is currently still at "1.18", |
| 44 | +which itself was an upgrade from "1.12" in [CL 526895](https://go.dev/cl/526895) |
| 45 | +as part of bringing all golang.org/x repos to use at minimum Go 1.18 language |
| 46 | +version ([issue 60268](https://go.dev/issue/48523)). |
| 47 | + |
| 48 | +Leaving go directive maintenance to be done entirely manually creates the |
| 49 | +possibility of some repositories staying on an older Go language version longer. |
| 50 | +When there's enough of a need to finally upgrade it to a recent Go language |
| 51 | +version, this requires a change across multiple major Go releases at once, |
| 52 | +which can be harder to review. |
| 53 | +Having continuous, smaller incremental upgrades requires creating many CLs for |
| 54 | +all of golang.org/x repositories every 6 months, which is toilsome if always |
| 55 | +done manually. |
| 56 | + |
| 57 | +## Proposal |
| 58 | + |
| 59 | +I propose that each time that a new major Go release 1.N.0 is made, |
| 60 | +the `go` directive in all golang.org/x repos will be upgraded to `go 1.(N-1).0`. |
| 61 | +For example, |
| 62 | +when Go 1.28.0 is released, |
| 63 | +golang.org/x modules would have their `go` directive set to `go 1.27.0`. |
| 64 | + |
| 65 | +This would be done automatically as part of a relui release workflow, |
| 66 | +which will generate CLs by running the following sequence at the module root |
| 67 | +of applicable repositories: |
| 68 | + |
| 69 | +``` |
| 70 | +go get go@1.(N-1).0 |
| 71 | +go mod tidy |
| 72 | +go fix ./... |
| 73 | +``` |
| 74 | + |
| 75 | +Using the go command at version `go1.N.0`. |
| 76 | + |
| 77 | +Modules whose `go` directive at the time is already a higher version will be |
| 78 | +skipped rather than downgraded. |
| 79 | + |
| 80 | +If a `toolchain` directive is present and higher than the new go directive, |
| 81 | +it will be kept as is. |
| 82 | +(The go command does this automatically while updating the go line.) |
| 83 | +If a `toolchain` directive isn't present, |
| 84 | +these automated CLs will not try to introduce it. |
| 85 | + |
| 86 | +The first two commands in the sequence leave the module in a tidy state. |
| 87 | +The `go fix ./...` command will apply high-confidence automated changes, |
| 88 | +in case any begin to apply with the updated Go language version. |
| 89 | +For example, go fix began to remove the now-obsolete |
| 90 | +[`// +build` lines](https://go.dev/doc/go1.18#go-build-lines) once a module |
| 91 | +is upgraded to 1.18 or later. |
| 92 | +For many new language versions this will be a no-op, but it is expected |
| 93 | +that including a `go fix ./...` invocation will be a net positive. |
| 94 | +We can decide to stop including it in the generated CLs based on experience. |
| 95 | + |
| 96 | +If a `go.work` file is checked in (rare case), then `go work sync` will also |
| 97 | +be run to sync the workspace's build list back to the workspace's modules. |
| 98 | + |
| 99 | +## Rationale |
| 100 | + |
| 101 | +### Why 1.(N-1).0? |
| 102 | + |
| 103 | +N-1 is chosen to align with the |
| 104 | +[Go release policy](https://go.dev/doc/devel/release#policy). |
| 105 | +The Go release policy states that a given major Go release is supported |
| 106 | +until there are two newer major releases. |
| 107 | + |
| 108 | +Picking N-1 makes this a no-op for golang.org/x module users who are using |
| 109 | +a supported Go release. If a user is using a pre-release version of the |
| 110 | +previous (also supported) major Go release, they'll be upgraded to |
| 111 | +the stable major release (e.g., `go1.22rc1` to `go1.22.0`). |
| 112 | +For golang.org/x module authors, raising the go directive from a lower value |
| 113 | +to N-1 enables taking advantage of newer language features and fixes potentially |
| 114 | +sooner than if no one got to updating the module's language version manually. |
| 115 | + |
| 116 | +### Why not 1.(N-0).0? |
| 117 | + |
| 118 | +N-0 would get in the way of one's ability to use the latest versions of |
| 119 | +golang.org/x modules with all supported Go releases. |
| 120 | +It would be possible to use the latest major Go release, |
| 121 | +but not the previous (still supported) major Go release, |
| 122 | +at least not without triggering a toolchain upgrade to a newer major Go release. |
| 123 | +The Go release policy states we support both releases equally, |
| 124 | +and issue bug fixes and security fixes to both, |
| 125 | +so this proposal preserves that equality. |
| 126 | + |
| 127 | +### Why not N-2 (or N-3, or N-4, and so on)? |
| 128 | + |
| 129 | +Using older versions gives the impression that those releases |
| 130 | +are still supported, |
| 131 | +but they are not. |
| 132 | + |
| 133 | +### Why not bump on each minor release? |
| 134 | + |
| 135 | +Another option would be to always use the latest 1.(N-1).X, |
| 136 | +updating all the x repos each time a new minor Go release comes out. |
| 137 | +That forces everyone to update to that new minor release |
| 138 | +in order to incorporate any new x repo changes, |
| 139 | +which seems too aggressive. |
| 140 | +As much as we try to avoid it, minor Go releases do sometimes contain bugs, |
| 141 | +and it should be possible to choose to use older ones if needed. |
| 142 | + |
| 143 | +### Why not bump the toolchain lines too? |
| 144 | + |
| 145 | +The `toolchain` line can only be set to a toolchain the same or newer than |
| 146 | +the `go` line, and it only affects people working in the repo itself. |
| 147 | +That is, it does not affect users of the x repos. |
| 148 | +Therefore it is not as important. |
| 149 | +Just as we want to allow users to use the x repos with any supported Go version, |
| 150 | +we want to allow users to work in the x repos with any supported Go version, so |
| 151 | +leaving the toolchain lines implied by the go line seems like the right choice. |
| 152 | + |
| 153 | +### Future work |
| 154 | + |
| 155 | +There are aspects of this work that have been considered but chosen to be left |
| 156 | +out of scope for the initial version. |
| 157 | +We may want to refine some of the smaller details down the road, |
| 158 | +especially once there's more experience with the proposed mechanism. |
| 159 | + |
| 160 | +#### Nested modules |
| 161 | + |
| 162 | +Nested modules are not in scope of the current tagging, |
| 163 | +and not in scope of the initial go directive maintenance either. |
| 164 | +There are fewer of them, and they often have custom constraints |
| 165 | +or release processes. |
| 166 | +They can be left to be managed by their corresponding repo owners for now. |
| 167 | +This can be revisited in the future, when it's more worthwhile. |
| 168 | + |
| 169 | +## Compatibility |
| 170 | + |
| 171 | +This proposal takes the [Go 1 Compatibility Promise](https://go.dev/doc/go1compat) |
| 172 | +and the [Go Release Policy](https://go.dev/doc/devel/release#policy) into account, |
| 173 | +and does not introduce compatibility problems. |
| 174 | + |
| 175 | +## Implementation |
| 176 | + |
| 177 | +This will be implemented as part of [relui](https://golang.org/x/build/cmd/relui), |
| 178 | +a service already responsible for Go release automation and |
| 179 | +monthly golang.org/x repository tagging. |
0 commit comments