1Maintainer / core-developer information
2========================================
3
4
5Releasing
6---------
7
8This section is about preparing a major release, incrementing the minor
9version, or a bug fix release incrementing the patch version. Our convention is
10that we release one or more release candidates (0.RRrcN) before releasing the
11final distributions. We follow the `PEP101
12<https://www.python.org/dev/peps/pep-0101/>`_ to indicate release candidates,
13post, and minor releases.
14
15Before a release
16................
17
181. Update authors table:
19
20   .. prompt:: bash $
21
22       cd build_tools; make authors; cd ..
23
24   and commit. This is only needed if the authors have changed since the last
25   release. This step is sometimes done independent of the release. This
26   updates the maintainer list and is not the contributor list for the release.
27
282. Confirm any blockers tagged for the milestone are resolved, and that other
29   issues tagged for the milestone can be postponed.
30
313. Ensure the change log and commits correspond (within reason!), and that the
32   change log is reasonably well curated. Some tools for these tasks include:
33
34   - ``maint_tools/sort_whats_new.py`` can put what's new entries into
35     sections. It's not perfect, and requires manual checking of the changes.
36     If the what's new list is well curated, it may not be necessary.
37
38   - The ``maint_tools/whats_missing.sh`` script may be used to identify pull
39     requests that were merged but likely missing from What's New.
40
414. Make sure the deprecations, FIXME and TODOs tagged for the release have
42   been taken care of.
43
44**Permissions**
45
46The release manager requires a set of permissions on top of the usual
47permissions given to maintainers, which includes:
48
49- *maintainer* role on ``scikit-learn`` projects on ``pypi.org`` and
50  ``test.pypi.org``, separately.
51- become a member of the *scikit-learn* team on conda-forge by editing the
52  ``recipe/meta.yaml`` file on
53  ``https://github.com/conda-forge/scikit-learn-feedstock``
54
55.. _preparing_a_release_pr:
56
57Preparing a release PR
58......................
59
60Major version release
61~~~~~~~~~~~~~~~~~~~~~
62
63Prior to branching please do not forget to prepare a Release Highlights page as
64a runnable example and check that its HTML rendering looks correct. These
65release highlights should be linked from the ``doc/whats_new/v0.99.rst`` file
66for the new version of scikit-learn.
67
68Releasing the first RC of e.g. version `0.99.0` involves creating the release
69branch `0.99.X` directly on the main repo, where `X` really is the letter X,
70**not a placeholder**. The development for the major and minor releases of `0.99`
71should **also** happen under `0.99.X`. Each release (rc, major, or minor) is a
72tag under that branch.
73
74This is done only once, as the major and minor releases happen on the same
75branch:
76
77   .. prompt:: bash $
78
79     # Assuming upstream is an alias for the main scikit-learn repo:
80     git fetch upstream main
81     git checkout upstream/main
82     git checkout -b 0.99.X
83     git push --set-upstream upstream 0.99.X
84
85   Again, `X` is literal here, and `99` is replaced by the release number.
86   The branches are called ``0.19.X``, ``0.20.X``, etc.
87
88In terms of including changes, the first RC ideally counts as a *feature
89freeze*. Each coming release candidate and the final release afterwards will
90include only minor documentation changes and bug fixes. Any major enhancement
91or feature should be excluded.
92
93Then you can prepare a local branch for the release itself, for instance:
94``release-0.99.0rc1``, push it to your github fork and open a PR **to the**
95`scikit-learn/0.99.X` **branch**. Copy the :ref:`release_checklist` templates
96in the description of the Pull Request to track progress.
97
98This PR will be used to push commits related to the release as explained in
99:ref:`making_a_release`.
100
101You can also create a second PR from main and targeting main to increment
102the ``__version__`` variable in `sklearn/__init__.py` to increment the dev
103version. This means while we're in the release candidate period, the latest
104stable is two versions behind the main branch, instead of one. In this PR
105targeting main you should also include a new file for the matching version
106under the ``doc/whats_new/`` folder so PRs that target the next version can
107contribute their changelog entries to this file in parallel to the release
108process.
109
110Minor version release
111~~~~~~~~~~~~~~~~~~~~~
112
113The minor releases should include bug fixes and some relevant documentation
114changes only. Any PR resulting in a behavior change which is not a bug fix
115should be excluded.
116
117First, create a branch, **on your own fork** (to release e.g. `0.99.3`):
118
119.. prompt:: bash $
120
121    # assuming main and upstream/main are the same
122    git checkout -b release-0.99.3 main
123
124Then, create a PR **to the** `scikit-learn/0.99.X` **branch** (not to
125main!) with all the desired changes:
126
127.. prompt:: bash $
128
129	git rebase -i upstream/0.99.2
130
131Copy the :ref:`release_checklist` templates in the description of the Pull
132Request to track progress.
133
134Do not forget to add a commit updating ``sklearn.__version__``.
135
136It's nice to have a copy of the ``git rebase -i`` log in the PR to help others
137understand what's included.
138
139.. _making_a_release:
140
141Making a release
142................
143
1440. Ensure that you have checked out the branch of the release PR as explained
145   in :ref:`preparing_a_release_pr` above.
146
1471. Update docs. Note that this is for the final release, not necessarily for
148   the RC releases. These changes should be made in main and cherry-picked
149   into the release branch, only before the final release.
150
151   - Edit the ``doc/whats_new/v0.99.rst`` file to add release title and list of
152     contributors.
153     You can retrieve the list of contributor names with:
154
155     ::
156
157       $ git shortlog -s 0.98.33.. | cut -f2- | sort --ignore-case | tr '\n' ';' | sed 's/;/, /g;s/, $//' | fold -s
158
159     - For major releases, link the release highlights example from the ``doc/whats_new/v0.99.rst`` file.
160
161   - Update the release date in ``whats_new.rst``
162
163   - Edit the ``doc/templates/index.html`` to change the 'News' entry of the
164     front page (with the release month as well).
165
1662. On the branch for releasing, update the version number in
167   ``sklearn/__init__.py``, the ``__version__``.
168
169   For major releases, please add a 0 at the end: `0.99.0` instead of `0.99`.
170
171   For the first release candidate, use the `rc1` suffix on the expected final
172   release number: `0.99.0rc1`.
173
1743. Trigger the wheel builder with the ``[cd build]`` commit marker using
175   the command:
176
177   .. prompt:: bash $
178
179    git commit --allow-empty -m "Trigger wheel builder workflow: [cd build]"
180
181   The wheel building workflow is managed by GitHub Actions and the results be browsed at:
182   https://github.com/scikit-learn/scikit-learn/actions?query=workflow%3A%22Wheel+builder%22
183
184.. note::
185
186  Before building the wheels, make sure that the ``pyproject.toml`` file is
187  up to date and using the oldest version of ``numpy`` for each Python version
188  to avoid `ABI <https://en.wikipedia.org/wiki/Application_binary_interface>`_
189  incompatibility issues. Moreover, a new line have to be included in the
190  ``pyproject.toml`` file for each new supported version of Python.
191
192.. note::
193
194  The acronym CD in `[cd build]` stands for `Continuous Delivery
195  <https://en.wikipedia.org/wiki/Continuous_delivery>`_ and refers to the
196  automation used to generate the release artifacts (binary and source
197  packages). This can be seen as an extension to CI which stands for
198  `Continuous Integration
199  <https://en.wikipedia.org/wiki/Continuous_integration>`_. The CD workflow on
200  GitHub Actions is also used to automatically create nightly builds and
201  publish packages for the development branch of scikit-learn. See
202  :ref:`install_nightly_builds`.
203
2044. Once all the CD jobs have completed successfully in the PR, merge it,
205   again with the `[cd build]` marker in the commit message. This time
206   the results will be uploaded to the staging area.
207
208   You should then be able to upload the generated artifacts (.tar.gz and .whl
209   files) to https://test.pypi.org using the "Run workflow" form for the
210   following GitHub Actions workflow:
211
212   https://github.com/scikit-learn/scikit-learn/actions?query=workflow%3A%22Publish+to+Pypi%22
213
2144.1 You can test the conda-forge builds by submitting a PR to the feedstock
215    repo: https://github.com/conda-forge/scikit-learn-feedstock. If you want to
216    publish an RC release on conda-forge, the PR should target the `rc` branch
217    as opposed to the `master` branch. The two branches need to be kept sync
218    together otherwise.
219
2205. If this went fine, you can proceed with tagging. Proceed with caution.
221   Ideally, tags should be created when you're almost certain that the release
222   is ready, since adding a tag to the main repo can trigger certain automated
223   processes.
224
225   Create the tag and push it (if it's an RC, it can be ``0.xx.0rc1`` for
226   instance):
227
228   .. prompt:: bash $
229
230     git tag -a 0.99.0  # in the 0.99.X branch
231     git push git@github.com:scikit-learn/scikit-learn.git 0.99.0
232
2336. Trigger the GitHub Actions workflow again but this time to upload the artifacts
234   to the real https://pypi.org (replace "testpypi" by "pypi" in the "Run
235   workflow" form).
236
2377. Alternatively, it's possible to collect locally the generated binary wheel
238   packages and source tarball and upload them all to PyPI by running the
239   following commands in the scikit-learn source folder (checked out at the
240   release tag):
241
242   .. prompt:: bash $
243
244       rm -r dist
245       pip install -U wheelhouse_uploader twine
246       python setup.py fetch_artifacts
247
248   This command will download all the binary packages accumulated in the
249   `staging area on the anaconda.org hosting service
250   <https://anaconda.org/scikit-learn-wheels-staging/scikit-learn/files>`_ and
251   put them in your local `./dist` folder.
252
253   Check the content of the `./dist` folder: it should contain all the wheels
254   along with the source tarball ("scikit-learn-RRR.tar.gz").
255
256   Make sure that you do not have developer versions or older versions of
257   the scikit-learn package in that folder.
258
259   Before uploading to pypi, you can test upload to test.pypi.org:
260
261   .. prompt:: bash $
262
263       twine upload --verbose --repository-url https://test.pypi.org/legacy/ dist/*
264
265   Upload everything at once to https://pypi.org:
266
267   .. prompt:: bash $
268
269       twine upload dist/*
270
2718. For major/minor (not bug-fix release), update the symlink for ``stable``
272   and the ``latestStable`` variable in
273   https://github.com/scikit-learn/scikit-learn.github.io:
274
275   .. prompt:: bash $
276
277       cd /tmp
278       git clone --depth 1 --no-checkout git@github.com:scikit-learn/scikit-learn.github.io.git
279       cd scikit-learn.github.io
280       echo stable > .git/info/sparse-checkout
281       git checkout main
282       rm stable
283       ln -s 0.999 stable
284       sed -i "s/latestStable = '.*/latestStable = '0.999';/" versionwarning.js
285       git add stable versionwarning.js
286       git commit -m "Update stable to point to 0.999"
287       git push origin master
288
289.. _release_checklist:
290
291Release checklist
292.................
293
294The following GitHub checklist might be helpful in a release PR::
295
296    * [ ] update news and what's new date in release branch
297    * [ ] update news and what's new date and sklearn dev0 version in main branch
298    * [ ] check that the for the release wheels can be built successfully
299    * [ ] merge the PR with `[cd build]` commit message to upload wheels to the staging repo
300    * [ ] upload the wheels and source tarball to https://test.pypi.org
301    * [ ] create tag on the main github repo
302    * [ ] confirm bot detected at
303      https://github.com/conda-forge/scikit-learn-feedstock and wait for merge
304    * [ ] upload the wheels and source tarball to PyPI
305    * [ ] https://github.com/scikit-learn/scikit-learn/releases publish
306    * [ ] announce on mailing list and on Twitter, and LinkedIn
307
308Merging Pull Requests
309---------------------
310
311Individual commits are squashed when a Pull Request (PR) is merged on Github.
312Before merging,
313
314- the resulting commit title can be edited if necessary. Note
315  that this will rename the PR title by default.
316- the detailed description, containing the titles of all the commits, can
317  be edited or deleted.
318- for PRs with multiple code contributors care must be taken to keep
319  the `Co-authored-by: name <name@example.com>` tags in the detailed
320  description. This will mark the PR as having `multiple co-authors
321  <https://help.github.com/en/github/committing-changes-to-your-project/creating-a-commit-with-multiple-authors>`_.
322  Whether code contributions are significanly enough to merit co-authorship is
323  left to the maintainer's discretion, same as for the "what's new" entry.
324
325
326The scikit-learn.org web site
327-----------------------------
328
329The scikit-learn web site (http://scikit-learn.org) is hosted at GitHub,
330but should rarely be updated manually by pushing to the
331https://github.com/scikit-learn/scikit-learn.github.io repository. Most
332updates can be made by pushing to master (for /dev) or a release branch
333like 0.99.X, from which Circle CI builds and uploads the documentation
334automatically.
335
336Travis Cron jobs
337----------------
338
339From `<https://docs.travis-ci.com/user/cron-jobs>`_: Travis CI cron jobs work
340similarly to the cron utility, they run builds at regular scheduled intervals
341independently of whether any commits were pushed to the repository. Cron jobs
342always fetch the most recent commit on a particular branch and build the project
343at that state. Cron jobs can run daily, weekly or monthly, which in practice
344means up to an hour after the selected time span, and you cannot set them to run
345at a specific time.
346
347For scikit-learn, Cron jobs are used for builds that we do not want to run in
348each PR. As an example the build with the dev versions of numpy and scipy is
349run as a Cron job. Most of the time when this numpy-dev build fail, it is
350related to a numpy change and not a scikit-learn one, so it would not make sense
351to blame the PR author for the Travis failure.
352
353The definition of what gets run in the Cron job is done in the .travis.yml
354config file, exactly the same way as the other Travis jobs. We use a ``if: type
355= cron`` filter in order for the build to be run only in Cron jobs.
356
357The branch targeted by the Cron job and the frequency of the Cron job is set
358via the web UI at https://www.travis-ci.org/scikit-learn/scikit-learn/settings.
359
360Experimental features
361---------------------
362
363The :mod:`sklearn.experimental` module was introduced in 0.21 and contains
364experimental features / estimators that are subject to change without
365deprecation cycle.
366
367To create an experimental module, you can just copy and modify the content of
368`enable_hist_gradient_boosting.py
369<https://github.com/scikit-learn/scikit-learn/blob/c9c89cfc85dd8dfefd7921c16c87327d03140a06/sklearn/experimental/enable_hist_gradient_boosting.py>`__,
370or
371`enable_iterative_imputer.py
372<https://github.com/scikit-learn/scikit-learn/blob/c9c89cfc85dd8dfefd7921c16c87327d03140a06/sklearn/experimental/enable_iterative_imputer.py>`_.
373
374.. note::
375
376  These are permalink as in 0.24, where these estimators are still
377  experimental. They might be stable at the time of reading - hence the
378  permalink. See below for instructions on the transition from experimental
379  to stable.
380
381Note that the public import path must be to a public subpackage (like
382``sklearn/ensemble`` or ``sklearn/impute``), not just a ``.py`` module.
383Also, the (private) experimental features that are imported must be in a
384submodule/subpackage of the public subpackage, e.g.
385``sklearn/ensemble/_hist_gradient_boosting/`` or
386``sklearn/impute/_iterative.py``. This is needed so that pickles still work
387in the future when the features aren't experimental anymore.
388
389To avoid type checker (e.g. mypy) errors a direct import of experimental
390estimators should be done in the parent module, protected by the
391``if typing.TYPE_CHECKING`` check. See `sklearn/ensemble/__init__.py
392<https://github.com/scikit-learn/scikit-learn/blob/c9c89cfc85dd8dfefd7921c16c87327d03140a06/sklearn/ensemble/__init__.py>`_,
393or `sklearn/impute/__init__.py
394<https://github.com/scikit-learn/scikit-learn/blob/c9c89cfc85dd8dfefd7921c16c87327d03140a06/sklearn/impute/__init__.py>`_
395for an example.
396
397Please also write basic tests following those in
398`test_enable_hist_gradient_boosting.py
399<https://github.com/scikit-learn/scikit-learn/blob/c9c89cfc85dd8dfefd7921c16c87327d03140a06/sklearn/experimental/tests/test_enable_hist_gradient_boosting.py>`__.
400
401
402Make sure every user-facing code you write explicitly mentions that the feature
403is experimental, and add a ``# noqa`` comment to avoid pep8-related warnings::
404
405    # To use this experimental feature, we need to explicitly ask for it:
406    from sklearn.experimental import enable_hist_gradient_boosting  # noqa
407    from sklearn.ensemble import HistGradientBoostingRegressor
408
409For the docs to render properly, please also import
410``enable_my_experimental_feature`` in ``doc/conf.py``, else sphinx won't be
411able to import the corresponding modules. Note that using ``from
412sklearn.experimental import *`` **does not work**.
413
414Note that some experimental classes / functions are not included in the
415:mod:`sklearn.experimental` module: ``sklearn.datasets.fetch_openml``.
416
417Once the feature become stable, remove all `enable_my_experimental_feature`
418in the scikit-learn code (even feature highlights etc.) and make the
419`enable_my_experimental_feature` a no-op that just raises a warning:
420`enable_hist_gradient_boosting.py
421<https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/experimental/enable_hist_gradient_boosting.py>`__.
422The file should stay there indefinitely as we don't want to break users code:
423we just incentivize them to remove that import with the warning.
424
425Also update the tests accordingly: `test_enable_hist_gradient_boosting.py
426<https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/experimental/tests/test_enable_hist_gradient_boosting.py>`__.
427