File: release.md

package info (click to toggle)
yotta 0.20.5-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,880 kB
  • sloc: python: 11,279; makefile: 32
file content (299 lines) | stat: -rw-r--r-- 12,905 bytes parent folder | download | duplicates (4)
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
---
layout: default
title: Releasing Software With yotta
section: tutorial/releasing
---

# Releasing Software With yotta

At its simplest, the lifecycle of a yotta module is three steps:

 1. Write some code
 2. Commit it to version control (our favourite is [Github](http://github.com))
 3. Publish your module to the yotta registry

The yotta registry, which hosts released versions of yotta modules and makes
them searchable and accessible to other people, makes sure that every version
of your module that you publish will be available forever. So even if you
abandon your source code, delete your Github and run away to live in the
mountains, people who are using your code won't be left stranded.

(If you want to take ownership of an abandoned yotta module, to publish fixes
and improvements, please submit a ticket on the yotta [issue
tracker](https://github.com/armmbed/yotta/issues).)

## <a name="versions" href="#versions">#</a> Release Version Numbers

As far as yotta is concerned, you can manage your source code however you like.
When you run `yotta publish`, the version number will be read from your
`module.json` file, and a new version uploaded to the yotta registry. But there
are some rules you must follow to play nicely with people using your module:

 * Backwards incompatible releases should increase the major version number.
   (run `yotta version major` to do this for you, and create an associated git tag, if
   you're using git)

 * If you introduce additional functionality you should increase the minor
   version number (`yotta version minor`)

 * Any other changes (bugfixes, changes with no external effect) should change
   the patch version number. (`yotta version patch`)

These are the basic rules of [semantic versioning](http://semver.org), there
are some exceptions for `0.x.x` releases during early development, but if you
stick to the basics it's hard to go wrong.

See [handling updates to dependencies](#dependency-updates) for guidance on how
to deal with breaking changes in your dependencies.

## <a name="branches" href="#branches">#</a> Release Branches

If you increase the major version of your module, you should bear in mind that
people may continue to use the previous version for some time. So it's a good
idea to back-port any important security patches or bugfixes to earlier major
versions.

For example, if you're releasing version `2.0.1` to fix a bug in the `2.0.0`
release, and this bug is also present in the older `1.7.4` release, you should
back-port the fix and release version `1.7.5`. Anyone using your old version
will be able to update to a fixed version *without worrying about backwards
incompatible changes*.

If you're using git to manage your code, you can easily go back to an earlier
version, and start a new branch for bugfix releases like this:

```
git checkout -b 1.7-bugfixes v1.7.5
```

This creates a new branch called `1.7-bugfixes`, based on the existing tag
`v1.7.5`, which `yotta version` will have created for you when you increased
the version to 1.7.5. (If you don't see any tags in your Github repository,
that's probably because you need to run `git push --tags` to push your tags to
the remote.)

Once you've got this branch you can make and commit changes, then use
`yotta version patch` and `yotta publish` to bump the version and publish a new
version, before switching back to your master branch:

```
git checkout master
```
## <a name="namespaces" href="#namespaces">#</a> Release Namesapces

If your module is written in C++, you should also take advantage of C++
namespaces to provide backwards compatibility. Namespaces allow you to provide old
versions of your API alongside new versions, make it obvious which version of
an API someone is using, and make it easy for your users to switch between
versions of your API.

In fact, namespaces are such a useful feature of C++ that we'd recommend using
them even if your module is otherwise C.

For example:

```C++
// file: mymodule/api.h

namespace mymodule{

  namespace v1{
    // the old version of foo
    void foo();
  } // namespace v1
  
  namespace v2{
    // the new version of foo, which foos in an incompatible way to before
    void foo();
  } // namespace v2
  
  namespace current {
      // import all symbols from mymodule::v2 into mymodule::current. Anyone
      // who wants to live on the bleeding edge can use
      // mymodule::current::foo()
      using namespace ::mymodule::v2;
  } // namespace current

} // namespace mymodule
```

This allows use of both versions of `foo` in the same program, if that is
necessary.

```C++
//#include "mymodule/api.h"
int main(){
    // we live on the edge :)
    using mymodule::current::foo;
    
    // calls the foo() we've imported with the using declaration (v2)
    foo();

    // but sometimes it's nice to have an old friend around:
    mymodule::v1::foo();

    return 0;
}
```

Note that you should **never** use `using namespace xxx` declarations in the
global scope in header files, as this would pollute the global namespace. 

The `using` declaration in the example above is OK because it only imports
names from one `mymodule::` namespace into another `mymodule::` namespace, both
of which are under the control of the module author.


## <a name="embedded-version-numbers" href="#embedded-version-numbers">#</a> Embedded Version Numbers

yotta makes the versions of modules available to CMake, and as preprocessor
definitions. This can be useful to include in built software so that a running
program can be queried for its version information. The definitions are:

```C
#define YOTTA_<MODULENAME>_VERSION_STRING "0.2.3"
#define YOTTA_<MODULENAME>_VERSION_MAJOR 0
#define YOTTA_<MODULENAME>_VERSION_MINOR 2
#define YOTTA_<MODULENAME>_VERSION_PATCH 3
```

These are defined for all modules in the system. Any non-alphanumeric
characters in the module name are converted to underscores, and the module name
is uppercase.


## <a name="dependency-updates" href="#dependency-updates">#</a> Handling Updates to Dependencies

The [Semantic Versioning](https://semver.org) rules clearly explain how to
increment the version number of your module when its functionality changes, but
they don't offer a clear guide to how to handle breaking changes made in
dependencies of your module. In this case there are some guidelines you can
follow to minimise the disruption to users of your module:

### <a name="impl-update" href="#impl-update">#</a> Breaking Changes in Private Dependencies
One common pattern is for a yotta module to have private "implementation"
modules, which implement the functionality for different compilation targets,
with the right implementation module included based on [config
information](/reference/config.html).

In these cases the base module (`foo` below) is the only module on which users
will depend: the implementation modules are not depended on directly by
anything other than the main module.

![illustration of API with implementation modules](/assets/img/foo-impl-simple.png)

In this case, to handle a backwards incompatible change being made to the
`foo-impl-1` implementation module, perform the following steps:

 1. make the changes to the `-impl` module, and publish a new major version 3.0
   (following the [semver](http://semver.org) rule for incrementing the major
   version on a breaking change)

     At this point foo does not yet use the new version, because its `^2.0.0`
     [dependency specification](/reference/module.html#dependencies) restricts
     it to compatible versions.

 2. Update foo to use the new implementation, and update its dependency
    specification on `foo-impl-1` accordingly to `^3.0.0`.

 3. Publish a new **minor** version of foo, increasing its version from `1.0.0`
    to `1.1.0`.

 4. Now, when applications [update](/reference/commands.html#yotta-update) they will
    get a new minor version of foo, and new major version of foo-impl.

It's possible to do this because the app (or any other module depending on foo)
**does not depend on the implementation modules directly**. It only depends on them via foo.

The implementation modules should have warnings in their README's that they
should not be depended on directly: if someone depends on them then it will not
be possible to just update the minor version of foo (this is the [next
case](#shared-deps-update)).

### <a name="shared-deps-update" href="#shared-deps-update">#</a> Breaking Changes in Shared Dependencies
Another pattern is that our module might rely on another module for its
implementation which is **not** solely used by your own module. This might be
the case if, for example, you depend on a utility library that other modules
and applications also depend on. For example:

![illustration of depending on a shared utility library](/assets/img/foo-utility-dependency.png)

In this case, the sequence of events when the shared module (`utility` in this
example) makes a new major release with breaking changes is:

 * **(1)** A new major version of utility with the breaking changes is published
           (`3.0.0`).
    
      As before, neither foo, nor our hypothetical application use the new
      version at this point, because their ^2.0.0` [dependency
      specifications](/reference/module.html#dependencies) restrict the
      versions that will be used to compatible ones.

 * Update foo to use the new `utility` version, at this point there are
   two possibilities:
   * **(2A)** it's possible to make foo support **both** the old and new versions
     of `utility` (perhaps by `#ifdef`ing on the `YOTTA_<MODULENAME>_VERSION_MAJOR`
     definition which yotta will make available for every module).
   * **(2B)** it's **only** possible to support one of the major versions of
     `utility` at a time

#### <a name="exposed-dep-compatible-update" href="#exposed-dep-compatible-update">#</a> Handled in a Backwards Compatible Way
If **(A)** is possible, then:

  * **(3A)** publish a new minor version of foo which is compatible with both
             version `2.x` and `3.x` of utility, with a corresponding
             dependency specification: `">=2.0.0,<4.0.0"`

  * **(4A)** when applications update, they will get the new `utility` version unless
      they have other dependencies on an older version
 
#### <a name="exposed-dep-noncompatible-update" href="#exposed-dep-noncompatible-update">#</a> Handled in a Backwards Incompatible Way
In case **(B)** things are harder:

  * **(3B)** Publish a new **major** version of `foo`, with dependency specification on
             3.x of `utility`.

  * **(4B)** The app still won't use either the new version of `foo`, or the new version
             of `utility`.

  * **(5B)** The app (or any other module which uses both `utility` and
             `foo`) must increase its dependency specs for both `foo` and
             `utility` before being able to update.

     Anything else that depends on either `foo` or `utility` needs to
     repeat the process from **(2)**, to determine whether its own
     major version needs to be updated. For a module which updated its version
     only because 

### <a name="shared-private-dep" href="#shared-private-dep">#</a> Synchronised Updates

Case (B) above is bad because it potentially forces major version updates of
lots of modules, and places a high burden on users. Fortunately there are cases
even where (B) applies that we can avoid a major version bump to `foo`.

![shared synchronised dependencies](/assets/img/shared-private-dep.png)

If the shared dependency (`utility` in this case) is depended on not directly
by applications, but instead it is depended on *only* by another module(s)
`bar`, then we can avoid the major update to `foo` with a synchronised update to
`foo` and `bar`:

(This is possible, for example, in internal modules in [`mbed
OS`](https://github.com/armmbed/mbed-os), where the only other things that
depend on them are also internal mbed OS modules. It's also common in private
modules shared only within your own applications.)

 * **(3C)** Update both `foo` and `bar` to use the new major version of `utility`.
            Review and test these changes together using `yotta link`.
 * **(4C)** Publish **minor** updates of both `foo` and `bar` simultaneously,
            which depend on the new major version of `utility`.
 * **(5C)** Any user who updates or installs will either get new versions of *both* 
            `foo` and `bar`, or old versions of both of them: both of which are
            working configurations.

Note that this is compatible with semantic versioning since the APIs of neither
`foo` nor `bar` have changed in a backwards incompatible way. In the future a
smarter dependency solver in yotta should also make this case automatic, by
solving for a working set of dependencies globally, rather than locally at each
point in the dependency graph.