|
| 1 | +#!/usr/bin/env python3 |
| 2 | + |
| 3 | +from subprocess import check_output |
| 4 | +import sys |
| 5 | +import os.path |
| 6 | + |
| 7 | + |
| 8 | +def main(tarname, gitroot): |
| 9 | + """Run this as ./compare_tar_against_git.py TARFILE GITROOT |
| 10 | +
|
| 11 | + Args |
| 12 | + ==== |
| 13 | +
|
| 14 | + TARFILE: Path to the built sdist (sympy-xx.tar.gz) |
| 15 | + GITROOT: Path ro root of git (dir containing .git) |
| 16 | + """ |
| 17 | + compare_tar_against_git(tarname, gitroot) |
| 18 | + |
| 19 | + |
| 20 | +## TARBALL WHITELISTS |
| 21 | + |
| 22 | +# If a file does not end up in the tarball that should, add it to setup.py if |
| 23 | +# it is Python, or MANIFEST.in if it is not. (There is a command at the top |
| 24 | +# of setup.py to gather all the things that should be there). |
| 25 | + |
| 26 | +# TODO: Also check that this whitelist isn't growing out of date from files |
| 27 | +# removed from git. |
| 28 | + |
| 29 | +# Files that are in git that should not be in the tarball |
| 30 | +git_whitelist = { |
| 31 | + # Git specific dotfiles |
| 32 | + '.gitattributes', |
| 33 | + '.gitignore', |
| 34 | + '.mailmap', |
| 35 | + # Travis and CI |
| 36 | + '.travis.yml', |
| 37 | + '.ci/durations.json', |
| 38 | + '.ci/generate_durations_log.sh', |
| 39 | + '.ci/parse_durations_log.py', |
| 40 | + '.ci/blacklisted.json', |
| 41 | + '.ci/README.rst', |
| 42 | + '.github/FUNDING.yml', |
| 43 | + '.editorconfig', |
| 44 | + '.coveragerc', |
| 45 | + 'asv.conf.travis.json', |
| 46 | + 'coveragerc_travis', |
| 47 | + 'codecov.yml', |
| 48 | + 'pytest.ini', |
| 49 | + 'MANIFEST.in', |
| 50 | + # Code of conduct |
| 51 | + 'CODE_OF_CONDUCT.md', |
| 52 | + # Pull request template |
| 53 | + 'PULL_REQUEST_TEMPLATE.md', |
| 54 | + # Contributing guide |
| 55 | + 'CONTRIBUTING.md', |
| 56 | + # Nothing from bin/ should be shipped unless we intend to install it. Most |
| 57 | + # of this stuff is for development anyway. To run the tests from the |
| 58 | + # tarball, use setup.py test, or import sympy and run sympy.test() or |
| 59 | + # sympy.doctest(). |
| 60 | + 'bin/adapt_paths.py', |
| 61 | + 'bin/ask_update.py', |
| 62 | + 'bin/authors_update.py', |
| 63 | + 'bin/build_doc.sh', |
| 64 | + 'bin/coverage_doctest.py', |
| 65 | + 'bin/coverage_report.py', |
| 66 | + 'bin/deploy_doc.sh', |
| 67 | + 'bin/diagnose_imports', |
| 68 | + 'bin/doctest', |
| 69 | + 'bin/generate_module_list.py', |
| 70 | + 'bin/generate_test_list.py', |
| 71 | + 'bin/get_sympy.py', |
| 72 | + 'bin/mailmap_update.py', |
| 73 | + 'bin/py.bench', |
| 74 | + 'bin/strip_whitespace', |
| 75 | + 'bin/sympy_time.py', |
| 76 | + 'bin/sympy_time_cache.py', |
| 77 | + 'bin/test', |
| 78 | + 'bin/test_external_imports.py', |
| 79 | + 'bin/test_executable.py', |
| 80 | + 'bin/test_import', |
| 81 | + 'bin/test_import.py', |
| 82 | + 'bin/test_isolated', |
| 83 | + 'bin/test_py2_import.py', |
| 84 | + 'bin/test_setup.py', |
| 85 | + 'bin/test_travis.sh', |
| 86 | + # The notebooks are not ready for shipping yet. They need to be cleaned |
| 87 | + # up, and preferably doctested. See also |
| 88 | + # https://github.com/sympy/sympy/issues/6039. |
| 89 | + 'examples/advanced/identitysearch_example.ipynb', |
| 90 | + 'examples/beginner/plot_advanced.ipynb', |
| 91 | + 'examples/beginner/plot_colors.ipynb', |
| 92 | + 'examples/beginner/plot_discont.ipynb', |
| 93 | + 'examples/beginner/plot_gallery.ipynb', |
| 94 | + 'examples/beginner/plot_intro.ipynb', |
| 95 | + 'examples/intermediate/limit_examples_advanced.ipynb', |
| 96 | + 'examples/intermediate/schwarzschild.ipynb', |
| 97 | + 'examples/notebooks/density.ipynb', |
| 98 | + 'examples/notebooks/fidelity.ipynb', |
| 99 | + 'examples/notebooks/fresnel_integrals.ipynb', |
| 100 | + 'examples/notebooks/qubits.ipynb', |
| 101 | + 'examples/notebooks/sho1d_example.ipynb', |
| 102 | + 'examples/notebooks/spin.ipynb', |
| 103 | + 'examples/notebooks/trace.ipynb', |
| 104 | + 'examples/notebooks/Bezout_Dixon_resultant.ipynb', |
| 105 | + 'examples/notebooks/IntegrationOverPolytopes.ipynb', |
| 106 | + 'examples/notebooks/Macaulay_resultant.ipynb', |
| 107 | + 'examples/notebooks/Sylvester_resultant.ipynb', |
| 108 | + 'examples/notebooks/README.txt', |
| 109 | + # This stuff :) |
| 110 | + 'release/.gitignore', |
| 111 | + 'release/README.md', |
| 112 | + 'release/Vagrantfile', |
| 113 | + 'release/fabfile.py', |
| 114 | + 'release/Dockerfile', |
| 115 | + 'release/Dockerfile-base', |
| 116 | + 'release/release.sh', |
| 117 | + 'release/rever.xsh', |
| 118 | + 'release/pull_and_run_rever.sh', |
| 119 | + 'release/compare_tar_against_git.py', |
| 120 | + # This is just a distribute version of setup.py. Used mainly for setup.py |
| 121 | + # develop, which we don't care about in the release tarball |
| 122 | + 'setupegg.py', |
| 123 | + # pytest stuff |
| 124 | + 'conftest.py', |
| 125 | + # Encrypted deploy key for deploying dev docs to GitHub |
| 126 | + 'github_deploy_key.enc', |
| 127 | + } |
| 128 | + |
| 129 | +# Files that should be in the tarball should not be in git |
| 130 | + |
| 131 | +tarball_whitelist = { |
| 132 | + # Generated by setup.py. Contains metadata for PyPI. |
| 133 | + "PKG-INFO", |
| 134 | + # Generated by setuptools. More metadata. |
| 135 | + 'setup.cfg', |
| 136 | + 'sympy.egg-info/PKG-INFO', |
| 137 | + 'sympy.egg-info/SOURCES.txt', |
| 138 | + 'sympy.egg-info/dependency_links.txt', |
| 139 | + 'sympy.egg-info/requires.txt', |
| 140 | + 'sympy.egg-info/top_level.txt', |
| 141 | + 'sympy.egg-info/not-zip-safe', |
| 142 | + 'sympy.egg-info/entry_points.txt', |
| 143 | + # Not sure where this is generated from... |
| 144 | + 'doc/commit_hash.txt', |
| 145 | + } |
| 146 | + |
| 147 | + |
| 148 | +def blue(text): |
| 149 | + return "\033[34m%s\033[0m" % text |
| 150 | + |
| 151 | + |
| 152 | +def red(text): |
| 153 | + return "\033[31m%s\033[0m" % text |
| 154 | + |
| 155 | + |
| 156 | +def run(*cmdline, cwd=None): |
| 157 | + """ |
| 158 | + Run command in subprocess and get lines of output |
| 159 | + """ |
| 160 | + return check_output(cmdline, encoding='utf-8', cwd=cwd).splitlines() |
| 161 | + |
| 162 | + |
| 163 | +def full_path_split(path): |
| 164 | + """ |
| 165 | + Function to do a full split on a path. |
| 166 | + """ |
| 167 | + # Based on https://stackoverflow.com/a/13505966/161801 |
| 168 | + rest, tail = os.path.split(path) |
| 169 | + if not rest or rest == os.path.sep: |
| 170 | + return (tail,) |
| 171 | + return full_path_split(rest) + (tail,) |
| 172 | + |
| 173 | + |
| 174 | +def compare_tar_against_git(tarname, gitroot): |
| 175 | + """ |
| 176 | + Compare the contents of the tarball against git ls-files |
| 177 | +
|
| 178 | + See the bottom of the file for the whitelists. |
| 179 | + """ |
| 180 | + git_lsfiles = set(i.strip() for i in run('git', 'ls-files', cwd=gitroot)) |
| 181 | + tar_output_orig = set(run('tar', 'tf', tarname)) |
| 182 | + tar_output = set() |
| 183 | + for file in tar_output_orig: |
| 184 | + # The tar files are like sympy-0.7.3/sympy/__init__.py, and the git |
| 185 | + # files are like sympy/__init__.py. |
| 186 | + split_path = full_path_split(file) |
| 187 | + if split_path[-1]: |
| 188 | + # Exclude directories, as git ls-files does not include them |
| 189 | + tar_output.add(os.path.join(*split_path[1:])) |
| 190 | + # print tar_output |
| 191 | + # print git_lsfiles |
| 192 | + fail = False |
| 193 | + print() |
| 194 | + print(blue("Files in the tarball from git that should not be there:")) |
| 195 | + print() |
| 196 | + for line in sorted(tar_output.intersection(git_whitelist)): |
| 197 | + fail = True |
| 198 | + print(line) |
| 199 | + print() |
| 200 | + print(blue("Files in git but not in the tarball:")) |
| 201 | + print() |
| 202 | + for line in sorted(git_lsfiles - tar_output - git_whitelist): |
| 203 | + fail = True |
| 204 | + print(line) |
| 205 | + print() |
| 206 | + print(blue("Files in the tarball but not in git:")) |
| 207 | + print() |
| 208 | + for line in sorted(tar_output - git_lsfiles - tarball_whitelist): |
| 209 | + fail = True |
| 210 | + print(line) |
| 211 | + print() |
| 212 | + |
| 213 | + if fail: |
| 214 | + sys.exit(red("Non-whitelisted files found or not found in the tarball")) |
| 215 | + |
| 216 | + |
| 217 | +if __name__ == "__main__": |
| 218 | + main(*sys.argv[1:]) |
0 commit comments