From Local Code to PyPI: Publishing My First Tool

In my previous post, I shared the story of building my first OSINT tool — UserRecon.

In this post, I want to share something equally important:
how I published this tool on PyPI, and why that journey felt like a tragedy-based comedy.




“What Should I Do Next?”

As soon as I finished building the tool, I did what most of us do nowadays.
I opened ChatGPT and asked:

“I have made this tool. What should I do next?”

The answer was simple:

“Publish it on GitHub.”

So I did exactly that.

Then I asked again:

“I also want to publish it on the apt store. Give me step-by-step guidance.”

After reading the response, I realised something important.
Publishing on apt would be too heavy for me right now. For a beginner, PyPI was a much better starting point.

And that’s how my PyPI journey began.


Tragedy + Comedy = PyPI

The first step was extremely simple:

Sign up on pypi.org

The real journey started after that.

Before anything else, I needed to understand the project structure required for PyPI.
Once I saw it, I realised I was missing one important file:

setup.py

Naturally, the next question in my head was:

“What exactly is setup.py?”


Understanding setup.py

ChatGPT gave me a basic template for setup.py, which helped.
But I still wanted clarity, so I Googled it.

In simple words, I understood this:

setup.py contains metadata about the tool and its developer.
It includes:

  • tool name

  • version

  • description

  • dependencies

  • installation details

Once I wrote setup.py and set the version to 1.0.0, I moved forward.


Building the Project (and Testing My Patience)

I opened the terminal at the root of the project and ran:

pip install build twine python -m build

That second command…
was the real patience test.

I got my first error.
It was completely unreadable.

I copied the error and pasted it into ChatGPT.

The response shocked me:

“You do not have a README.md file.”

I did have a README.

So I pasted my README content into ChatGPT.

That’s when the comedy hit.

The issue wasn’t the README itself —
I had used ''' (triple quotes) instead of ``` (backticks) for code blocks.

Such a silly mistake ๐Ÿ˜‚
I fixed it and ran the build command again.


Another Error. Of Course.

This time the error said:

README.md is not included in the source distribution.

That’s when I learned something new.

A PyPI build has two parts:

  • sdist (source distribution)

  • wheel

To tell PyPI that README.md is part of the source, I had to create a new file:

MANIFEST.in

And include:

include README.md

After that, the build finally succeeded.
(Of course, I ran it again just to be sure.)


Uploading to PyPI (Yet Another Test)

Now came the upload step:

twine upload dist/*

It asked me for an API key.

And I was like:

“For what? From where?” ๐Ÿคจ

Answer:

From your PyPI account.

So I created an API key.

During this whole process, PyPI kept asking for passwords — and as cybersecurity people, we all know how simple and memorable our passwords are ๐Ÿ˜ต‍๐Ÿ’ซ.

Eventually…
the tool was uploaded.


“It Installed… But Didn’t Work”

I tested it by running:

pip install userrecon

And boom — error.

But it worked perfectly on my local system.

After debugging, I realised the issue:
I hadn’t created an __init__.py file.

Locally, it worked fine.
But PyPI expects package structure to be explicit, even if the file is empty.

So I added an empty __init__.py, bumped the version to 1.0.1, and uploaded again.


One More Bug (Last One, I Promise)

After installing again, I hit another error.

This time it said:

Module platforms not found.

I was confused.
The module existed and worked locally.

After checking everything, ChatGPT pointed out the issue:

When importing a module from the same package, PyPI expects relative imports.

So instead of:

        import platforms

I had to use:

        from . import platforms

These mistakes  forced me to upload four different versions of the same tool.


The Moment It Finally Worked

When everything finally worked —
installation, execution, imports — I genuinely couldn’t hold it in.

I banged the table and said:

YES. I DID IT. ๐Ÿ†


Final Thoughts

Publishing on PyPI taught me more than writing the tool itself:

  • how packaging works

  • how environments differ

  • why “works on my machine” is not enough

  • and how patience is part of engineering

I’ll be back with another post —

Until then:

  • keep breaking things

  • keep fixing them

  • and don’t fear mistakes — they’re part of the build.

Comments

Popular posts from this blog

Obfuscation and Deobfuscation

OSINT Basics: Introduction, Scope, and Ethical Boundaries (Part 1)

Cybersecurity Devices and Technologies Part 1