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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
|
# Governance thin-edge.io
This documents serves as a guiding document to ease technical decision-making in the project as a whole.
Some of its goals are:
- Making sure that the different stakeholders are represented with their requirements
- Making sure that contributors know how decisions are made and who they can address concerns/questions to
- Making sure that team members know what is required of them for the project
## The project vision
In the [vision.md](./vision.md) the goal of the project is encoded.
It specifies the different functional requirements and non-functional requirements of the project as a whole.
All the stakeholders must agree on the vision upon joining.
- The current stakeholders are: Software AG, and IFM
Changes to the vision need to be done in a plenum of stakeholders, this makes sure that everyone is aware and agrees to the evolution of the project.
## Team Structure
* Teams are either single contributors or a group of them
* They are responsible for a specific part of project, and as such are the maintainers of those parts
* This includes: Reviewing pull requests, triaging issues in their assigned area, and general maintenance
* Each team is assigned a single or multiple subtrees of the project, as defined by the project structure
* The teams follow the project hierarchy for decisions, i.e. higher decisions take precedence
The important bits:
At the 'top' are the maintainers, whose job is to define and realize the project vision of thin-edge.io.
Maintainers should strive for agreement based on the following factors:
- The vision of the whole project
- Feasibility, accounting for future technical and cultural shifts
- The health of the project going forward
Overall, the maintainers should not be beholden to their respective companies, and instead to the health of the project and its community.
Underneath the leads are the teams or individuals, that each are allowed to make similar decisions about the project *in the area they have been delegated*.
- Some decisions are global in the project, like the core/API
- Team members should try to make sure to explore possible options together before calling for time-consuming solutions like a vote. This mostly includes uncontroversial changes that are low impact or have already been agreed upon
- If an exclusive choice has to be made (where it is not possible to entertain two conflicting approaches), and no clear side is more advantageous to pick, a vote should be held with the majority opinion being the final decision.
- Some decisions are local, e.g. a plugin does not impact others with its behaviour, however it still needs to be a 'good citizen' in the project (CI, format, etc..)
- These should be clearly scoped in their respective project part
- However, if needed a 'higher up' decision can be requested if no consensus is achieved
## Project Structure
The project has a hierarchy to solve these two problems:
1. How can users make sure that their voice and interests are represented in the project?
- Each member party appoints one core team member
- They form the group of people that have to, and are responsible for, signing off on the technical implementation of the project
2. How to make the lives of these team members easier?
-----
* Thin-edge is organized around sub-projects - to ease decision taking.
* The sub-projects are organized in a hierarchy
* This makes it easier to decouple different parts of the project
* This makes it easier to decide on technical questions
* Everyone has to agree on the core project as it is the foundations to all others.
* At the periphery, sub-projects might be related to specific eco-systems (e.g. Cumulocity or Moneo) and therefore have independent decision processes.
* At an intermediate level, one might have sub-projects related to specific use-cases like “JSON over MQTT”.
* All the sub-projects share a common repository - to ease consistency across projects and over time.
* Labels are used to organize issues/PRs/discussions along sub-projects.
* Code ownership is used to enforce cooperation around key components, but the default is to let things open and to trust each other, using version control as a safety net.
```
core contributors = [SAG, IFM]
-> Project leads, one each from [core contributors]
|
\- Responsible for `/*`, aka everything in the repository
|
|
\
\-> Delegated project Teams, any combination of [teams, individual contributors]
|
|\- Responsible for `/plugins/plugin_{foo,bar}`
||- Are trusted by the project leads (who would have to stand-in for the trusted members)
|
|-> Can be needed to nest further if the project grows bigger
```
## Repository maintenance
To assure a consistent level of quality and appearance, the project has some rules for maintainers to follow:
- **Clear commits**
Having clear, concise and well-written commit messages for all changes is not
only a indicator for the seriousness of the project, but also a way to ensure
sustainable development via repeatability of the sources of the project.
Because of this, we try to ensure the highest possible quality for each
individual commit message.
Because such a thing cannot necessarily or in full be enforced via tooling, it
remains mainly the obligation of a reviewer of a change-set to ensure
* Exclusiveness of the individual commits
* that one commit is one atomic change, and not an accumulation of several
individual and separate changes
* that the commit message of the change expresses why the change was made
Every reviewer of a pull request is asked to review not only the changes, but
also the commit messages.
Reviewers are explicitly empowered and actually encouraged to block pull
requests that contain commit messages that do not meet a certain standard
even and especially also if the proposed changes are acknowledged.
The contributor should document why they implemented a change properly. A good
rule of thumb is that if a change is larger than 60 lines, a commit message
without a body won't suffice anymore.
The more lines are touched by an individual commit, the more lines should be
used to explain the change.
The project implements lints using github actions to meet such criteria.
Also, some hard criteria is checked via github action lints:
* The `Signed-off-by` trailer lint must be present in all commits (also see
section "Ensuring the Signed-off-by-trailer")
* The commit body must meet the default formatting of a commit message, that
is
* Subject line not longer than 50 characters
* Subject capitalized
* No period at the end of the subject
* Imperative mood in the subject
* Second line empty
* Body lines not longer than 72 characters
Commits using the `--fixup` or `--squash` feature of `git-commit` are allowed,
but won't be merged (read more about this in the "merge strategies" section).
- **Ensuring the Signed-off-by-trailer**
Contributors need to sign the "Contributor License Agreement". They do so by
signing off each individual commit with a so-called "Signed-off-by Trailer".
Git offers the `--signoff`/`-s` flags when using `git-commit` to commit
changes.
To ensure that all commits have the trailer present in the commit message, a
CI lint is installed in the github actions workflows of the project. This lint
blocks pull requests to be merged if one or more of the commits of the pull
request misses the `Signed-off-by` trailer.
As a result, it is not possible to merge pull requests that miss the trailer.
- **Coding styleguide**
Coding style is enforced via `rustfmt` in its default configuration.
Compliance with the coding style is enforced via CI.
- **Testing**
Testing is done via workflows in github actions using `cargo test --all-features` for all
crates.
The main objective with this is that a developer should be able to simply run
`cargo test` on their local machine to be able to see whether CI would succeed
for changes they submit in a pull request.
Thus, all CI tests (unit tests, but also integration tests) are implemented in
Rust and do not rely on external services.
End-to-end system tests - that depend on external services - are run outside the CI pipeline,
to avoid inconsistent test outcomes because of external issues.
- **Benchmarks**
- **Documentation builds**
Source code documentation as well as other documentation is tested via github
actions workflows as well, to ensure that a developer is able to build all
relevant documentation on their local machine.
- **Keeping spec up to date**
- **Evergreen master**
The project pursues the "evergreen master" strategy. That means that at every
point in time, the commit that `master`/`main` points to must successfully
build and pass all tests successfully.
Reaching that goal is possible because of the employed merge strategies (read
below).
- **Merge strategies**
Merging is the way how the project accepts and implements changes.
Because of the pursued "everygreen master", merging is implemented in a
certain way that forbids several bad practices which could lead to a breaking
build on the `master`/`main` branch, and a special workflow is implemented to
ensure not only the "evergreen master" but also to streamline the workflow for
integrating pull requests.
The following actions are **not** allowed:
* Committing to `master`/`main` directly
* Squash-Merging of pull requests (which is equivalent to committing to
`master`/`main` directly).
The github repository is configured to not allow squash merges.
* Merging of "!fixup" or "!squash" commits. A github actions
job is employed to block pull requests from being merged if such commits are
in the PR branch. Rebase should be used to clean those up.
It is explicitly _not_ forbidden to have merge-commits in a pull request.
Long-running pull requests that contain a large number of changes and are
developed over a long period might contain merges. They might even merge
`master`/`main` to get up-to-date with the latest developments. Though it is
not encouraged to have such long-running pull requests and discouraged to
merge `master`/`main` into a PR branch, the project acknowledges that
sometimes it is not avoidable.
To summarize, the following requirements have to be met before a pull request
can be merged:
* Reviews of the relevant persons (ensured via github setting)
* Status checks (CI, lints, etc) (ensured via github setting)
* builds, tests, lints are green (ensured via github action)
* Commit linting (ensured via github action)
* No missing `Signed-off-by` lines (ensured via github action)
* No "!fixup"/"!squash" commits in the pull request (ensured via github
action)
Merging itself is implemented via a "merge bot": [bors-ng](https://bors.tech).
bors-ng is used to prevent "merge skew" or "semantic merge conflicts"
(read more [here](https://bors.tech/essay/2017/02/02/pitch/)).
- **Dependency updates**
Dependencies should be kept in sync over all crates in the project. That means
that different crates of the project should try to use dependencies in the
same versions, but also that dependencies should be harmonized in a way that a
specific problem should not be solved with more than one external library at a
time.
Updates of dependencies is automated via a github bot
([dependabot](https://github.com/dependabot)).
To ensure harmonization of dependencies, a dedicated team (see "Team
Structure") is responsible for keeping an eye on the list of dependencies.
- **License linting**
License linting describes the act of checking the licenses of dependencies and
whether they meet a certain criteria.
For example, it is not feasible to import an external library that is licensed
as GPL-3.0 in an Apache-2.0 licensed codebase.
Because of this, a github action is installed to lint the licenses of
dependencies. This action runs as a normal lint (see "evergreen master") and
blocks pull requests if dependencies get imported that do not meet a set of
rules agreed upon by the project.
## Release workflow
The following describes how thin-edge.io is released.
The following chapters do not talk about major releases (increasing "x" in a
"x.y.z" version number) as there is no workflow for these implemented yet.
### Preface
The following chapter describes the release process of the thin-edge.io project
and the corrosponding crates.
The goal of the workflow described in this chapter is to have _as little impact
as possible_ on the general development of the project.
Releases should _never_ stop, prevent or even just slow down the normal
development of the project.
For example, halting development on the `main` branch and forbidding
contributors and maintainers to merge changes to it even for as little as one
hour just so that a new release can be published is not acceptable.
Also should "patch" releases (sometimes called "bugfix releases") have no impact
on the project pace or even concern developers - they should just happen in
parallel without any influence on the normal development activities.
With these requirements, we implement the following workflow.
### Semver conformity and API coverage
We adhere to [semver](https://semver.org),
with the [exceptions implied by the cargo implementation of the standard](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-cratesio).
Parts of the API that are covered by semver are explicitly marked. Our public
API is extensively tested in integration tests. If changes to the public API of
thin-egde.io are necessary, these changes are documented in the `CHANGELOG.md`
file of the project, with reasoning why the change was necessary and how users
can upgrade to the new interface(s).
<!-- TODO: Expand on how exactly these changes are documented? -->
We also define the following rules for our release process:
* We release new features by increasing the "minor" version
* When we release new patch releases, these releases never contain new features,
but only fixes to bugs.
### Release cycle
Minor releases are published roughly every TBD months.
Until a minor release is published, the previous minor release _can_ receive
patch releases (accordingly to [semver](https://semver.org/)).
### Strategy
We use the release-branch strategy, where development happens on the `main`
branch and releases are done on dedicated `release-X.Y.Z` branches.
Because we employ the "evergreen main branch" strategy and all of our merge
commits on the `main` branch are guaranteed to successfully build and pass CI, a
release can potentially be made starting from every commit on the `main` branch.
For a description of the release workflow, read below.
### Release maintainer
For every release, one or more "release maintainers" are appointed by the core team.
The responsibilities of the release maintainers are:
* Creation and maintenance of the `release-x.y.z` branches
* Cherry-picking bugfix patches from the `main` branch and applying them to the
relevant `release-x.y.z` branch (via pull request to that branch)
* If a patch does not apply cleanly, it is _not_ the responsibility of the
release maintainer to make the patch apply. It is responsibility of the
release maintainer, though, to talk to the developer of the patch and work
with them to port the fix back to the release
* Create git-tags for the release(s) as appropriate
* Create github artifacts (or take care of a github-action pipeline that does)
for releases
* Port the changes to the `CHANGELOG.md` file back to `main` (via pull request)
* Submit pull requests to the `main` branch to update all version numbers for
the next release cycle
It is explicitely allowed for everyone to open bugfix-backport pull requests to
a release branch, but the release maintainer(s) decide when and how to apply
them to the release branch.
### Release branches
Release branches MUST be named with a pattern. This pattern is
```
"release" dash <major version number> dot <minor version number> dot "x"
```
The `"x"` is used instead of the "patch version number", because patch releases
are published from the release branches (read below).
A release branch gets the following rules enforced via GitHub:
* Only the "release-maintainers" team can create branches named `"release-*"`
* Pull requests are required on these branches
* Status checks are required to succeed on these branches
* "CODEOWNERS" is **not** enforced on these branches
* These rules are also enforced for administrators
### Workflow for minor releases
The following steps happen when a minor release is made:
1. One release maintainer creates a branch, starting from a recent commit on
`main`, named after the aforementioned naming rule.
2. It is ensured that this branch cannot be pushed to by anyone except the
[bors merge bot](https://bors.tech)
(Refer to the "Merge strategies" point in the "Repository maintenance"
chapter for more information)
3. The release maintainer crafts the `CHANGELOG.md` entries for this release and
opens a pull request for it, targeting the aforementioned release branch.
4. Once agreed upon with everyone involved, they merge the PR for the
`CHANGELOG.md` to the release branch
5. The release maintainer publishes the release on crates.io and creates a new
tag for the release.
They might add a tag message as they seem appropriate.
They then push the tag to the GitHub repository of the project.
They also make sure that release artifacts are published on GitHub for the
new release (or make sure that a github-actions pipeline does this)
6. After the release is done, the `CHANGELOG.md` changes are cherry-picked to a
pull request to the `main` branch by the release maintainer
7. After the release is done, the release maintainer opens a pull request to the
`main` branch to update all version numbers of all crates to the next minor
version
### Workflow for patch releases
Patch releases are published when appropriate, there is no fixed release cycle.
It might be a good rule-of-thumb to not release more than one patch release per
week.
#### Backporting
Bugfixes that are added to a patch release _must_ have been applied to the
`main` branch, before they are backported to the appropriate `release-x.y.z`
branch.
If a bugfix that is applied to `main` cannot be backported, due to either
conflicts or because the feature that is about to be fixed does not exist in the
`release-x.y.z` branch anymore, the patch may be adapted to apply cleanly or a
special patch may be crafted.
The release maintainer is encouraged to reach out to the developer of the patch,
as it is _not_ the responsibility of the release maintainer to adapt patches.
In any case, fixes that are backported to a `release-x.y.z` branch, MUST pass
CI and thus MUST be submitted to the release branch via a pull request.
#### Publishing of patch releases
A patch release is published as soon as the release maintainer thinks it is
appropriate.
The steps the release maintainer follows are almost the same as the steps of the
minor release workflow:
* The release maintainer submits a pull request to the release branch with the
following changes:
* `CHANEGLOG.md` entries for the patch level release
* Updates of the version numbers for all crates in the "patch"-level position,
e.g. "0.42.0" becomes "0.42.1",
* Once the aforementioned pull request is merged, the release maintainer
publishes the release on crates.io, tags (`git tag -a vMAJOR.MINOR.PATCH`)
the release and pushes the tag to the GitHub repository of the project
* Once the release is published, the release maintainer opens a pull request to
the `main` branch of the project, porting the changes that were made to the
`CHANGELOG.md` file to this branch
### More
The `CHANGELOG.md` is added on the release branch rather than on the `main`
branch. The reason for this is the workflow: If the changelog is adapted on
`main`, the branchoff for the release branch can _only_ happen directly after
the changelog was merged, because other merges may introduce new changes that
need to be in the changelog. Also, the changelog-adding pull request must be up
to date all the time, if patches get merged to `main` _while_ the changelog is
written, the changelog would need further fixes.
If the changelog is added on the release branch, no synchronization is
necessary, as the branchoff for the release already happened.
Instead, the changelog entries can be "backported" to `main`, which is trivial.
In a similar fashion are patch-level `CHANGELOG.md` entries ported to the `main`
branch.
Version number bumps happen right _after_ branchoff for a release. Doing the
version number bump before the release would mean that the release maintainers
would have to wait for the version-bump-pull-request, which is not acceptable
under the preassumption that every commit from `main` can potentially be
released.
By bumping the numbers right _after_ the release, but for the next release, we
automatically get a peace-of-mind state for that next release, where the release
maintainers can again just take any commit on `main` and start their release
process.
The implication of the patch-level release workflow is that the `main` branch
does never see changes to the version strings of the crates in the "patch" level
position. This is intentional, as there is no need for that.
## Related
* [Understanding open source governance models](https://www.redhat.com/en/blog/understanding-open-source-governance-models)
* [Producing Open Source Software](https://producingoss.com/en/producingoss.html)
|