mass-driver


Namemass-driver JSON
Version 0.16.2 PyPI version JSON
download
home_pagehttps://github.com/OverkillGuy/mass-driver
SummarySend bulk repo change requests
upload_time2023-07-25 19:24:02
maintainer
docs_urlNone
authorJb Doyon
requires_python>=3.11,<4.0
licenseGPL-3.0-or-later
keywords repo-automation
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Mass Driver
![PyPI](https://img.shields.io/pypi/v/mass-driver)
![PyPI - License](https://img.shields.io/pypi/l/mass-driver)

Send bulk repo change requests.

This repository is on Github: [Overkillguy/mass-driver](https://github.com/OverkillGuy/mass-driver/).

Requires Python 3.11.
## Usage

See also the docs at [jiby.tech/mass-driver/](https://jiby.tech/mass-driver/)

### Installation

Install the package:

    pip install mass-driver

We recommend you install CLIs via [pipx](https://pypa.github.io/pipx/), for dependency isolation:

    pipx install mass-driver

If you want to install from a git branch rather than Pypi:

    pipx install https://github.com/OverkillGuy/mass-driver
    # See pipx docs: https://pypa.github.io/pipx/#running-from-source-control

### Running the tool

Use the help menu to start with:

    mass-driver --help

### Preparing a change

Let's prepare for doing a change over dozens of repositories.
We'll need to find a `PatchDriver` that suits our needs, and configure it accordingly.

List available `PatchDriver`s via:

    mass-driver drivers --list
    # The docs for a single driver:
    mass-driver driver --info counter

Remember, `PatchDriver`s are exposed via a python plugin system, which means anyone can package their own!

Once you've got a driver, you should create a Migration file, in TOML:

``` toml
# Saved as "fix_teamname.toml"
[mass-driver.migration]
# As seen in 'git log':
commit_message = """Change team name

Team name XYZ is wrong, we should be called ABC instead.
See JIRA-123[1].

[1]: https://example.com/tickets/JIRA-123
"""

# Override the local git commit author
commit_author_name = "John Smith"
commit_author_email = "smith@example.com"

branch_name = "fix-team-name"

# PatchDriver class to use.
# Selected via plugin name, from "massdriver.drivers" entrypoint
driver_name = "teamname-changer"

# Config given to the PatchDriver instance
driver_config = { filename = "catalog.yaml", team_name = "Core Team" }

# Note: No "forge" section = no forge activity to pursue (no PR will be created)
```

With this file named `fix_teamname.toml` in hand, we can apply the change
locally, either against a local repo we've already cloned:

``` shell
mass-driver run fix_teamname.toml --repo-path ~/workspace/my-repo/
```
Or against a repo being cloned from URL:

``` shell
mass-driver run fix_teamname.toml --repo-path 'git@github.com:OverkillGuy/sphinx-needs-test.git'
```

The cloned repo will be under `.mass_driver/repos/USER/REPONAME/`.
We should expect a branch named `fix-team-name` with a single commit.

To apply the change over a list of repositories, create a file with relevant
repos:

``` shell
cat <<EOF > repos.txt
git@github.com:OverkillGuy/sphinx-needs-test.git
git@github.com:OverkillGuy/speeders.git
EOF

mass-driver run fix_teamname.toml --repo-filelist repos.txt
```

### Creating PRs

Once the commits are done locally, let's send them up as PR a second step.
For this, we'll be creating a second activity file containing a Forge definition.

Similarly, forges can be listed and detailed:

```shell
mass-driver forges --list
# The docs for a single forge:
mass-driver forge --info counter
```

Consider using the `forge_name = "github"`.
Create a new Activity with a Forge:

``` toml
# An Activity made up of just a forge
[mass-driver.forge]
forge_name = "github"

base_branch = "main"

head_branch = "fix-teamname"
draft_pr = true
pr_title = "[JIRA-123] Bump counter.txt to 1"
pr_body = """Change team name

Team name XYZ is wrong, we should be called ABC instead.
See JIRA-123[1].

[1]: https://example.com/tickets/JIRA-123
"""

# Do you need to git push the branch before PR?
git_push_first = true
```

Now run mass-driver, remembering to set the `FORGE_TOKEN` envvar for a Github/other auth token.

``` shell
export FORGE_TOKEN="ghp_supersecrettoken"
mass-driver run fix_teamname_forge.toml --repo-filelist repos.txt
```

### Combining migration then forge
Sometimes, we wish to expedite both the committing and the PR creation in a single move.

The Activity file can contain both sections:

``` toml
# An activity made up of first a Migration, then a Forge
[mass-driver.migration]
# As seen in 'git log':
commit_message = """Change team name

Team name XYZ is wrong, we should be called ABC instead.
See JIRA-123[1].

[1]: https://example.com/tickets/JIRA-123
"""

# Override the local git commit author
commit_author_name = "John Smith"
commit_author_email = "smith@example.com"

branch_name = "fix-team-name"

# PatchDriver class to use.
# Selected via plugin name, from "massdriver.drivers" entrypoint
driver_name = "teamname-changer"

# Config given to the PatchDriver instance
driver_config = { filename = "catalog.yaml", team_name = "Core Team" }

# And a forge = PR creation after Migration
[mass-driver.forge]
forge_name = "github"

base_branch = "main"

head_branch = "fix-teamname"
draft_pr = true
pr_title = "[JIRA-123] Bump counter.txt to 1"
pr_body = """Change team name

Team name XYZ is wrong, we should be called ABC instead.
See JIRA-123[1].

[1]: https://example.com/tickets/JIRA-123
"""

# Do you need to git push the branch before PR?
git_push_first = true

```
<!-- source-activity -->
### Discovering repos using a Source


Sometimes, the repos we want to apply patches to is a dynamic thing, coming from
tooling, like a Github repository search, some compliance tool report, service
catalogue, etc.

To address this, mass-driver can use a Source plugin to discover repositories to
apply activities to.

``` toml
# An Activity file with a file-list Source
[mass-driver.source]
source_name = "repo-list"
# Source 'repo-list' takes a 'repos' list of cloneable URLs:
[mass-driver.source.source_config]
repos = [
  "git@github.com:OverkillGuy/mass-driver.git",
  "git@github.com:OverkillGuy/mass-driver-plugins.git",
]
```

Because we included a `Source`, we can omit the CLI flags `--repo-path` or
`--repo-filelist`, to instead rely on the activity's config to discover the
repos.

``` shell
mass-driver run activity.toml
```

Smarter Sources can use more elaborate parameters, maybe even secret parameters
like API tokens.


Note that to pass secrets safely at runtime, config parameters passed via
`source_config` in file format can be passed as envvar, using prefix `SOURCE_`.
So we could have avoided the `repos` entry in file, by providing a
`SOURCE_REPOS` envvar instead. This feature works because the Source class
derives from `Pydantic.BaseSettings`.

As a `Source` developer, though, you should really look into usage of
`Pydantic.SecretStr` to avoid leaking the secret when config or result is
stored. See [Pydantic docs on Secret
fields](https://docs.pydantic.dev/1.10/usage/types/#secret-types).


### Using the scanners
Before doing any actual migration, we might want to explore existing repositories to see what kind of change is required.

Mass-driver provides for this usecase via the scanners plugin system, enabling a simple python function to be run against many repos, with the purpose of gathering information.

<!-- scanner-activity -->
Let's define an Activity file specifying a list of scanners to run:

``` toml
# An Activity file for scanning
[mass-driver.scan]
scanner_names = ["root-files", "dockerfile-from"]
```
This can be run just like a migration:

``` shell
mass-driver run scan.toml --repo-filelist repos.txt
```
### Reviewing bulk PR status

Have a look at the `view-pr` subcommand for reviewing the status of many PRs at
once.

It requires specifying a forge like `github`, along with setting any required
tokens, such as via `FORGE_TOKEN` envvar for `github` forge.

``` shell
export FORGE_TOKEN=xyz
mass-driver view-pr github \
    --pr \
    https://github.com/OverkillGuy/mass-driver/pull/1 https://github.com/OverkillGuy/mass-driver/pull/2
# Can specify multiple PRs as a args list
```
Equivalently via a file full of newline-delimited PR URLs

``` shell
export FORGE_TOKEN=xyz
mass-driver view-pr github --pr-filelist prs.txt
```

With sample result:

```none
> Pull request review mode!
[001/004] Fetching PR status...
[002/004] Fetching PR status...
[003/004] Fetching PR status...
[004/004] Fetching PR status...

Merged:
https://github.com/OverkillGuy/mass-driver/pull/1
https://github.com/OverkillGuy/sphinx-needs-test/pull/1

Closed (but not merged):
https://github.com/OverkillGuy/mass-driver/pull/2
https://github.com/OverkillGuy/sphinx-needs-test/pull/2

In summary: 4 unique PRs, of which...
- 002 (50.0%) merged
- 002 (50.0%) closed (but not merged)
```

## Development

### Python setup

This repository uses Python3.11, using
[Poetry](https://python-poetry.org) as package manager to define a
Python package inside `src/mass_driver/`.

`poetry` will create virtual environments if needed, fetch
dependencies, and install them for development.


For ease of development, a `Makefile` is provided, use it like this:

	make  # equivalent to "make all" = install lint docs test build
	# run only specific tasks:
	make install
	make lint
	make test
	# Combine tasks:
	make install test

Once installed, the module's code can now be reached through running
Python in Poetry:

	$ poetry run python
	>>> from mass_driver import main
	>>> main("blabla")


This codebase uses [pre-commit](https://pre-commit.com) to run linting
tools like `flake8`. Use `pre-commit install` to install git
pre-commit hooks to force running these checks before any code can be
committed, use `make lint` to run these manually. Testing is provided
by `pytest` separately in `make test`.

### Documentation

Documentation is generated via [Sphinx](https://www.sphinx-doc.org/en/master/),
using the cool [myst_parser](https://myst-parser.readthedocs.io/en/latest/)
plugin to support Markdown files like this one.

Other Sphinx plugins provide extra documentation features, like the recent
[AutoAPI](https://sphinx-autoapi.readthedocs.io/en/latest/index.html) to
generate API reference without headaches.

To build the documentation, run

    # Requires the project dependencies provided by "make install"
    make docs
	# Generates docs/build/html/

To browse the website version of the documentation you just built, run:

    make docs-serve

And remember that `make` supports multiple targets, so you can generate the
documentation and serve it:

    make docs docs-serve

## License

This project is released under GPLv3 or later. See `COPYING` file for GPLv3
license details.

### Templated repository

This repo was created by the cookiecutter template available at
https://github.com/OverkillGuy/python-template, using commit hash: `5c882f2e22311a2307263d14877c8229a2ed6961`.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/OverkillGuy/mass-driver",
    "name": "mass-driver",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.11,<4.0",
    "maintainer_email": "",
    "keywords": "repo-automation",
    "author": "Jb Doyon",
    "author_email": "jb@jiby.tech",
    "download_url": "https://files.pythonhosted.org/packages/8c/ff/2a85c1e9dcaa650827405f59e02bb0740c7f61752b513b82b76575e6e4c9/mass_driver-0.16.2.tar.gz",
    "platform": null,
    "description": "# Mass Driver\n![PyPI](https://img.shields.io/pypi/v/mass-driver)\n![PyPI - License](https://img.shields.io/pypi/l/mass-driver)\n\nSend bulk repo change requests.\n\nThis repository is on Github: [Overkillguy/mass-driver](https://github.com/OverkillGuy/mass-driver/).\n\nRequires Python 3.11.\n## Usage\n\nSee also the docs at [jiby.tech/mass-driver/](https://jiby.tech/mass-driver/)\n\n### Installation\n\nInstall the package:\n\n    pip install mass-driver\n\nWe recommend you install CLIs via [pipx](https://pypa.github.io/pipx/), for dependency isolation:\n\n    pipx install mass-driver\n\nIf you want to install from a git branch rather than Pypi:\n\n    pipx install https://github.com/OverkillGuy/mass-driver\n    # See pipx docs: https://pypa.github.io/pipx/#running-from-source-control\n\n### Running the tool\n\nUse the help menu to start with:\n\n    mass-driver --help\n\n### Preparing a change\n\nLet's prepare for doing a change over dozens of repositories.\nWe'll need to find a `PatchDriver` that suits our needs, and configure it accordingly.\n\nList available `PatchDriver`s via:\n\n    mass-driver drivers --list\n    # The docs for a single driver:\n    mass-driver driver --info counter\n\nRemember, `PatchDriver`s are exposed via a python plugin system, which means anyone can package their own!\n\nOnce you've got a driver, you should create a Migration file, in TOML:\n\n``` toml\n# Saved as \"fix_teamname.toml\"\n[mass-driver.migration]\n# As seen in 'git log':\ncommit_message = \"\"\"Change team name\n\nTeam name XYZ is wrong, we should be called ABC instead.\nSee JIRA-123[1].\n\n[1]: https://example.com/tickets/JIRA-123\n\"\"\"\n\n# Override the local git commit author\ncommit_author_name = \"John Smith\"\ncommit_author_email = \"smith@example.com\"\n\nbranch_name = \"fix-team-name\"\n\n# PatchDriver class to use.\n# Selected via plugin name, from \"massdriver.drivers\" entrypoint\ndriver_name = \"teamname-changer\"\n\n# Config given to the PatchDriver instance\ndriver_config = { filename = \"catalog.yaml\", team_name = \"Core Team\" }\n\n# Note: No \"forge\" section = no forge activity to pursue (no PR will be created)\n```\n\nWith this file named `fix_teamname.toml` in hand, we can apply the change\nlocally, either against a local repo we've already cloned:\n\n``` shell\nmass-driver run fix_teamname.toml --repo-path ~/workspace/my-repo/\n```\nOr against a repo being cloned from URL:\n\n``` shell\nmass-driver run fix_teamname.toml --repo-path 'git@github.com:OverkillGuy/sphinx-needs-test.git'\n```\n\nThe cloned repo will be under `.mass_driver/repos/USER/REPONAME/`.\nWe should expect a branch named `fix-team-name` with a single commit.\n\nTo apply the change over a list of repositories, create a file with relevant\nrepos:\n\n``` shell\ncat <<EOF > repos.txt\ngit@github.com:OverkillGuy/sphinx-needs-test.git\ngit@github.com:OverkillGuy/speeders.git\nEOF\n\nmass-driver run fix_teamname.toml --repo-filelist repos.txt\n```\n\n### Creating PRs\n\nOnce the commits are done locally, let's send them up as PR a second step.\nFor this, we'll be creating a second activity file containing a Forge definition.\n\nSimilarly, forges can be listed and detailed:\n\n```shell\nmass-driver forges --list\n# The docs for a single forge:\nmass-driver forge --info counter\n```\n\nConsider using the `forge_name = \"github\"`.\nCreate a new Activity with a Forge:\n\n``` toml\n# An Activity made up of just a forge\n[mass-driver.forge]\nforge_name = \"github\"\n\nbase_branch = \"main\"\n\nhead_branch = \"fix-teamname\"\ndraft_pr = true\npr_title = \"[JIRA-123] Bump counter.txt to 1\"\npr_body = \"\"\"Change team name\n\nTeam name XYZ is wrong, we should be called ABC instead.\nSee JIRA-123[1].\n\n[1]: https://example.com/tickets/JIRA-123\n\"\"\"\n\n# Do you need to git push the branch before PR?\ngit_push_first = true\n```\n\nNow run mass-driver, remembering to set the `FORGE_TOKEN` envvar for a Github/other auth token.\n\n``` shell\nexport FORGE_TOKEN=\"ghp_supersecrettoken\"\nmass-driver run fix_teamname_forge.toml --repo-filelist repos.txt\n```\n\n### Combining migration then forge\nSometimes, we wish to expedite both the committing and the PR creation in a single move.\n\nThe Activity file can contain both sections:\n\n``` toml\n# An activity made up of first a Migration, then a Forge\n[mass-driver.migration]\n# As seen in 'git log':\ncommit_message = \"\"\"Change team name\n\nTeam name XYZ is wrong, we should be called ABC instead.\nSee JIRA-123[1].\n\n[1]: https://example.com/tickets/JIRA-123\n\"\"\"\n\n# Override the local git commit author\ncommit_author_name = \"John Smith\"\ncommit_author_email = \"smith@example.com\"\n\nbranch_name = \"fix-team-name\"\n\n# PatchDriver class to use.\n# Selected via plugin name, from \"massdriver.drivers\" entrypoint\ndriver_name = \"teamname-changer\"\n\n# Config given to the PatchDriver instance\ndriver_config = { filename = \"catalog.yaml\", team_name = \"Core Team\" }\n\n# And a forge = PR creation after Migration\n[mass-driver.forge]\nforge_name = \"github\"\n\nbase_branch = \"main\"\n\nhead_branch = \"fix-teamname\"\ndraft_pr = true\npr_title = \"[JIRA-123] Bump counter.txt to 1\"\npr_body = \"\"\"Change team name\n\nTeam name XYZ is wrong, we should be called ABC instead.\nSee JIRA-123[1].\n\n[1]: https://example.com/tickets/JIRA-123\n\"\"\"\n\n# Do you need to git push the branch before PR?\ngit_push_first = true\n\n```\n<!-- source-activity -->\n### Discovering repos using a Source\n\n\nSometimes, the repos we want to apply patches to is a dynamic thing, coming from\ntooling, like a Github repository search, some compliance tool report, service\ncatalogue, etc.\n\nTo address this, mass-driver can use a Source plugin to discover repositories to\napply activities to.\n\n``` toml\n# An Activity file with a file-list Source\n[mass-driver.source]\nsource_name = \"repo-list\"\n# Source 'repo-list' takes a 'repos' list of cloneable URLs:\n[mass-driver.source.source_config]\nrepos = [\n  \"git@github.com:OverkillGuy/mass-driver.git\",\n  \"git@github.com:OverkillGuy/mass-driver-plugins.git\",\n]\n```\n\nBecause we included a `Source`, we can omit the CLI flags `--repo-path` or\n`--repo-filelist`, to instead rely on the activity's config to discover the\nrepos.\n\n``` shell\nmass-driver run activity.toml\n```\n\nSmarter Sources can use more elaborate parameters, maybe even secret parameters\nlike API tokens.\n\n\nNote that to pass secrets safely at runtime, config parameters passed via\n`source_config` in file format can be passed as envvar, using prefix `SOURCE_`.\nSo we could have avoided the `repos` entry in file, by providing a\n`SOURCE_REPOS` envvar instead. This feature works because the Source class\nderives from `Pydantic.BaseSettings`.\n\nAs a `Source` developer, though, you should really look into usage of\n`Pydantic.SecretStr` to avoid leaking the secret when config or result is\nstored. See [Pydantic docs on Secret\nfields](https://docs.pydantic.dev/1.10/usage/types/#secret-types).\n\n\n### Using the scanners\nBefore doing any actual migration, we might want to explore existing repositories to see what kind of change is required.\n\nMass-driver provides for this usecase via the scanners plugin system, enabling a simple python function to be run against many repos, with the purpose of gathering information.\n\n<!-- scanner-activity -->\nLet's define an Activity file specifying a list of scanners to run:\n\n``` toml\n# An Activity file for scanning\n[mass-driver.scan]\nscanner_names = [\"root-files\", \"dockerfile-from\"]\n```\nThis can be run just like a migration:\n\n``` shell\nmass-driver run scan.toml --repo-filelist repos.txt\n```\n### Reviewing bulk PR status\n\nHave a look at the `view-pr` subcommand for reviewing the status of many PRs at\nonce.\n\nIt requires specifying a forge like `github`, along with setting any required\ntokens, such as via `FORGE_TOKEN` envvar for `github` forge.\n\n``` shell\nexport FORGE_TOKEN=xyz\nmass-driver view-pr github \\\n    --pr \\\n    https://github.com/OverkillGuy/mass-driver/pull/1 https://github.com/OverkillGuy/mass-driver/pull/2\n# Can specify multiple PRs as a args list\n```\nEquivalently via a file full of newline-delimited PR URLs\n\n``` shell\nexport FORGE_TOKEN=xyz\nmass-driver view-pr github --pr-filelist prs.txt\n```\n\nWith sample result:\n\n```none\n> Pull request review mode!\n[001/004] Fetching PR status...\n[002/004] Fetching PR status...\n[003/004] Fetching PR status...\n[004/004] Fetching PR status...\n\nMerged:\nhttps://github.com/OverkillGuy/mass-driver/pull/1\nhttps://github.com/OverkillGuy/sphinx-needs-test/pull/1\n\nClosed (but not merged):\nhttps://github.com/OverkillGuy/mass-driver/pull/2\nhttps://github.com/OverkillGuy/sphinx-needs-test/pull/2\n\nIn summary: 4 unique PRs, of which...\n- 002 (50.0%) merged\n- 002 (50.0%) closed (but not merged)\n```\n\n## Development\n\n### Python setup\n\nThis repository uses Python3.11, using\n[Poetry](https://python-poetry.org) as package manager to define a\nPython package inside `src/mass_driver/`.\n\n`poetry` will create virtual environments if needed, fetch\ndependencies, and install them for development.\n\n\nFor ease of development, a `Makefile` is provided, use it like this:\n\n\tmake  # equivalent to \"make all\" = install lint docs test build\n\t# run only specific tasks:\n\tmake install\n\tmake lint\n\tmake test\n\t# Combine tasks:\n\tmake install test\n\nOnce installed, the module's code can now be reached through running\nPython in Poetry:\n\n\t$ poetry run python\n\t>>> from mass_driver import main\n\t>>> main(\"blabla\")\n\n\nThis codebase uses [pre-commit](https://pre-commit.com) to run linting\ntools like `flake8`. Use `pre-commit install` to install git\npre-commit hooks to force running these checks before any code can be\ncommitted, use `make lint` to run these manually. Testing is provided\nby `pytest` separately in `make test`.\n\n### Documentation\n\nDocumentation is generated via [Sphinx](https://www.sphinx-doc.org/en/master/),\nusing the cool [myst_parser](https://myst-parser.readthedocs.io/en/latest/)\nplugin to support Markdown files like this one.\n\nOther Sphinx plugins provide extra documentation features, like the recent\n[AutoAPI](https://sphinx-autoapi.readthedocs.io/en/latest/index.html) to\ngenerate API reference without headaches.\n\nTo build the documentation, run\n\n    # Requires the project dependencies provided by \"make install\"\n    make docs\n\t# Generates docs/build/html/\n\nTo browse the website version of the documentation you just built, run:\n\n    make docs-serve\n\nAnd remember that `make` supports multiple targets, so you can generate the\ndocumentation and serve it:\n\n    make docs docs-serve\n\n## License\n\nThis project is released under GPLv3 or later. See `COPYING` file for GPLv3\nlicense details.\n\n### Templated repository\n\nThis repo was created by the cookiecutter template available at\nhttps://github.com/OverkillGuy/python-template, using commit hash: `5c882f2e22311a2307263d14877c8229a2ed6961`.\n",
    "bugtrack_url": null,
    "license": "GPL-3.0-or-later",
    "summary": "Send bulk repo change requests",
    "version": "0.16.2",
    "project_urls": {
        "Documentation": "https://jiby.tech/mass-driver",
        "Homepage": "https://github.com/OverkillGuy/mass-driver",
        "Repository": "https://github.com/OverkillGuy/mass-driver"
    },
    "split_keywords": [
        "repo-automation"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9f70d33f0c38ffdb5e7c48d6aec19bc3de29f81df3a487726e67a67c15d6c10a",
                "md5": "e9d98b5a1ad9be2257efb7e067727130",
                "sha256": "297acba2fa6d693faa73306d8d901c0bebc54ca918b0f57fed53d95a3dd354a4"
            },
            "downloads": -1,
            "filename": "mass_driver-0.16.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "e9d98b5a1ad9be2257efb7e067727130",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11,<4.0",
            "size": 59023,
            "upload_time": "2023-07-25T19:24:00",
            "upload_time_iso_8601": "2023-07-25T19:24:00.686140Z",
            "url": "https://files.pythonhosted.org/packages/9f/70/d33f0c38ffdb5e7c48d6aec19bc3de29f81df3a487726e67a67c15d6c10a/mass_driver-0.16.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8cff2a85c1e9dcaa650827405f59e02bb0740c7f61752b513b82b76575e6e4c9",
                "md5": "4b32d47599e6577475fdfdb229432c0f",
                "sha256": "41162605b6a410ae282286bcd1b035c2b6dd239b1c033291b7e97fc5cdaf20f6"
            },
            "downloads": -1,
            "filename": "mass_driver-0.16.2.tar.gz",
            "has_sig": false,
            "md5_digest": "4b32d47599e6577475fdfdb229432c0f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11,<4.0",
            "size": 34649,
            "upload_time": "2023-07-25T19:24:02",
            "upload_time_iso_8601": "2023-07-25T19:24:02.761368Z",
            "url": "https://files.pythonhosted.org/packages/8c/ff/2a85c1e9dcaa650827405f59e02bb0740c7f61752b513b82b76575e6e4c9/mass_driver-0.16.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-07-25 19:24:02",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "OverkillGuy",
    "github_project": "mass-driver",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "mass-driver"
}
        
Elapsed time: 0.33911s