Repository: dtrx-py/dtrx (language: python)

Issue:
Why require twine?
When I try to build dtrx without having twine installed, I get:

```
* Getting dependencies for wheel...

ERROR Missing dependencies:
	twine>=1.11.0
```

Why does dtrx require twine? This was added in 24eeff44664bfb18343bbdd34e09db3265c28b7d "to enable upload" according to the commit message, but I'm not trying to upload anything.

As I understand it, dtrx is a utility to extract archives and twine is a utility to upload things to pypi. It doesn't seem like dtrx should need twine.

There are no occurrences of "twine" in the source code except in `setup_requires` in setup.py. If I remove the mention of twine there, it builds fine without twine.

```diff
--- setup.py.orig	2021-09-15 15:45:01.000000000 -0500
+++ setup.py	2022-01-07 16:28:35.000000000 -0600
@@ -70,6 +70,6 @@
     long_description_content_type="text/markdown",
     # using markdown as pypi description:
     # https://dustingram.com/articles/2018/03/16/markdown-descriptions-on-pypi
-    setup_requires=["setuptools>=38.6.0", "wheel>=0.31.0", "twine>=1.11.0"],
+    setup_requires=["setuptools>=38.6.0", "wheel>=0.31.0"],
     install_requires=install_requires,
```


Discussion / hints:
Thanks for filing this ticket @ryandesign !

`twine` is specified as a dependency there just as a convenience; to upload a project with a markdown-based README (long_description) requires a specific minimum `twine` version:

https://dustingram.com/articles/2018/03/16/markdown-descriptions-on-pypi/#5-upgrade-your-twine

I'm thinking of migrating this package to `poetry` to simplify the build system, but not sure it's worth it.

FWIW, this is the command I use to build the package, which should just install `twine` into `./.eggs` for the build:
```bash
❯ python setup.py bdist_wheel
```

Fix patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 8ef32bd..d06cd6c 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -10,7 +10,25 @@ on:
 
 jobs:
   build:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-20.04
+
+    steps:
+      # checkout v2, with recursive submodule update
+      - uses: actions/checkout@v2
+        with:
+          submodules: recursive
+
+      - name: 🛠️ Build
+        run: |
+          pip install build==0.7.0
+          python -m build
+
+      - uses: actions/upload-artifact@v2
+        with:
+          name: release-packages
+          path: dist/
+  test:
+    runs-on: ubuntu-20.04
 
     strategy:
       matrix:
@@ -23,17 +41,11 @@ jobs:
           submodules: recursive
 
       # build the Docker image we use to run the tests
-      - name: test.sh
+      - name: 🧪 Run Tox Tests
         run: TOX_ARGS='-e ${{ matrix.python }}' ./test.sh
 
-      # upload-artifact to save the output wheels
-      - uses: actions/upload-artifact@v1
-        with:
-          name: ${{ matrix.python }}-wheel-output
-          path: .tox/${{ matrix.python }}/dist/
-
   manpage:
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-20.04
 
     steps:
       # checkout v2, with recursive submodule update
@@ -42,7 +54,7 @@ jobs:
           submodules: recursive
 
       # build the Docker image we use to run the tests
-      - name: test.sh
+      - name: 📝 Generate Man Pages
         run: RUN_JOB=rst2man ./test.sh
 
       # upload-artifact to save the output wheels
diff --git a/.gitignore b/.gitignore
index 4fb1fbc..fa390b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,8 @@
 build
-
-# noahp added
 /.tox/
 /.eggs/
 /*.egg-info/
 /dist/
 /dtrx.1
 __pycache__
-
-# generated during setup.py run
-/scripts/dtrxc
+*.pyc
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index 33826cb..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-include COPYING INSTALL README NEWS
-recursive-include tests *
-exclude tests/testscript.sh
diff --git a/README.md b/README.md
index 2c74794..56a1acc 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,10 @@ pyversions](https://img.shields.io/pypi/pyversions/dtrx.svg?style=for-the-badge&
 
 - [dtrx](#dtrx)
   - [Changes in this repo](#changes-in-this-repo)
+  - [Development](#development)
+    - [Contributions](#contributions)
+    - [Issues](#issues)
+    - [Releases](#releases)
 
 # dtrx
 
@@ -42,3 +46,46 @@ tests by running (requires Docker installed):
 ```bash
 ./test.sh
 ```
+
+## Development
+
+### Contributions
+
+Contributions are gladly welcomed! Feel free to open a Pull Request with any
+changes.
+
+### Issues
+
+When posting an issue, it can be very handy to provide any example files (for
+example, the archive that failed to extract) or reproduction steps so we can
+address the problem quickly.
+
+### Releases
+
+Releases are tagged in this repo and published to pypi.org. The release process
+is the following (unfortunately manual) steps:
+
+```bash
+# update the VERSION value in dtrx/dtrx.py, then:
+❯ git add dtrx/dtrx.py
+❯ git commit  # fill in the commit message
+
+# create an annotated tag for the release. usually good to put a list of new
+# commits since the previous tag, for example by listing them with:
+# ❯ git log $(git describe --tags --abbrev=0)..HEAD --oneline
+❯ git tag -a <version number>
+
+# build the release archives (requires the 'build' package, `pip install build`)
+❯ python -m build
+
+# confirm the correct build artifacts were generated
+❯ ls dist
+dtrx-8.2.2-py2.py3-none-any.whl  dtrx-8.2.2.tar.gz
+
+# upload to pypi.org (requires a user account on the dtrx project)
+❯ twine upload dist/*
+
+# optional, but nice to do, create a GitHub Release for the tag. requires
+# permissions on the dtrx GitHub repo
+❯ gh release create --generate-notes <tag>
+```
diff --git a/INSTALL b/archived/INSTALL
similarity index 100%
rename from INSTALL
rename to archived/INSTALL
diff --git a/NEWS b/archived/NEWS
similarity index 100%
rename from NEWS
rename to archived/NEWS
diff --git a/README b/archived/README
similarity index 100%
rename from README
rename to archived/README
diff --git a/archived/README.md b/archived/README.md
new file mode 100644
index 0000000..43280d0
--- /dev/null
+++ b/archived/README.md
@@ -0,0 +1,5 @@
+# Archived Documents
+
+These files originally resided at the top-level of this repo, but have been deprecated now that the documentations is moving to Markdown-based files for most purposes.
+
+The files are preserved for posterity and in honor of the original author of this software, Brett Smith.
diff --git a/TODO b/archived/TODO
similarity index 100%
rename from TODO
rename to archived/TODO
diff --git a/contributions.mbox b/archived/contributions.mbox
similarity index 100%
rename from contributions.mbox
rename to archived/contributions.mbox
diff --git a/web/common.css b/archived/web/common.css
similarity index 100%
rename from web/common.css
rename to archived/web/common.css
diff --git a/web/index.html b/archived/web/index.html
similarity index 100%
rename from web/index.html
rename to archived/web/index.html
diff --git a/dtrx/__init__.py b/dtrx/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/dtrx b/dtrx/dtrx.py
similarity index 99%
rename from scripts/dtrx
rename to dtrx/dtrx.py
index d1aa050..0e6ba8b 100755
--- a/scripts/dtrx
+++ b/dtrx/dtrx.py
@@ -1716,6 +1716,9 @@ def run(self):
         return 0
 
 
-if __name__ == "__main__":
+def main():
     app = ExtractorApplication(sys.argv[1:])
     sys.exit(app.run())
+
+if __name__ == "__main__":
+    main()
diff --git a/pyproject.toml b/pyproject.toml
index c70af8c..6e8fba4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,7 @@
 [build-system]
 requires = [
-    "setuptools >= 40.0.4",
-    "wheel >= 0.29.0",
+    # latest at time of writing
+    "setuptools >= 44.1.1",
+    "wheel >= 0.37.0",
 ]
 build-backend = 'setuptools.build_meta'
diff --git a/setup.cfg b/setup.cfg
index 2a9acf1..7282b38 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,45 @@
+[metadata]
+name = dtrx
+version = attr: dtrx.dtrx.VERSION
+description = Script to intelligently extract multiple archive types
+long_description = file: README.md
+long_description_content_type = text/markdown
+author = Brett Smith
+author_email = brettcsmith@brettcsmith.org
+license = GNU General Public License version 3 or later
+classifiers =
+    Development Status :: 5 - Production/Stable
+    Environment :: Console
+    Intended Audience :: End Users/Desktop
+    Intended Audience :: System Administrators
+    License :: OSI Approved :: GNU General Public License (GPL)
+    Natural Language :: English
+    Operating System :: POSIX
+    Programming Language :: Python
+    Programming Language :: Python :: 2
+    Programming Language :: Python :: 2.7
+    Programming Language :: Python :: 3
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+    Programming Language :: Python :: 3.10
+    Programming Language :: Python :: Implementation :: CPython
+    Topic :: Utilities
+download_url = https://github.com/dtrx-py/dtrx
+project_urls =
+    homepage = http://www.brettcsmith.org/2007/dtrx/
+    code = https://github.com/dtrx-py/dtrx
+
+[options]
+packages = dtrx
+install_requires =
+    subprocess32;python_version < '3.3'
+python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*
+
+[options.entry_points]
+console_scripts =
+    dtrx = dtrx.dtrx:main
+
 [bdist_wheel]
 universal = 1
diff --git a/setup.py b/setup.py
index 4ec6b27..14bfa6a 100644
--- a/setup.py
+++ b/setup.py
@@ -1,75 +1,5 @@
-#!/usr/bin/env python
+# Thin wrapper for backwards compatibility
 
-import io
-import os
-import sys
+import setuptools
 
-from setuptools import setup
-
-# Get long description from readme
-ROOT_PATH = os.path.abspath(os.path.join(os.environ.get("TOX_INI_DIR", ".")))
-README_PATH = os.path.join(ROOT_PATH, "README.md")
-with io.open(README_PATH, "rt", encoding="utf8") as readmefile:
-    README = readmefile.read()
-
-if sys.version_info <= (3, 2):
-    install_requires = ["subprocess32"]
-else:
-    install_requires = []
-
-# get version number from dtrx
-# suppress the deprecation warning for imp, it's for py 2.7/3.x compat
-import warnings
-
-warnings.filterwarnings("ignore", category=DeprecationWarning)
-import imp
-
-dtrx = imp.load_source("dtrx", os.path.join(ROOT_PATH, "scripts/dtrx"))
-
-setup(
-    name="dtrx",
-    version=dtrx.VERSION,
-    description="Script to intelligently extract multiple archive types",
-    author="Brett Smith",
-    author_email="brettcsmith@brettcsmith.org",
-    url="http://www.brettcsmith.org/2007/dtrx/",
-    project_urls={
-        "Code": "https://github.com/dtrx-py/dtrx",
-    },
-    download_url="https://github.com/dtrx-py/dtrx",
-    scripts=["scripts/dtrx"],
-    license="GNU General Public License, version 3 or later",
-    classifiers=[
-        "Development Status :: 5 - Production/Stable",
-        "Environment :: Console",
-        "Intended Audience :: End Users/Desktop",
-        "Intended Audience :: System Administrators",
-        "License :: OSI Approved :: GNU General Public License (GPL)",
-        "Natural Language :: English",
-        "Operating System :: POSIX",
-        "Programming Language :: Python",
-        "Topic :: Utilities",
-        "Programming Language :: Python :: 2.7",
-        "Programming Language :: Python :: 3.6",
-        "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
-        "Programming Language :: Python :: 3.9",
-    ],
-    # long_description="""dtrx extracts archives in a number of different
-    #   formats; it currently supports tar, zip (including self-extracting
-    #   .exe files), cpio, rpm, deb, gem, 7z, cab, rar, lzh, arj, and
-    #   InstallShield files.  It can also decompress files compressed with gzip,
-    #   bzip2, lzma, xz, lrzip, lzip, or compress.
-    #   In addition to providing one command to handle many different archive
-    #   types, dtrx also aids the user by extracting contents consistently.
-    #   By default, everything will be written to a dedicated directory
-    #   that's named after the archive.  dtrx will also change the
-    #   permissions to ensure that the owner can read and write all those
-    #   files.""",
-    long_description=README,
-    long_description_content_type="text/markdown",
-    # using markdown as pypi description:
-    # https://dustingram.com/articles/2018/03/16/markdown-descriptions-on-pypi
-    setup_requires=["setuptools>=38.6.0", "wheel>=0.31.0", "twine>=1.11.0"],
-    install_requires=install_requires,
-)
+setuptools.setup()
diff --git a/gen-manpage.sh b/tools/gen-manpage.sh
similarity index 100%
rename from gen-manpage.sh
rename to tools/gen-manpage.sh


Test patch:
diff --git a/test.sh b/test.sh
index 394b412..c4e930f 100755
--- a/test.sh
+++ b/test.sh
@@ -29,6 +29,6 @@ case $RUN_JOB in
     ;;
     rst2man)
         # build man page from README
-        docker run --rm -v "$(pwd)":/mnt/workspace -t "$DOCKER_IMAGE_NAME" bash gen-manpage.sh README dtrx.1
+        docker run --rm -v "$(pwd)":/mnt/workspace -t "$DOCKER_IMAGE_NAME" bash tools/gen-manpage.sh archived/README dtrx.1
     ;;
 esac
diff --git a/tests/compare.py b/tests/compare.py
index 8637e18..7d68e8a 100755
--- a/tests/compare.py
+++ b/tests/compare.py
@@ -40,15 +40,15 @@
 except NameError:
     from sets import Set as set
 
-if os.path.exists("scripts/dtrx") and os.path.exists("tests"):
+if os.path.exists("dtrx/dtrx.py") and os.path.exists("tests"):
     os.chdir("tests")
-elif os.path.exists("../scripts/dtrx") and os.path.exists("../tests"):
+elif os.path.exists("../dtrx/dtrx.py") and os.path.exists("../tests"):
     pass
 else:
     print("ERROR: Can't run tests in this directory!")
     sys.exit(2)
 
-DTRX_SCRIPT = os.path.realpath("../scripts/dtrx")
+DTRX_SCRIPT = os.path.realpath("../dtrx/dtrx.py")
 SHELL_CMD = ["sh", "-se"]
 ROOT_DIR = os.path.realpath(os.curdir)
 NUM_TESTS = 0