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 | VERSION=5.0.0 |
|
80 | VERSION=5.0.0 | |
81 | BRANCH=master |
|
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 | 2. Create GitHub stats and finish release note |
|
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 | installs an ``ipython2`` or ``ipython3`` script, depending on the version of |
|
236 | installs an ``ipython2`` or ``ipython3`` script, depending on the version of | |
230 | Python it is built for. Using an universal wheel would prevent this. |
|
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 | Use the following to actually upload the result of the build:: |
|
249 | Use the following to actually upload the result of the build:: | |
233 |
|
250 | |||
234 | ./tools/release upload |
|
251 | ./tools/release upload |
@@ -2,6 +2,90 b'' | |||||
2 | 7.x Series |
|
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 | .. _version 714: |
|
89 | .. _version 714: | |
6 |
|
90 | |||
7 | IPython 7.14 |
|
91 | IPython 7.14 |
@@ -225,7 +225,7 b' everything = set()' | |||||
225 | for key, deps in extras_require.items(): |
|
225 | for key, deps in extras_require.items(): | |
226 | if ':' not in key: |
|
226 | if ':' not in key: | |
227 | everything.update(deps) |
|
227 | everything.update(deps) | |
228 | extras_require['all'] = everything |
|
228 | extras_require['all'] = list(sorted(everything)) | |
229 |
|
229 | |||
230 | if 'setuptools' in sys.modules: |
|
230 | if 'setuptools' in sys.modules: | |
231 | setuptools_extra_args['python_requires'] = '>=3.6' |
|
231 | setuptools_extra_args['python_requires'] = '>=3.6' |
@@ -2,6 +2,7 b'' | |||||
2 | """IPython release build script. |
|
2 | """IPython release build script. | |
3 | """ |
|
3 | """ | |
4 | import os |
|
4 | import os | |
|
5 | import sys | |||
5 | from shutil import rmtree |
|
6 | from shutil import rmtree | |
6 |
|
7 | |||
7 | from toollib import sh, pjoin, get_ipdir, cd, sdists, buildwheels |
|
8 | from toollib import sh, pjoin, get_ipdir, cd, sdists, buildwheels | |
@@ -12,15 +13,10 b' def build_release():' | |||||
12 | ipdir = get_ipdir() |
|
13 | ipdir = get_ipdir() | |
13 | cd(ipdir) |
|
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 | # Build source and binary distros |
|
16 | # Build source and binary distros | |
22 | sh(sdists) |
|
17 | sh(sdists) | |
23 | buildwheels() |
|
18 | buildwheels() | |
|
19 | sh(' '.join([sys.executable, 'tools/retar.py', 'dist/*.gz'])) | |||
24 |
|
20 | |||
25 | if __name__ == '__main__': |
|
21 | if __name__ == '__main__': | |
26 | build_release() |
|
22 | build_release() |
@@ -3,7 +3,6 b'' | |||||
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 | import subprocess |
|
5 | import subprocess | |
6 | import os |
|
|||
7 |
|
6 | |||
8 | from toollib import cd, sh |
|
7 | from toollib import cd, sh | |
9 |
|
8 |
@@ -81,13 +81,10 b' else:' | |||||
81 | sh('mv ipython-*.tgz %s' % ipbackupdir) |
|
81 | sh('mv ipython-*.tgz %s' % ipbackupdir) | |
82 |
|
82 | |||
83 | # Build release files |
|
83 | # Build release files | |
84 |
sh('./build_release |
|
84 | sh('./build_release') | |
85 |
|
85 | |||
86 | cd(ipdir) |
|
86 | cd(ipdir) | |
87 |
|
87 | |||
88 | # Upload all files |
|
|||
89 | sh(sdists) |
|
|||
90 |
|
||||
91 | buildwheels() |
|
88 | buildwheels() | |
92 | print("`./release upload` to upload source distribution on PyPI and ipython archive") |
|
89 | print("`./release upload` to upload source distribution on PyPI and ipython archive") | |
93 | sys.exit(0) |
|
90 | sys.exit(0) |
@@ -98,12 +98,6 b' then' | |||||
98 |
|
98 | |||
99 | fi |
|
99 | fi | |
100 |
|
100 | |||
101 | echo |
|
|||
102 | echo $BLUE"Attempting to build package..."$NOR |
|
|||
103 |
|
||||
104 | tools/build_release |
|
|||
105 | rm dist/* |
|
|||
106 |
|
||||
107 | if ask_section "Should we commit, tag, push... etc ? " |
|
101 | if ask_section "Should we commit, tag, push... etc ? " | |
108 | then |
|
102 | then | |
109 | echo |
|
103 | echo | |
@@ -161,13 +155,40 b' fi' | |||||
161 | if ask_section "Should we build and release ?" |
|
155 | if ask_section "Should we build and release ?" | |
162 | then |
|
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 | echo |
|
185 | echo | |
165 | echo $BLUE"Attempting to build package..."$NOR |
|
186 | echo $BLUE"Attempting to build package..."$NOR | |
166 |
|
187 | |||
167 | tools/release |
|
188 | tools/release | |
168 |
|
189 | |||
169 | echo $RED |
|
190 | echo $RED"Check the shasum for SOURCE_DATE_EPOCH=$SOURCE_DATE_EPOCH" | |
170 | echo '$ shasum -a 256 dist/*' |
|
191 | echo $RED'$ shasum -a 256 dist/*' | |
171 | shasum -a 256 dist/* |
|
192 | shasum -a 256 dist/* | |
172 | echo $NOR |
|
193 | echo $NOR | |
173 |
|
194 |
General Comments 0
You need to be logged in to leave comments.
Login now