@@ -38,11 +38,208 @@ This creates a network of packages that work together seamlessly.
3838
3939## How
4040
41- Publishing involves:
41+ ### Building your package
4242
43- - Building your package into distribution formats (typically a wheel ` .whl ` and source distribution ` .tar.gz ` ).
44- - Uploading these distributions to PyPI.
45- - Making your package discoverable and installable by anyone in the Python community.
43+ Before you can publish your package, you need to build it into distribution formats that ` pip ` can install.
44+ The two standard formats are:
4645
47- In the days of old, you may have executed these steps manually.
46+ - ** Wheel (` .whl ` )** : A pre-built binary distribution that installs quickly
47+ - ** Source distribution (` .tar.gz ` )** : The raw source code that gets built during installation
4848
49+ To create these distributions, you need a ** build backend** .
50+ The workshop template uses [ Hatchling] ( https://hatch.pypa.io/ ) , a modern, standards-compliant build system.
51+ You can see this configured in your ` pyproject.toml ` :
52+
53+ ``` toml {.no-copy}
54+ [build-system ]
55+ requires = [" hatchling" ]
56+ build-backend = " hatchling.build"
57+ ```
58+
59+ This tells Python's build tools to use Hatchling when building your package.
60+
61+ To build your package, use the ` build ` module (part of the Python Packaging Authority's standard tooling):
62+
63+ ``` bash
64+ pip install build
65+ python -m build
66+ ```
67+
68+ ``` console {.no-copy}
69+ * Creating isolated environment: venv+pip...
70+ * Installing packages in isolated environment:
71+ - hatchling
72+ * Getting build dependencies for sdist...
73+ * Building sdist...
74+ * Building wheel from sdist...
75+ * Creating isolated environment: venv+pip...
76+ * Installing packages in isolated environment:
77+ - hatchling
78+ * Getting build dependencies for wheel...
79+ * Building wheel...
80+ Successfully built dev-tutorial-<YOUR_USERNAME>-0.0.1.tar.gz and dev_tutorial_<YOUR_USERNAME>-0.0.1-py3-none-any.whl
81+ ```
82+
83+ This creates a ` dist/ ` directory containing both distribution formats:
84+
85+ ``` bash
86+ ls dist/
87+ ```
88+
89+ ``` console {.no-copy}
90+ dev-tutorial-<YOUR_USERNAME>-0.0.1.tar.gz
91+ dev_tutorial_<YOUR_USERNAME>-0.0.1-py3-none-any.whl
92+ ```
93+
94+ ### Versioning
95+
96+ Notice that the built distributions include a version number (` 0.0.1 ` ).
97+ This comes from the ` __version__ ` variable in ` src/dev_tutorial_<YOUR_USERNAME>/__about__.py ` :
98+
99+ ``` python {.no-copy}
100+ __version__ = " 0.0.1"
101+ ```
102+
103+ Version numbers help users understand what changes between releases.
104+ The standard approach is [ Semantic Versioning (SemVer)] ( https://semver.org/ ) , which uses the format ` MAJOR.MINOR.PATCH ` :
105+
106+ - ** MAJOR** : Increment when you make backwards-incompatible API changes (e.g., ` 1.0.0 ` → ` 2.0.0 ` )
107+ - ** MINOR** : Increment when you add functionality in a backward-compatible manner (e.g., ` 1.0.0 ` → ` 1.1.0 ` )
108+ - ** PATCH** : Increment when you make backward-compatible bug fixes (e.g., ` 1.0.0 ` → ` 1.0.1 ` )
109+
110+ For example:
111+ - ` 0.0.1 ` → ` 0.0.2 ` : Fixed a bug
112+ - ` 0.0.2 ` → ` 0.1.0 ` : Added a new feature
113+ - ` 0.1.0 ` → ` 1.0.0 ` : First stable release or breaking changes
114+
115+ !!!info "Version 0.x.x"
116+
117+ Versions starting with `0.` (like `0.1.0`) indicate the package is still in early development.
118+ Users should expect breaking changes between minor versions.
119+ Once your API is stable, release version `1.0.0`.
120+
121+ ### Creating API tokens
122+
123+ Before you can upload to PyPI or Test PyPI, you need to create an ** API token** for authentication.
124+ PyPI no longer accepts username/password authentication - tokens are required.
125+
126+ For Test PyPI:
127+
128+ 1 . Create an account at [ https://test.pypi.org/ ] ( https://test.pypi.org/ )
129+ 2 . Go to Account Settings → API tokens
130+ 3 . Click "Add API token"
131+ 4 . Give it a name (e.g., "dev-tutorial upload")
132+ 5 . Copy the token immediately - it's only shown once!
133+
134+ The token will look like ` pypi-... ` (a long string of characters).
135+
136+ !!!warning "Save your token!"
137+
138+ API tokens are only displayed once when created.
139+ Store it somewhere safe - you'll need it for uploads.
140+ If you lose it, you'll have to create a new one.
141+
142+ ### Uploading to Test PyPI
143+
144+ Before publishing to the real PyPI, it's smart to test with ** Test PyPI** - a separate instance where you can experiment without consequences.
145+
146+ Install ` twine ` , the tool for uploading packages:
147+
148+ ``` bash
149+ pip install twine
150+ ```
151+
152+ Upload your distributions to Test PyPI:
153+
154+ ``` bash
155+ twine upload --repository testpypi dist/*
156+ ```
157+
158+ When prompted:
159+ - ** Username** : Enter ` __token__ ` (exactly as written, with double underscores)
160+ - ** Password** : Paste your API token (including the ` pypi- ` prefix)
161+
162+ Once uploaded, your package will be available at:
163+
164+ ```
165+ https://test.pypi.org/project/dev-tutorial-<YOUR_USERNAME>/
166+ ```
167+
168+ To install from Test PyPI and verify it works:
169+
170+ ``` bash
171+ pip install --index-url https://test.pypi.org/simple/ dev-tutorial-< YOUR_USERNAME>
172+ ```
173+
174+ !!! info "The real PyPI"
175+
176+ Test PyPI is a separate service from the real PyPI.
177+ Packages uploaded there are periodically deleted, and dependencies from the real PyPI might not be available.
178+ It's purely for testing the upload process!
179+ Publishing to the real PyPI is very similar, but I would only do it with _your_ proper package when it's ready.
180+
181+ ### Automating with GitHub Actions
182+
183+ Manually building and uploading packages works, but it's error-prone and tedious.
184+ A better approach is to automate the entire publishing process with GitHub Actions.
185+ The workshop template includes a workflow file ` .github/workflows/cd.yml ` that automatically publishes to Test PyPI when you push a version tag.
186+
187+ !!! question "If you are feeling lost"
188+
189+ I know this all seems like a lot. It is!
190+ The main goal here is to expose you to this approach of doing things, you can take your time in understanding all the aspects.
191+
192+ ### Setting up trusted publishing
193+
194+ Instead of storing API tokens as secrets, the workflow uses ** trusted publishing** - a modern, more secure authentication method.
195+ With trusted publishing, PyPI trusts GitHub Actions to publish on your behalf without needing to manage tokens.
196+
197+ To set this up on Test PyPI:
198+
199+ 1 . Go to [ https://test.pypi.org/ ] ( https://test.pypi.org/ ) and log in
200+ 2 . Click ** Your projects** (but you won't have a project yet - that's okay!)
201+ 3 . Go to ** Publishing** in the left sidebar
202+ 4 . Scroll to ** Add a new pending publisher**
203+ 5 . Fill in the form:
204+ - ** PyPI Project Name** : ` dev-tutorial-<YOUR_USERNAME> ` (your package name)
205+ - ** Owner** : Your GitHub username
206+ - ** Repository name** : ` dev-tutorial-<YOUR_USERNAME> `
207+ - ** Workflow name** : ` cd.yml `
208+ 6 . Click ** Add**
209+
210+ !!!info "How trusted publishing works"
211+
212+ When your GitHub Actions workflow runs, GitHub issues a short-lived identity token that proves the workflow is running from your specific repository.
213+ Test PyPI verifies this token and allows the upload - no long-lived secrets needed!
214+ This is more secure because there are no tokens to leak or rotate.
215+
216+ ### Automated publishing workflow
217+
218+ With trusted publishing and automated workflows, releasing a new version is as simple as:
219+
220+ 1 . Update the version in ` __about__.py ` following SemVer (e.g., ` 0.0.1 ` → ` 0.1.0 ` )
221+ 2 . Commit the bump in version number:
222+
223+ ```
224+ # Update version, commit, tag, and push
225+ git commit -am 'Bump version to 0.1.0'
226+ ```
227+
228+ 3. Create and push a Git tag:
229+
230+ ```
231+ git tag v0.1.0
232+ git push origin main v0.1.0
233+ ```
234+
235+ → **GitHub Actions automatically builds and publishes to Test PyPI!**
236+
237+ Watch the workflow run in the **Actions** tab of your repository.
238+ If something goes wrong, check the workflow logs for error messages.
239+
240+ Once published, verify your package has a new version at `https://test.pypi.org/project/dev-tutorial-<YOUR_USERNAME>/`.
241+
242+ !!! info "This is the end..."
243+
244+ For now, this is the end of this basics of software development workshop.
245+ More material might be coming soon!
0 commit comments