Show More
@@ -0,0 +1,60 b'' | |||
|
1 | """ | |
|
2 | Un-targz and retargz a targz file to ensure reproducible build. | |
|
3 | ||
|
4 | usage: | |
|
5 | ||
|
6 | $ export SOURCE_DATE_EPOCH=$(date +%s) | |
|
7 | ... | |
|
8 | $ python retar.py <tarfile.gz> | |
|
9 | ||
|
10 | The process of creating an sdist can be non-reproducible: | |
|
11 | - directory created during the process get a mtime of the creation date; | |
|
12 | - gziping files embed the timestamp of fo zip creation. | |
|
13 | ||
|
14 | This will untar-retar; ensuring that all mtime > SOURCE_DATE_EPOCH will be set | |
|
15 | equal to SOURCE_DATE_EPOCH. | |
|
16 | ||
|
17 | """ | |
|
18 | ||
|
19 | import tarfile | |
|
20 | import sys | |
|
21 | import os | |
|
22 | import gzip | |
|
23 | import io | |
|
24 | ||
|
25 | if len(sys.argv) > 2: | |
|
26 | raise ValueError("Too many arguments") | |
|
27 | ||
|
28 | ||
|
29 | timestamp = int(os.environ["SOURCE_DATE_EPOCH"]) | |
|
30 | ||
|
31 | old_buf = io.BytesIO() | |
|
32 | with open(sys.argv[1], "rb") as f: | |
|
33 | old_buf.write(f.read()) | |
|
34 | old_buf.seek(0) | |
|
35 | old = tarfile.open(fileobj=old_buf, mode="r:gz") | |
|
36 | ||
|
37 | buf = io.BytesIO() | |
|
38 | new = tarfile.open(fileobj=buf, mode="w", format=tarfile.GNU_FORMAT) | |
|
39 | for i, m in enumerate(old): | |
|
40 | data = None | |
|
41 | # mutation does not work, copy | |
|
42 | if m.name.endswith('.DS_Store'): | |
|
43 | continue | |
|
44 | m2 = tarfile.TarInfo(m.name) | |
|
45 | m2.mtime = min(timestamp, m.mtime) | |
|
46 | m2.size = m.size | |
|
47 | m2.type = m.type | |
|
48 | m2.linkname = m.linkname | |
|
49 | if m.isdir(): | |
|
50 | data = old.extractfile(m) | |
|
51 | new.addfile(m2, data) | |
|
52 | else: | |
|
53 | new.addfile(m2) | |
|
54 | new.close() | |
|
55 | old.close() | |
|
56 | ||
|
57 | buf.seek(0) | |
|
58 | with open(sys.argv[1], "wb") as f: | |
|
59 | with gzip.GzipFile('', "wb", fileobj=f, mtime=timestamp) as gzf: | |
|
60 | gzf.write(buf.read()) |
@@ -80,6 +80,13 b' for the release you are actually making::' | |||
|
80 | 80 | VERSION=5.0.0 |
|
81 | 81 | BRANCH=master |
|
82 | 82 | |
|
83 | For `reproducibility of builds <https://reproducible-builds.org/specs/source-date-epoch/>`_, | |
|
84 | we recommend setting ``SOURCE_DATE_EPOCH`` prior to running the build; record the used value | |
|
85 | of ``SOURCE_DATE_EPOCH`` as it may not be available from build artifact. You | |
|
86 | should be able to use ``date +%s`` to get a formatted timestamp:: | |
|
87 | ||
|
88 | SOURCE_DATE_EPOCH=$(date +%s) | |
|
89 | ||
|
83 | 90 | |
|
84 | 91 | 2. Create GitHub stats and finish release note |
|
85 | 92 | ---------------------------------------------- |
@@ -229,6 +236,16 b' uploading them to PyPI. We do not use an universal wheel as each wheel' | |||
|
229 | 236 | installs an ``ipython2`` or ``ipython3`` script, depending on the version of |
|
230 | 237 | Python it is built for. Using an universal wheel would prevent this. |
|
231 | 238 | |
|
239 | Check the shasum of files with:: | |
|
240 | ||
|
241 | shasum -a 256 dist/* | |
|
242 | ||
|
243 | and takes notes of them you might need them to update the conda-forge recipes. | |
|
244 | Rerun the command and check the hash have not changed:: | |
|
245 | ||
|
246 | ./tools/release | |
|
247 | shasum -a 256 dist/* | |
|
248 | ||
|
232 | 249 | Use the following to actually upload the result of the build:: |
|
233 | 250 | |
|
234 | 251 | ./tools/release upload |
@@ -2,6 +2,90 b'' | |||
|
2 | 2 | 7.x Series |
|
3 | 3 | ============ |
|
4 | 4 | |
|
5 | .. _version 715: | |
|
6 | ||
|
7 | IPython 7.15 | |
|
8 | ============ | |
|
9 | ||
|
10 | IPython 7.15 brings a number of bug fixes and user facing improvements. | |
|
11 | ||
|
12 | Misc Noticeable changes: | |
|
13 | ------------------------ | |
|
14 | ||
|
15 | - Long completion name have better elision in terminal :ghpull:`12284` | |
|
16 | - I've started to test on Python 3.9 :ghpull:`12307` and fix some errors. | |
|
17 | - Hi DPI scaling of figures when using qt eventloop :ghpull:`12314` | |
|
18 | - Document the ability to have systemwide configuration for IPython. | |
|
19 | :ghpull:`12328` | |
|
20 | - Fix issues with input autoformatting :ghpull:`12336` | |
|
21 | ||
|
22 | Reproducible Build | |
|
23 | ------------------ | |
|
24 | ||
|
25 | Starting with IPython 7.15, I am attempting to provide reproducible builds, | |
|
26 | that is to say you should be able from the source tree to generate an sdist | |
|
27 | and wheel that are identical byte for byte with the publish version on PyPI. | |
|
28 | ||
|
29 | I've only tested on a couple of machines so far and the process is relatively | |
|
30 | straightforward, so this mean that IPython not only have a deterministic build | |
|
31 | process, but also I have either removed, or put under control all effects of | |
|
32 | the build environments on the final artifact. I encourage you to attempt the | |
|
33 | build process on your machine as documented in :ref:`core_developer_guide` | |
|
34 | and let me know if you do not obtain an identical artifact. | |
|
35 | ||
|
36 | While reproducible builds is critical to check that the supply chain of (open | |
|
37 | source) software has not been compromised, it can also help to speedup many | |
|
38 | of the build processes in large environment (conda, apt...) by allowing | |
|
39 | better caching of intermediate build steps. | |
|
40 | ||
|
41 | Learn more on `<https://reproducible-builds.org/>`_. `Reflections on trusting | |
|
42 | trust <https://dl.acm.org/doi/10.1145/358198.358210>`_ is also one of the | |
|
43 | cornerstone and recommended reads on this subject. | |
|
44 | ||
|
45 | .. note:: | |
|
46 | ||
|
47 | The build commit from which the sdist is generated is also `signed | |
|
48 | <https://en.wikipedia.org/wiki/Digital_signature>`_, so you should be able to | |
|
49 | check it has not been compromised, and the git repository is a `merkle-tree | |
|
50 | <https://en.wikipedia.org/wiki/Merkle_tree>`_, you can check the consistency | |
|
51 | with `git-fsck <https://git-scm.com/docs/git-fsck>`_ which you likely `want | |
|
52 | to enable by default | |
|
53 | <https://gist.github.com/mbbx6spp/14b86437e794bffb4120>`_. | |
|
54 | ||
|
55 | NEP29: Last version to support Python 3.6 | |
|
56 | ----------------------------------------- | |
|
57 | ||
|
58 | IPython 7.15 will be the Last IPython version to officially support Python | |
|
59 | 3.6, as stated by `NumPy Enhancement Proposal 29 | |
|
60 | <https://numpy.org/neps/nep-0029-deprecation_policy.html>`_. Starting with | |
|
61 | next minor version of IPython I may stop testing on Python 3.6 and may stop | |
|
62 | publishing release artifacts that install on Python 3.6 | |
|
63 | ||
|
64 | Highlighted features | |
|
65 | -------------------- | |
|
66 | ||
|
67 | Highlighted features are not new, but seem to not be widely known, this | |
|
68 | section will help you discover in more narrative form what you can do with | |
|
69 | IPython. | |
|
70 | ||
|
71 | Increase Tab Completion Menu Height | |
|
72 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
|
73 | ||
|
74 | In terminal IPython it is possible to increase the hight of the tab-completion | |
|
75 | menu. To do so set the value of | |
|
76 | :configtrait:`TerminalInteractiveShell.space_for_menu`, this will reserve more | |
|
77 | space at the bottom of the screen for various kind of menus in IPython including | |
|
78 | tab completion and searching in history. | |
|
79 | ||
|
80 | Autoformat Code in the terminal | |
|
81 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
|
82 | ||
|
83 | If you have a preferred code formatter, you can configure IPython to | |
|
84 | reformat your code. Set the value of | |
|
85 | :configtrait:`TerminalInteractiveShell.autoformatter` to for example ``'black'`` | |
|
86 | and IPython will auto format your code when possible. | |
|
87 | ||
|
88 | ||
|
5 | 89 | .. _version 714: |
|
6 | 90 | |
|
7 | 91 | IPython 7.14 |
@@ -225,7 +225,7 b' everything = set()' | |||
|
225 | 225 | for key, deps in extras_require.items(): |
|
226 | 226 | if ':' not in key: |
|
227 | 227 | everything.update(deps) |
|
228 | extras_require['all'] = everything | |
|
228 | extras_require['all'] = list(sorted(everything)) | |
|
229 | 229 | |
|
230 | 230 | if 'setuptools' in sys.modules: |
|
231 | 231 | setuptools_extra_args['python_requires'] = '>=3.6' |
@@ -2,6 +2,7 b'' | |||
|
2 | 2 | """IPython release build script. |
|
3 | 3 | """ |
|
4 | 4 | import os |
|
5 | import sys | |
|
5 | 6 | from shutil import rmtree |
|
6 | 7 | |
|
7 | 8 | from toollib import sh, pjoin, get_ipdir, cd, sdists, buildwheels |
@@ -12,15 +13,10 b' def build_release():' | |||
|
12 | 13 | ipdir = get_ipdir() |
|
13 | 14 | cd(ipdir) |
|
14 | 15 | |
|
15 | # Cleanup | |
|
16 | for d in ['build', 'dist', pjoin('docs', 'build'), pjoin('docs', 'dist'), | |
|
17 | pjoin('docs', 'source', 'api', 'generated')]: | |
|
18 | if os.path.isdir(d): | |
|
19 | rmtree(d) | |
|
20 | ||
|
21 | 16 | # Build source and binary distros |
|
22 | 17 | sh(sdists) |
|
23 | 18 | buildwheels() |
|
19 | sh(' '.join([sys.executable, 'tools/retar.py', 'dist/*.gz'])) | |
|
24 | 20 | |
|
25 | 21 | if __name__ == '__main__': |
|
26 | 22 | build_release() |
@@ -3,7 +3,6 b'' | |||
|
3 | 3 | """ |
|
4 | 4 | |
|
5 | 5 | import subprocess |
|
6 | import os | |
|
7 | 6 | |
|
8 | 7 | from toollib import cd, sh |
|
9 | 8 |
@@ -81,13 +81,10 b' else:' | |||
|
81 | 81 | sh('mv ipython-*.tgz %s' % ipbackupdir) |
|
82 | 82 | |
|
83 | 83 | # Build release files |
|
84 |
sh('./build_release |
|
|
84 | sh('./build_release') | |
|
85 | 85 | |
|
86 | 86 | cd(ipdir) |
|
87 | 87 | |
|
88 | # Upload all files | |
|
89 | sh(sdists) | |
|
90 | ||
|
91 | 88 | buildwheels() |
|
92 | 89 | print("`./release upload` to upload source distribution on PyPI and ipython archive") |
|
93 | 90 | sys.exit(0) |
@@ -98,12 +98,6 b' then' | |||
|
98 | 98 | |
|
99 | 99 | fi |
|
100 | 100 | |
|
101 | echo | |
|
102 | echo $BLUE"Attempting to build package..."$NOR | |
|
103 | ||
|
104 | tools/build_release | |
|
105 | rm dist/* | |
|
106 | ||
|
107 | 101 | if ask_section "Should we commit, tag, push... etc ? " |
|
108 | 102 | then |
|
109 | 103 | echo |
@@ -161,13 +155,40 b' fi' | |||
|
161 | 155 | if ask_section "Should we build and release ?" |
|
162 | 156 | then |
|
163 | 157 | |
|
158 | echo $BLUE"going to set SOURCE_DATE_EPOCH"$NOR | |
|
159 | echo $BLUE'export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD)'$NOR | |
|
160 | echo $GREEN"Press enter to continue"$NOR | |
|
161 | read | |
|
162 | ||
|
163 | export SOURCE_DATE_EPOCH=$(git show -s --format=%ct HEAD) | |
|
164 | ||
|
165 | echo $BLUE"SOURCE_DATE_EPOCH set to $SOURCE_DATE_EPOCH"$NOR | |
|
166 | echo $GREEN"Press enter to continue"$NOR | |
|
167 | read | |
|
168 | ||
|
169 | ||
|
170 | ||
|
171 | echo | |
|
172 | echo $BLUE"Attempting to build package..."$NOR | |
|
173 | ||
|
174 | tools/release | |
|
175 | ||
|
176 | ||
|
177 | echo $RED'$ shasum -a 256 dist/*' | |
|
178 | shasum -a 256 dist/* | |
|
179 | echo $NOR | |
|
180 | ||
|
181 | echo $BLUE"We are going to rebuild, node the hash above, and compare them to the rebuild"$NOR | |
|
182 | echo $GREEN"Press enter to continue"$NOR | |
|
183 | read | |
|
184 | ||
|
164 | 185 | echo |
|
165 | 186 | echo $BLUE"Attempting to build package..."$NOR |
|
166 | 187 | |
|
167 | 188 | tools/release |
|
168 | 189 | |
|
169 | echo $RED | |
|
170 | echo '$ shasum -a 256 dist/*' | |
|
190 | echo $RED"Check the shasum for SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" | |
|
191 | echo $RED'$ shasum -a 256 dist/*' | |
|
171 | 192 | shasum -a 256 dist/* |
|
172 | 193 | echo $NOR |
|
173 | 194 |
General Comments 0
You need to be logged in to leave comments.
Login now