diff --git a/_static/pyos.css b/_static/pyos.css
index 9426d7e92..c24d4407a 100644
--- a/_static/pyos.css
+++ b/_static/pyos.css
@@ -42,6 +42,25 @@ html, body {
}
}
+/* Make sure the header nav is centered */
+.navbar-header-items .me-auto, .me-auto .navbar-header-items__center {
+ margin-left: auto!important;
+ margin-right: auto!important;
+}
+
+/* custom fonts */
+
+html, body {
+ font-size: 1.02rem;
+}
+
+/* Allow the center content to expand to wide on wide screens */
+@media (min-width: 960px){
+.bd-page-width {
+ max-width: min(100%, 1600px)!important;
+ }
+}
+
/* Make sure the header nav is centered - not sure why it's not overriding*/
.navbar-header-items .me-auto, .me-auto .navbar-header-items__center {
margin-left: auto!important;
@@ -52,6 +71,7 @@ html, body {
html, body {
font-size: 1.02rem;
+ font-family: 'Poppins', sans-serif!important;
}
body p {
diff --git a/_templates/header.html b/_templates/header.html
new file mode 100644
index 000000000..c1c673038
--- /dev/null
+++ b/_templates/header.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/_templates/nav.html b/_templates/nav.html
new file mode 100644
index 000000000..1d6fb07c3
--- /dev/null
+++ b/_templates/nav.html
@@ -0,0 +1 @@
+
this is a test does it work?
diff --git a/images/packaging-workflow.png b/images/packaging-workflow.png
new file mode 100644
index 000000000..10b61d335
Binary files /dev/null and b/images/packaging-workflow.png differ
diff --git a/images/tutorials/code-to-script-diagram.png b/images/tutorials/code-to-script-diagram.png
new file mode 100644
index 000000000..5f14f3ed3
Binary files /dev/null and b/images/tutorials/code-to-script-diagram.png differ
diff --git a/images/tutorials/github-new-repo.png b/images/tutorials/github-new-repo.png
new file mode 100644
index 000000000..fa1cca8b9
Binary files /dev/null and b/images/tutorials/github-new-repo.png differ
diff --git a/images/tutorials/github-repo-sidebar.png b/images/tutorials/github-repo-sidebar.png
new file mode 100644
index 000000000..6f6e966d4
Binary files /dev/null and b/images/tutorials/github-repo-sidebar.png differ
diff --git a/images/tutorials/packaging-steps.png b/images/tutorials/packaging-steps.png
new file mode 100644
index 000000000..6a4086143
Binary files /dev/null and b/images/tutorials/packaging-steps.png differ
diff --git a/images/tutorials/publish-package.png b/images/tutorials/publish-package.png
new file mode 100644
index 000000000..324e27d07
Binary files /dev/null and b/images/tutorials/publish-package.png differ
diff --git a/images/tutorials/toolbox.jpg b/images/tutorials/toolbox.jpg
new file mode 100644
index 000000000..ef34f6263
Binary files /dev/null and b/images/tutorials/toolbox.jpg differ
diff --git a/images/tutorials/view-license-github.png b/images/tutorials/view-license-github.png
new file mode 100644
index 000000000..57dc8d2ae
Binary files /dev/null and b/images/tutorials/view-license-github.png differ
diff --git a/tutorials/2-add-readme.md b/tutorials/2-add-readme.md
new file mode 100644
index 000000000..500665892
--- /dev/null
+++ b/tutorials/2-add-readme.md
@@ -0,0 +1,171 @@
+# Add a README file to your Python package
+
+
+
+
+
+In the [previous lesson](1-installable-code.md) you:
+
+1. Learned how create the basic structure of a Python package and
+2. How to make your code `pip` installable.
+
+:::{admonition} Learning objectives
+
+In this lesson you will learn:
+
+1. How to add a **README.md** file to your package.
+2. What the core elements of a **README.md** file are.
+
+:::
+
+
+::::{grid} 2
+
+:::{grid-item}
+:columns: 4
+:class: sd-m-auto
+
+```{button-link} https://www.pyopensci.org/python-package-guide/documentation/repository-files/readme-file-best-practices.html
+:color: secondary
+:class: sd-rounded-pill float-left
+
+ pyOpenSci README file overview guide.
+```
+
+:::
+
+:::{grid-item}
+:columns: 4
+:class: sd-m-auto
+
+```{button-link} https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes
+:color: secondary
+:class: sd-rounded-pill float-left
+
+ Guide: about `README.md` files
+```
+:::
+
+
+:::{grid-item}
+:columns: 4
+:class: sd-m-auto
+
+empty
+:::
+::::
+
+
+
+## What is a README file?
+
+The `README.md` file is a file located at the root of your GitHub or GitLab repo that helps
+a user understand:
+
+- You package's name and what it does
+- The current development "state" of the package (through badges)
+- How to use your package: this might include a short get started demo that shows someone how to import and quickly use your package. Or it could be a set of links to get started tutorials in your documentation.
+- How to contribute to your package: normally you would link to a `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md` file in this part of the readme
+- How to cite your package
+
+Your **README.md** file is important as it is often the first thing that someone sees before they install your package. The README file also will be used to populate your PyPI landing page. So it's good to create this file before you publish to PyPI.
+
+### GitHub highlights your README file as a core file
+
+Every GitHub repository landing page has a right hand side bar that lists and links to elements of your package including:
+
+- A link to your Readme file
+- Your license
+- Number of Stars and
+- Number of Forks
+
+:::{figure-md} github-sidebar
+
+
+The README.md file is not only the landing page for your package, it also is listed as one of the core elements describing your package repository on GitHub.
+:::
+
+## Create a README.md file for your package
+
+- To get started if it doesn't already exist, create a file called `README.md` in your local GitHub repository.
+
+- At the top of the `README.md` file, add the name of your package (and a logo if you have one). It's fine if you don't have a logo.
+
+- Next add the following sections to your `README.md` file
+
+1. **Badges:** Add any badges below the name of your file. if you don't have badges yet, that is ok. you'll have at least one once you [publish your package to PyPI](6-publish-pypi) in lesson 4.
+2. **Package overview:** Below the badges, add a section that provides an easy-to-understand overview of what your package does. Keep this section short and if you can avoid jargon or define technical words to make the description accessible to more people.
+3. **Installation Instructions:** Below the description add installation instructions. this might tell people how to install your package `pip install packagename` or `conda install`... You can come back and add this information after you publish to PyPI in lesson 4.
+4. **Additional Setup Information** In this section also briefly document (or link to documentation for) any additional setup that is required to use your package. This might include tokens or authentication information if it is applicable to your package. Or additional installations of tools such as GDAL, etc. Note: many packages won't need any additional information here!
+5. **How to use your package:** Next add a brief demo of how to use your package. this might include a small code chunk that demonstrates importing and a quick call to functionality in your package.
+6. **Descriptive links to docs:** Unless you already have your documentation online, you can leave this section empty for now. this section would include links to tutorials or documentation get-started pages that will help your users understand how to use your package.
+7. **Community section:** this is where you'll add links to your contributing guide and `CODE_OF_CONDUCT.md` once you create those. You can also leave this empty for now. You will add a [`CODE_OF_CONDUCT.md` file in the next lesson](4-add-license-file.md).
+8. **Citation information:** also leave this empty for now. You can learn how to setup your repository with zenodo to make it citable in [this lesson](extras/2-connect-repo-to-zenodo.md).
+
+Your finished `README.md` file should look something like this:
+
+```markdown
+# pyOpenSci-package
+
+[](https://doi.org/10.5281/zenodo.8365068)
+[](https://github.com/pyOpenSci/software-review/issues/115)
+
+## What packagename does
+
+Short description here using non technical language that describes what your package does.
+
+## How to install
+
+
+To install this package... use:
+
+`pip install packagename`
+
+## OPTIONAL - if you have additional setup instructions add them here. if not, skip this section.
+
+## Get started using packagename
+
+Here add a quick code demo showing a user how to use the package after it is installed.
+
+`
+from packagename.module import xmethod
+
+a = xmethod.dosomething(var1, var2)
+
+`
+
+You can also add any links to this section to tutorials in your documentation.
+
+## Community
+
+Add information here about contributing to your package. Be sure to add links to your `CODE_OF_CONDUCT` file and your development guide. For now this section might be empty. You can go back and fill it in later.
+
+## How to cite packagename
+
+citation information here
+```
+
+## Wrapping up
+
+It's important to consider the information that a new user or contributor might
+need when creating your `README.md` file. Above is a set of recommendations as you are just getting
+started. You may find the need for other elements to be added to this file
+as you further develop your package and as a community begins to use your
+package.
+
+For instance, as you add get-started tutorials to your documentation, you can
+link directly to those in the README file.
+
+In the [next lesson](4-add-license-file.md), you will add a LICENSE file to
+your Python package repository. A license file is critical as it tells users how they legally can (and can't use your package). It also:
+
+* Builds trust with your users
+* Discourages misuse of your package and associated code
diff --git a/tutorials/4-add-license-file.md b/tutorials/4-add-license-file.md
new file mode 100644
index 000000000..6d19090ff
--- /dev/null
+++ b/tutorials/4-add-license-file.md
@@ -0,0 +1,110 @@
+# Add a LICENSE & CODE_OF_CONDUCT to your Python package
+
+In the [previous lesson](2-add-readme) you created a basic `README.md` file for your scientific Python package's repository.
+
+:::{admonition} Learning objectives
+:class: tip
+
+In this lesson you will learn:
+
+1. How to select and add a `LICENSE` file to your package repository with a focus on the GitHub interface.
+2. How to add a `CODE_OF_CONDUCT` file to your package repository.
+3. How you can use the Contributors Covenant website to add generic language as a starting place for your `CODE_OF_CONDUCT`.
+:::
+
+
+
+## Add a LICENSE file to your repository
+
+A license file is a document that contains legal language about how users are allowed to use and reuse your software. Generally, we suggest that you select a permissive license that accommodates the other most commonly used licenses in the scientific Python ecosystem (MIT and BSD-3).
+
+[Click here for an overview of license recommendations for the scientific Python ecosystem](../documentation/repository-files/license-files.html#use-open-permissive-licenses-when-possible)
+
+### Where should the license file live & how do you add it?
+
+Your `LICENSE` file should be placed at the root of your package's repository.
+
+There are two ways to add a license file:
+
+1. When you create a new repository on GitHub, it will ask you if you wish to add a `LICENSE` file at that time.
+2. You can also always go back and add a `LICENSE` file to your repo.
+
+### How to add a LICENSE to your GitHub repository
+
+If you don't already have a `LICENSE` file in your repo, add one now.
+
+* Follow the instructions to select and add a license to your repository on the [ GitHub LICENSE page]( https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-license-to-a-repository) .
+* Once you have added your LICENSE file, be sure to sync your git local repository with the repository on GitHub.com. This means running `git pull` to update your local branch.
+
+:::{admonition} An overview of LICENSES in the scientific Python ecosystem
+:class: note
+
+In the pyOpenSci [packaging guidebook](../documentation/repository-files/license-files.html), we provide an overview of license in the scientific Python ecosystem. We review why license files are important, which ones are most commonly used for scientific software and how to select the correct license.
+
+If you want a broad overview of why licenses are important for protecting open source software, [check out this blog post that overviews the legal side of things.](https://opensource.guide/legal/#just-give-me-the-tldr-on-what-i-need-to-protect-my-project)
+:::
+
+:::{figure-md} github-new-repo
+
+
+When you initially create a repository on GitHub you can add a license
+through their interface.
+:::
+
+
+:::{figure-md} view-license
+
+
+You can also view an overview of the license on GitHub if you view it in the GitHub interface.
+:::
+
+Next, you will add a `CODE_OF_CONDUCT` file to your repository.
+
+## Add a CODE_OF_CONDUCT file to your repo
+
+A `CODE_OF_CONDUCT` file is critical to supporting your community as
+grows around your project. The `CODE_OF_CONDUCT` is important as it:
+1. Establishes guidelines for how users and contributors interact with each other and you in your software repository.
+2. Identifies negative behaviors that you don't want in your interactions. In extreme situations, you can use this language as a moderation tool that can be references when moderating tense conversations.
+
+If you are unsure of what language to add to your `CODE_OF_CONDUCT`
+file, we suggest that you adopt the [contributor covenant language](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) as a starting place.
+
+[](#)
+
+* Add a `CODE_OF_CONDUCT.md` file to your repository if it doesn't
+already exist.
+* Visit the contributor covenant website and [add the language here to the file.](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) [A markdown version can be found here.](https://www.contributor-covenant.org/version/2/1/code_of_conduct/code_of_conduct.md)
+
+
+:::{admonition} Additional Code of Conduct resources
+:class: note
+
+* [ Guide: `CODE_OF_CONDUCT.md` files](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-code-of-conduct-to-your-project)
+* [pyOpenSci package guide `CODE_OF_CONDUCT.md` overview](https://www.pyopensci.org/python-package-guide/documentation/repository-files/code-of-conduct-file.html)
+
+:::
+
+## Sync your LICENSE & COC files to GitHub / GitLab
+
+It's time to sync your repository to ensure that all of the files that you have added locally are on GitHUB.com and all of the files that have been added on GitHub are local.
+
+* If you added a `LICENSE` file to GitHub using the GitHub online interface, be sure to pull it down locally using `git pull`.
+* Similarly be sure to pull or push your newly added `CODE_OF_CONDUCT` to your GitHub repository if you created that file locally.
+
+## Wrap up
+
+In this lesson and the [last lesson](2-add-readme), you have now added:
+
+* `README` file
+* `LICENSE` file
+* `CODE_OF_CONDUCT` file
+
+These are core files that every scientific Python package should include. In the next lessons, you will:
+* [Flesh out your `pyproject.toml` file](5-pyproject-toml) which will support building and publishing your package on PyPI. and
+* You will learn how to [publish your package to PyPI](6-publish-pypi)!
diff --git a/tutorials/5-pyproject-toml.md b/tutorials/5-pyproject-toml.md
new file mode 100644
index 000000000..9b4f43a1d
--- /dev/null
+++ b/tutorials/5-pyproject-toml.md
@@ -0,0 +1,496 @@
+# Make your Python package PyPI ready - pyproject.toml
+
+
+
+
+
+In [the installable code lesson](2-installable-code), you learned how to add the bare minimum information to a `pyproject.toml` file to make it `pip` installable. You then learned:
+
+* How to add a user-friendly `README.md` file to your package and
+* How to add a `LICENSE` and `CODE_OF_CONDUCT` file
+
+To both publish your package to PyPI and also help users find your package,
+you will want to add additional metadata to your `pyproject.toml` file that
+describes the use and contents of your package. You will
+learn how to do that in this lesson.
+
+
+
+:::{admonition} Learning Objectives
+:class: tip
+
+In this lesson you will learn:
+
+1. More about what a `pyproject.toml` file is
+1. How to declare important information (metadata) about your project to prepare for publication to PyPI
+
+If you wish to learn more about the `pyproject.toml` format, [check out this page. ](../package-structure-code/pyproject-toml-python-package-metadata.md)
+:::
+
+
+
+:::{admonition} Lesson highlights TL&DR
+
+When creating your pyproject.toml file, consider the following:
+
+1. As highlighted above, there are only two required metadata that you need to install and publish your package:
+* **[build-system]** and
+* **[project]**.
+2. The **[project]** table stores your package's metadata. There are only two _required_ fields in the **[project]** table:
+* **name=** and
+* **version=**
+3. You should add more metadata to the `[project]` table as it will make it easier for users to find your project on PyPI.
+3. When you are adding classifiers to the [project] table, only use valid values from [PyPIβs classifier page](https://PyPI.org/classifiers/). An invalid value here will raise an error when you build your package or publish to PyPI.
+4. There is no specific order for tables in the `pyproject.toml` file. However fields need to be placed within the correct table sections. For example `requires =` always need to be in the **[build-system]** table.
+5. We suggest (but do not require!) that you include your **[build-system]** table at the top of your `pyproject.toml` file.
+6. Always check the documentation for the package management tool that you are using given some of them read metadata differently. In our pyOpenSci tutorials we are using hatchling. If you decide to use a different build tool you may have to format some of your tables slightly differently.
+:::
+
+## What is a pyproject.toml file?
+
+The `pyproject.toml` file is a human and machine-readable TOML file. The `pyproject.toml` file tells your build tool:
+
+- What build backend to use to build your package (e.g. `setuptools`, `hatchling`, `pdm.build`, etc.)
+- How and where to retrieve your package's version (dynamically vs. statically)
+- What dependencies you are using
+- What versions of Python your package supports
+
+The `pyproject.toml` file can also be used to configure other
+tools such as Black.
+
+### About the .toml format
+
+The **pyproject.toml** file is written in [TOML (Tom's Obvious, Minimal Language) format](https://toml.io/en/). TOML is an easy-to-read structure that is founded on key/value pairs. Each section in the **pyproject.toml** file contains a `[table identifier]`.
+Below that table identifier are key/value pairs that
+support configuration for that particular table.
+
+- Below `[build-system]` is a table in the toml language.
+- Within the build-system table below `requires =` is a key.
+- The associated value for `requires =` is an array containing the value "hatchling".
+
+```toml
+[build-system] # <- this is a table
+requires = ["hatchling"] # requires = is a key and "hatchling" is a value contained within an array specified by square brackets [].
+```
+
+:::{tip}
+Check out the [PyPA documentation](https://packaging.python.org/en/latest/tutorials/packaging-projects/#creating-pyproject-toml) if you are interested in setting build configurations for other tools.
+:::
+
+### What is the metadata for?
+
+As discussed in our [pyproject.toml file overview page in the packaging guide](../package-structure-code/pyproject-toml-python-package-metadata/), the pyproject.toml file is the file that your build tool uses to populate
+a `METADATA` that is included in your final Python package.
+
+This `METADATA` file is then used by PyPI to populate your package's PyPI landing page and help users filter through the tens of thousands of packages published there.
+
+```{figure} ../images/python-build-package/pypi-metadata-classifiers.png
+:scale: 50 %
+:align: center
+:alt: Image showing the left side bar of PyPI for the package xclim. The section at the top says Classifier. Below there is a list of items including Development status, intended audience, License, natural language, operating system, programming language and topic. Below each of those sections are various classifier options." width="300px">
+
+When you add the classifier section to your pyproject.toml
+and your package is built, the build tool organizes the metadata into a format that PyPI can understand and
+represent on your PyPI landing page. These classifiers also allow users to sort through packages by version of python they support, categories and more.
+```
+
+### pyproject.toml files make it easier for potential contributors to understand your package's structure
+
+The `pyproject.toml` file also makes it easy for anyone browsing your GitHub
+repository to quickly understand:
+
+- How your package is built,
+- What Python versions and operating systems it supports
+- What it does, who maintains it
+- And more
+
+The file is also often used to configure tools such as static type checkers (e.g. mypy), linters (e.g. black, ruff), and other tools.
+
+## Time to update your pyproject.toml file
+
+In the last lesson, you created a bare-bones pyproject.toml
+file that contained the core elements needed to build your
+package:
+
+1. A `[build]` table where ou defined your project's backend build tool (`hatchling`)
+2. A `[project]` table where you defined your project's version and name.
+
+The `pyproject.toml` file that you created, looked like this:
+
+```toml
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "pyospackage"
+version = "1.1"
+```
+
+Your next step is to add additional recommended metadata fields that will both
+help users find your package on PyPI and also better describe the scope of your package. Once you add this metadata, you don't have to do it again. These metadata fields will only be updated periodically when you do something such as:
+
+- drop a package dependency
+- modify what Python versions your package supports.
+
+:::{admonition} Python package build backends
+
+While the example above uses the `hatchling` build back-end you can chose
+to use `setuptools` or any other tool you wish to create and build
+your package.
+
+[Learn more about packaging tools and build back-ends here](https://www.pyopensci.org/python-package-guide/package-structure-code/python-package-build-tools.html#build-back-ends)
+
+Important: In some instances, specific build back-ends will need metadata provided in a slightly different way in your `pyproject.toml` file. Be sure to read documentation for the build tool that you are using if you don't use Hatchling which is used in this tutorial.
+
+The documentation for the hatchling back-end is [here](https://hatch.pypa.io/latest/config/metadata/)
+:::
+
+## Add metadata to your pyproject.toml `[project]` table
+
+So far you have a project name and a version in the `[project]` table.
+
+```toml
+[project]
+name = "pyospackage"
+version = "1.1"
+
+```
+
+Let's add the following to your table:
+
+- A **description** of your package. This should be a single line and should briefly describe the goal of your package using non technical terms if as all possible!
+- package **authors**
+- package **maintainers**
+
+:::{admonition} Authors vs. maintainers
+:class: tip
+
+When adding maintainers and authors, you may want to think about the difference between the two.
+
+Authors generally include people who:
+* originally created / designed developed the package and
+* people who add new functionality to the package.
+
+Whereas maintainers are the people that are currently, actively working on the project. It is often the case that there is overlap in authors and maintainers. As such these lists may be similar or the same.
+
+A good example of when the lists might diverge is sometimes you have a package where an initial author developed it and then stepped down as a maintainer to move on to other things. This person may continue to be considered an author but no longer actively maintains the package.
+
+It is important to note there that there are many ways define author vs maintainer and we don't prescribe a single approach in this tutorial.
+
+However, we encourage you to consider carefully, for PyPI publication, who
+you want to have listed as authors and maintainers on your PyPI landing page.
+:::
+
+When you add authors and maintainers you need to use a format that will look like a Python list with a dictionary within it type of format:
+
+`authors = [{ name = "Firstname lastname", email = "email@pyopensci.org" }]`
+
+If you have two authors you can add them like this:
+
+`authors = [{ name = "Firstname lastname", email = "email@pyopensci.org" }, { name = "Firstname lastname", email = "email@pyopensci.org" }]`
+
+:::{admonition} Author names & emails
+:class: note
+
+There is a quirk with PyPI for authors that have names but not emails in the pyproject.toml. If you don't have emails for everyone, we suggest that you only add names.
+
+:::
+
+Your `pyproject.toml` file now should look like the example below. It is OK if you only have 1 author and the same author is also maintainer of your package:
+
+```toml
+[project]
+name = "pyospackage"
+version = "1.1"
+description = "Tools that update the pyOpenSci contributor and review metadata that is posted on our website"
+authors = [{ name = "Firstname lastname", email = "email@pyopensci.org" }]
+maintainers = [{ name = "Firstname lastname", email = "email@pyopensci.org" }, { name = "Firstname lastname", email = "email@pyopensci.org" }]
+```
+
+### Add classifiers to your metadata
+
+Next you will add classifiers to your `pyproject.toml` file. The value for each classifier that you add to your `pyproject.toml` file must come from the list of [PyPI accepted classifier values found here](https://PyPI.org/classifiers/). Any deviations in spelling and format will cause issues when you publish to PyPI.
+
+:::{admonition} What happens when you use incorrect classifiers?
+:class: tip
+
+If you do not [use standard classifier values](https://PyPI.org/classifiers/), when you try to publish your package on PyPI it will be rejected. π Don't worry if PyPI rejects you on your first try! It has happened to all of us.
+:::
+
+Review that list and add items below to your `pyproject.toml` file:
+
+- development status
+- intended audiences,
+- topic
+- license and
+- programming language support
+
+The classifier key should look something like the example below. A few notes:
+
+- Your classifier values might be different depending upon the license you have selected for your package, your intended audience, development status of your package and the Python versions that you support
+- You can add as many classifiers as you wish as long as you use the [designated PyPI classifier values](https://PyPI.org/classifiers/).
+
+```toml
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Build Tools",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+```
+
+Note that while classifiers are not required in your `pyproject.toml` file, they will help users find your package. As such we strongly recommend that you add them.
+
+### Add package dependencies
+
+Next add your dependencies table to the project table.
+Dependencies represent the Python packages that your package requires to run. Similar to the build-system above, dependencies
+get added in an array (similar to a Python list) structure.
+
+```toml
+dependencies = ["numpy", "requests", "pandas", "pydantic"]
+
+```
+
+:::{admonition} Pin dependencies with caution
+Pinnning dependencies references to specifying a specific version of a dependency like this `numpy == 1.0`. In some specific cases, you may chose to pin or specify a lower or upper bound of a specific package. You can do that using syntax like this:
+
+`ruamel-yaml>=0.17.21`
+
+Note that unless you are building an application, you want to be cautious about pinning dependencies. This is because
+users will be installing your package into various environments. A pinned dependency can make resolving an environment more challenging. As such only pin dependencies to a specific version if you absolutely need to do so.
+
+One build tool that you should be aware of that pins dependencies by default is Poetry. [Read more about how to safely add dependencies with Poetry, here.](../package-structure-code/python-package-build-tools.html#challenges-with-poetry)
+:::
+
+### Requires-python
+
+Finally, add the `requires-python` field to your `pyproject.toml` `[project]` table. The `requires-python` field, helps pip understand the lowest version of Python that you package supports when it's installed. It is thus a single value.
+
+`requires-python = ">=3.10"`
+
+### Your current pyproject.toml file
+
+Once you have dependencies declared in the `pyproject.toml` file, your build
+tool will know to install them when your package is installed using pip.
+
+The project table of your `pyproject.toml` file should now look something like the example below.
+
+```toml
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "pyospackage"
+version = "1.1"
+description = "Tools that update the pyOpenSci contributor and review metadata that is posted on our website"
+authors = [{ name = "Firstname lastname", email = "email@pyopensci.org" }]
+maintainers = [{ name = "Firstname lastname", email = "email@pyopensci.org" }, { name = "Firstname lastname", email = "email@pyopensci.org" }]
+
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Build Tools",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3 :: Only",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+
+dependencies = ["ruamel-yaml>=0.17.21", "requests", "python-dotenv", "pydantic"]
+
+```
+
+### Add a license
+
+In the previous lessons, you added both a [README.md](4-add-license-file) file and a [LICENSE](4-add-license-file) to your package repository.
+Once you have those files, you can add them to your pyproject.toml file as
+links following the example below.
+
+
+```
+requires-python = ">=3.10"
+readme = "README.md"
+license = {file = 'LICENSE'}
+```
+
+and a `requires-Python = ">=3.10"` that is important to have for pip.
+
+## Add the `[project.urls]` table
+
+Finally, add the project.urls table to your
+pyproject.toml file.
+
+`project.urls` contains links that are relevant for your project. You might want to include:
+
+- **Homepage:** A link to your published documentation for your project. If you are working through this tutorial, then you may not have this link yet. That's ok, you can skip it for the time being.
+- **Bug reports:** a link to your issues / discussions or wherever you want users to report bugs.
+- **Source:** the GitHub / GitLab link for your project.
+
+```
+[project.urls] # Optional
+"Homepage" = "https://www.pyopensci.org"
+"Bug Reports" = "https://github.com/pyopensci/pyosmeta/issues"
+"Source" = "https://github.com/pyopensci/pyosmeta/"
+```
+
+## Putting it all together - your completed pyproject.toml file
+
+Below is an example of a complete `pyproject.toml` file that
+is commented with all of the sections we discussed above.
+
+```toml
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[project]
+name = "pyosmeta"
+dynamic = ["version"]
+description = "Tools that update the pyOpenSci contributor and review metadata that is posted on our website"
+authors = [{ name = "Firstname lastname", email = "email@pyopensci.org" }]
+
+maintainers = [
+ { name = "firstname lastname", email = "admin@pyopensci.org" },
+]
+
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Build Tools",
+ "License :: OSI Approved :: MIT License",
+ "Programming Language :: Python :: 3 :: Only", # BE sure to specify that you use python 3.x
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+]
+
+dependencies = ["ruamel-yaml>=0.17.21", "requests", "python-dotenv", "pydantic"]
+
+requires-python = ">=3.10"
+readme = "README.md"
+license = { text = "MIT" }
+
+[project.urls] # Optional
+"Homepage" = "https://www.pyopensci.org"
+"Bug Reports" = "https://github.com/pyopensci/pyosmeta/issues"
+#"Funding" = ""
+"Source" = "https://github.com/pyopensci/pyosmeta/issues"
+```
+
+
+## Appendix - A fully commented pyproject.toml file
+
+Below is a fully commented pyproject.toml file if you want to use it for reference.
+
+```toml
+# You can delete all of the comments once you have created your own pyproject.toml file.
+
+# The build system table. Here we use pdm.backend as the build back end tool.
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+# The [project] section contains your package's metadata
+# notice that the version is setup to be dynamically generated using dynamic=[βversionβ]
+
+[project]
+name = "pyospackage"
+# dynamic = ["version"] # you will learn how to dynamically set the version in lesson XX
+version = "1.2" # manually assign version (not preferred)
+description = "Tools that update the pyOpenSci contributor and review metadata that is posted on our website"
+authors = [{ name = "Firstname lastname", email = "email@pyopensci.org" }]
+
+# maintainers section is optional but suggested.
+maintainers = [
+ { name = "firstname lastname", email = "admin@pyopensci.org" }, # Optional
+]
+
+# Classifiers have set values - be sure to only use classifier values from the
+# PyPI page here: https://PyPI.org/classifiers/
+
+classifiers = [
+ # How mature is this project? Common values are
+ # 3 - Alpha
+ # 4 - Beta
+ # 5 - Production/Stable
+ "Development Status :: 4 - Beta",
+
+ # Indicate who your project is intended for
+ "Intended Audience :: Developers",
+ "Topic :: Software Development :: Build Tools",
+
+ # Pick your license (using syntax from the classifier page). We suggest MIT, BSD3 or Apache if you are corporate
+ "License :: OSI Approved :: MIT License",
+
+ # Specify the Python versions ensuring that you indicate you support Python 3.
+ # this is only for PyPI and other metadata associated with your package - for your users to see
+ "Programming Language :: Python :: 3 :: Only", # BE sure to specify that you use python 3.x
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+]
+
+# Optional, but suggested. You can decide whether you wish to pin or not pin dependencies. for most projects you want to avoid pinning dependencies
+dependencies = ["ruamel-yaml>=0.17.21", "requests", "python-dotenv", "pydantic"]
+# This is the metadata that pip reads to understand what versions your package supports
+requires-python = ">=3.10"
+readme = "README.md"
+license = { FILE = LICENSE }
+
+# Examples listed include a pattern for specifying where the package tracks
+# issues, where the source is hosted, where to say thanks to the package
+# maintainers, and where to support the project financially. The key is
+# what's used to render the link text on PyPI.
+[project.urls] # Optional
+"Homepage" = "https://www.pyopensci.org"
+"Bug Reports" = "https://github.com/pyopensci/pyosmeta/issues"
+#"Funding" = ""
+"Source" = "https://github.com/pyopensci/pyosmeta/issues"
+
+# TODO: look into what hatch needs to recognize src/
+# TODO: also look into how hatch vcs interacts
+# TODO: look into how you define files that go into sdist with hatchling
+# This config is important for telling pdm?? that the package should live in
+# a src directory
+#package-dir = "src"
+
+
+# Note that you need to create the tag after all commits are created for vcs based versioning to work
+[tool.hatch]
+version.source = "vcs"
+build.hooks.vcs.version-file = "src//version.py"
+```
+
+
+## Example `pyproject.toml` files
+
+Below are some examples of `pyproject.toml` files from various packages in the scientific and pyOpenSci ecosystem.
+* [PyPAs fully documented pyProject.toml file](https://github.com/pypa/sampleproject/blob/main/pyproject.toml)
+* [taxpasta has a nicely organized pyProject.toml file and is a pyOpenSci approved package](https://github.com/taxprofiler/taxpasta/blob/f9f6eea2ae7dd08bb60a53dd49ad77e4cf143573/pyproject.toml)
+
+
+## What's next??
+
+You are now ready to publish your Python package to PyPI and conda-forge.
+
+* In the next lesson, you will learn [how to publish to PyPI](6-publish-pypi).
+* Following that you'll learn how to create the recipe needed for conda-forge to build your package from PyPI and [publish it on conda-forge](7-publish-conda-forge).
+
+
diff --git a/tutorials/6-publish-pypi.md b/tutorials/6-publish-pypi.md
new file mode 100644
index 000000000..c5088a337
--- /dev/null
+++ b/tutorials/6-publish-pypi.md
@@ -0,0 +1,507 @@
+# Publish your Python package to PyPI
+
+
+
+
+
+In the previous lessons, you've learned:
+
+1. How to make your code pip installable.
+2. How to add a `README`, `LICENSE` & `CODE of CONDUCT` file file to your package
+3. How to setup your `pyproject.toml` file to support publishing on PyPI.
+
+:::{admonition} Learning Objectives
+:class: tip
+
+In this lesson you will learn how to:
+
+- How to build your package's source (sdist) and wheel distributions
+- Setup an account on testPyPI (the process is similar for the real PyPI)
+- Publish your package to testPyPI using [twine](https://twine.readthedocs.io/en/stable/)
+
+Once your package is on PyPI you can then easily publish it to conda-forge
+using the [grayskull](https://conda.github.io/grayskull/) tool. You do not need to build the package specifically
+for conda, conda-forge will build from your PyPI source distribution file (sdist). You will learn how to publish to conda-forge in the [next lesson](7-publish-conda-forge.md).
+
+In a followup lesson, you will learn how to automate publishing to PyPI using GitHub actions.
+:::
+
+:::{figure-md} build-workflow-tutorial
+
+
+You need to build your Python package in order to publish it to PyPI (or Conda). The build process organizes your code and metadata into a distribution format that can be uploaded to PyPI and subsequently downloaded and installed by users. NOTE: you need to publish a sdist to PyPI in order for conda-forge to properly build your package automatically.
+:::
+
+
+
+## Test PyPI.org vs PyPI
+
+There are two "warehouses" that you can use to publish your package.
+
+1. **Test PyPI (https://test.pypi.org):** Test PyPI is a version of the PyPI repository that can be used for testing. This is a great place to practice and learn how to publish a package without taking up space on the real PyPI servers.
+2. **Real PyPI (https://pypi.org):** This is the PyPI "warehouse" where you can officially publish your Python package. IMPORTANT: only publish your package to PyPI when you are ready for it to be used by others and/or confident that it will become a package that you maintain. PyPI is not a place to practice learning how to publish a Python package.
+
+The steps for publishing on test PyPI vs. real PyPI are the same with the
+exception of a different url. Thus, in this lesson you will use testPyPI
+to practice and learn.
+
+
+
+
+## 3 Steps for publishing a Python package on PyPI
+
+There are 3 things that you need to do to publish your Python package
+to PyPI. You need to:
+
+1. [**Build your package**](../package-structure-code/python-package-distribution-files-sdist-wheel). Building a package is the process of turning your code into 2 distribution files: an sdist and a wheel. The wheel distribution file is particularly important for users who will `pip install` your package.
+2. **Create an account on (test) PyPI**: You will need to create a PyPI account and associated token which provides permissions for you to upload your package.
+3. **Publish to PyPI using `twine`**: Once you have completed the above two steps, you are ready to use `twine` to publish your package!
+
+The first time you publish your package to PyPI you will do it manually.
+After that you can opt to create a automated workflow that publishes an updated
+version of your package to PyPI every time you create a release on GitHub.
+
+:::{admonition} Learn more about building Python packages in our guide
+:class: tip
+- [Learn more about building here](../package-structure-code/python-package-distribution-files-sdist-wheel)
+- [Learn more about the wheel](#python-wheel)
+- [Learn more about the sdist (source distribution)](#python-source-distribution)
+
+:::
+
+## Step 1: Build your package's sdist and wheel distributions
+
+To begin you will need to create your package's sdist and wheel distribution
+files. This process is known as building your package.
+
+1. Activate your development environment if it is not already active.
+2. Install your package requirements if you haven't already. based on the `pyproject.toml` (do i need this if they just install twine manually below? )
+
+### **If you are using conda for your environment manager:**
+
+```bash
+> conda activate pyospackage-dev
+> conda list
+> pip install -e .[dev]
+```
+
+### If you are using venv:
+
+::::{tab-set}
+
+:::{tab-item} Linux/Mac
+```bash
+> source pyospackage-dev /bin/activate
+> pip list
+> pip install -e .[dev]
+```
+
+:::
+
+:::{tab-item} Windows
+
+```bash
+> pyospackage-dev\Scripts\activate
+> pip list
+> pip install -e .[dev]
+```
+:::
+
+::::
+
+
+
+
+
+3. You are now ready to build your package! Note that here you are using the [PyPA build tool](https://github.com/pypa/build) as a "Front end" tool that builds
+your package's sdist and wheel using the hatchling build back end. Remember that you defined your build backend here in the build system table of your `pyproject.toml` file. So build knows to use [hatchling](https://hatch.pypa.io/latest/).
+
+To build your package run:
+
+`python -m build`
+
+```bash
+β― python -m build
+
+* Creating virtualenv isolated environment...
+* Installing packages in isolated environment... (hatchling)
+* Getting build dependencies for sdist...
+* Building sdist...
+* Building wheel from sdist
+* Creating virtualenv isolated environment...
+* Installing packages in isolated environment... (hatchling)
+* Getting build dependencies for wheel...
+* Building wheel...
+Successfully built pyospackage-0.1.0.tar.gz and pyospackage-0.1.0-py3-none-any.whl
+```
+When you build your package, it will create two output "files":
+
+1. [.whl or wheel file](/package-structure-code/python-package-distribution-files-sdist-wheel.html#wheel-whl-files) and
+2. a [sdist (source distribution)](../package-structure-code/python-package-distribution-files-sdist-wheel.html#source-distribution-sdist) stored in a `.tar.gz` compressed format.
+
+
+```
+dist/pyospackage-0.1.0-py3-none-any.whl
+dist/pyospackage-0.1.0.tar.gz
+```
+:::{admonition} Learn more about the wheel and sdist formats
+:class: tip
+You can learn more about both of these distribution files and package
+building in general in the [build page of our packaging guide](../package-structure-code/python-package-distribution-files-sdist-wheel).
+:::
+
+The above two files are what you will need to publish your package to
+PyPI. The tar.gz file is particularly important if you wish to [publish
+your package to conda-forge](7-publish-conda-forge). You'll learn more about that in this
+lesson.
+
+### Congratulations - you've created your package distribution files
+
+You've now created your package distribution. You're officially on
+your way to publishing your package on PyPI.
+
+## Step 2. Setup your testPyPI account
+
+Next, you'll setup an account on `testPyPI`. Remember that you
+are using testPyPI here instead of the real PyPI as a way to
+safely learn how to publish a package without stressing the
+real PyPI's servers up with lots of "test" packages.
+
+:::{admonition} Test vs. real PyPI
+If you have a package that you are confident belongs on the real PyPI. All of the steps below will also work for you with slight modifications which will be noted below.
+:::
+
+1. [Open up a web browser and go to the test PyPI website](https://test.pypi.org/).
+2. [Create an account](https://test.pypi.org/account/register/) if you don't already have one. Be sure to store your password in a safe place!
+3. Once you have an account setup, login to it.
+4. Search on [https://test.pypi.org/](https://test.pypi.org/) (or pypi.org) to ensure that the package name that you have selected doesn't already exist. If you are using our test pyosPackage, then we suggest that you add your name or GitHub username to the end of the package name to ensure it's unique.
+
+Example: `pyosPackage_yourNameHere`.
+
+:::{figure-md} build-workflow-tutorial
+
+
+Before you try to upload to (test) PyPI, check to see if the name of your package is already taken. You can do that using
+the search box at the top of the (test) PyPI website.
+:::
+
+
+:::{admonition} Setup 2-factor (2FA) authentication
+
+2-factor authentication is a secure login process that allows you to
+use a backup device that only you can access to validate that the person logging in is really you. It addresses the issue of password phishing where someone else gains access to a password and can login to your account.
+
+This matters on PyPI because someone could login to your account and upload a version of your package that has security issues. These issues will then impact all of your users when they download and install that version of the package.
+
+While you don't have to setup 2-factor authentication, we strongly
+suggest that you do so.
+:::
+
+## Step 3. Create a package upload token
+
+To upload your package to PyPI, you will need to create a token. Ideally
+this token is specific to the package that you are publishing.
+
+However, if your package isnβt already on PyPI, then you will need to create a token for your account first and then create a package-specific token.
+
+:::{admonition} Why create package-specific tokens?
+
+It's ideal to create a package-specific token. When you create an account wide token this allows anyone with access to then access all of your PyPI projects. By creating a package specific token, you are limiting the scope of the token to only your specific package. This is just a safe way to set things up for you particularly if you are collaborating with others on package development.
+:::
+
+
+## Create a secure PyPI token
+
+Follow the steps below to create your token.
+
+* First login and go to your account settings in testPyPI
+* Scroll down to the **API tokens** section
+* Click on the **Add API Token** button
+* If you are new to using PyPI and don't have any packages there yet, OR if you have other packages on PyPI but are uploading a new package, you will need to create an account-wide token.
+
+
+:::{admonition} Tool options for publishing to PyPI
+
+In this lesson you use twine to upload to PyPI. Note that other tools such as Flit, Hatch and PDM use slightly different approaches to storing and accessing your token to support uploading your package to PyPI. All of those tools are great ways to publish your package and provide a more end-to-end packaging solution.
+:::
+
+
+### Using a PyPI token
+
+To use your API token, you do the following:
+
+* Set your username to `__token__`
+* Set your password to the token value, including the pypi- prefix
+
+### Upload to PyPI using twine
+
+It can be challenging to always have to type in your username
+and token value for PyPI. Twin allows you to store both your
+username and token value in a `.pypirc` file stored in your
+computer's `$HOME` directory. This file is then used every
+time you upload to PyPI so you don't have to find and enter
+the long token each time.
+
+```toml
+[pypi]
+username = __token__
+password =
+```
+
+:::{admonition}
+:class: note
+NOTE: the `.pypirc` file stores your token in plain text format. Thus, in another lesson you will learn how to store your PyPI authentication information in a GitHub secret / or trusted publisher ...
+:::
+
+
+:::{admonition} using a .pypirc file if your always publishing manually
+### Create your `.pypirc` file with authentication information
+
+To create your `.pypirc` file, do the following.
+
+1. open up bash / your terminal
+2. Create a new file using touch
+3. Open the file in your favorite text editor. Below you use vscode but any text editor will work.
+
+The commands for the above steps are below:
+
+```bash
+$ touch ~/.pypirc
+$ code .pypirc # open with vscode
+```
+Once you open up the file in vscode or whatever text editor you prefer, you can add a toml table containing your testPyPI username and token information stored in a password key.
+
+```toml
+[testpypi]
+ username = __token__
+ password = pypi-rest-of-your-token-here
+```
+Save the file and then check that it looks correct in bash using the `cat` command:
+
+```bash
+# view the file printed in your terminal
+$ cat ~/.pypirc
+```
+
+:::
+
+Now, install twine:
+
+::::{tab-set}
+
+:::{tab-item} Linux/Mac
+
+```bash
+python3 -m pip install --upgrade twine
+```
+:::
+
+:::{tab-item} Windows
+```bash
+py -m pip install --upgrade twine
+```
+:::
+::::
+
+### Upload your package
+
+Once you have stored your PyPI authentication information you
+can upload your package to PyPI.
+
+::::{tab-set}
+
+:::{tab-item} Linux/Mac
+
+```bash
+
+β― python3 -m twine upload --repository testpypi dist/*
+
+Uploading distributions to https://test.pypi.org/legacy/
+Uploading pyospackage-0.1.0-py3-none-any.whl
+100% βββββββββββββββββββββββββ 8.8/8.8 kB β’ 00:00 β’ 6.6 MB/s
+Uploading pyospackage-0.1.0.tar.gz
+100% βββββββββββββββββββββββββ 8.6/8.6 kB β’ 00:00 β’ 3.1 MB/s
+
+View at:
+https://test.pypi.org/project/pyosPackage/0.1.0/
+```
+
+:::
+
+:::{tab-item} Windows
+
+```bash
+
+β― py -m twine upload --repository testpypi dist/*
+
+Uploading distributions to https://test.pypi.org/legacy/
+Uploading pyospackage-0.1.0-py3-none-any.whl
+100% βββββββββββββββββββββββββ 8.8/8.8 kB β’ 00:00 β’ 6.6 MB/s
+Uploading pyospackage-0.1.0.tar.gz
+100% βββββββββββββββββββββββββ 8.6/8.6 kB β’ 00:00 β’ 3.1 MB/s
+
+View at:
+https://test.pypi.org/project/pyosPackage/0.1.0/
+```
+:::
+
+::::
+
+
+
+
+:::{admonition} Two factor authentication and PyPI
+
+If you have 2FA setup and try to upload to PyPI using a username and
+password, you will get an error similar to the one below. To
+upload to PyPI you will need to setup a token authentication.
+You will learn how to do that below.
+
+```bash
+β python3 -m twine upload --repository testpypi dist/*
+Uploading distributions to https://test.pypi.org/legacy/
+Enter your username: your-pypi-username-here
+Enter your password: your-password-here
+Uploading pyospackage-0.1.0-py3-none-any.whl
+100% ββββββββββββββββββββββββββββββββββββββββ 8.8/8.8 kB β’ 00:00 β’ 4.0 MB/s
+WARNING Error during upload. Retry with the --verbose option for more details.
+ERROR HTTPError: 401 Unauthorized from https://test.pypi.org/legacy/
+ User lwasser has two factor auth enabled, an API Token or Trusted Publisher must
+ be used to upload in place of password.
+```
+
+:::
+
+
+## Install your package from TestPyPI
+
+Once your package is uploaded to test PyPI, you can install it from testPYPI.
+You can find the installation instructions on the testPyPI landing page for your newly uploaded package.
+
+:::{figure-md} testpypi-landing-page
+
+
+This is an example landing page for the pyosPackage that was just uploaded. Notice at the top of the page there is instructions for how to install the package from test PyPI. You can simply copy that code and use it to install your package from testPyPi locally.
+:::
+
+
+[Check out our pyOpenSci test package landing page here](https://test.pypi.org/project/pyosPackage/) for an example. NOtice at the top there is the package name and version. Below are installation instructions:
+
+`pip install -i https://test.pypi.org/simple/ pyosPackage`
+
+### Time to install your package
+
+* On your computer, activate the development environment that
+you wish to install your newly published package in.
+* Run the installation instructions for your package from test PyPI.
+
+::::{tab-set}
+
+:::{tab-item} Conda
+
+```bash
+> conda activate pyospkg-dev
+> pip install -i https://test.pypi.org/simple/ youPackageNameHere
+> conda list
+```
+:::
+
+:::{tab-item} venv mac / Linux
+```bash
+> source pyospackage-dev /bin/activate
+> pip install -i https://test.pypi.org/simple/ youPackageNameHere
+> pip list
+```
+:::
+
+:::{tab-item} venv windows
+```bash
+> source pyospackage-dev /bin/activate
+> pip install -i https://test.pypi.org/simple/ youPackageNameHere
+> pip list
+```
+:::
+::::
+
+
+:::{admonition} The value of end-to-end tools like hatch, flit and poetry
+In this lesson we are using core tools including:
+
+* hatchling
+* PyPA's build
+* twine
+
+to build and publish your package to PyPI.
+
+End-to-end packaging tools such as Hatch, PDM, Poetry and
+Flit can manage all of the above steps but have to be
+configured.
+
+For example, while twine users a `.pypirc` file, Hatch will cache your PyPI token information to make publishing to PyPI from your computer easier. Be sure the read the documentation for any end-to-end publication tool that you chose to use.
+:::
+
+
+
+
+
+
+## Package-specific token vs trusted publisher
+
+For long run maintenance of your package, you have two options
+related to PyPI publication.
+
+1. You can create a package-specific token which you will use to publish your package (manually) to PyPI. This is a great option if you don't wish to automate your PyPI publication workflow.
+2. You can also create an automated publication workflow on GitHub using GitHub actions. This is a great way to make the publication process easier and it also supports a growing maintainer team. In this case we suggest you don't worry about the token and instead setup a specific GitHub action that publishes your package when you make a release. You can then create a "trusted publisher" workflow on PyPI.
+
+You will learn how to create the automated trusted publisher workflow in a followup lesson.
+
+
+### OPTIONAL: If you want to use a manual token-based publication workflow
+
+If you plan to use your token regularly to publish to PyPI, we strongly recommend going through the above steps again to create
+a token specific to your new package.
+
+To do this:
+1. Go to testPyPI
+1. Navigate to the "Your Projects" section of your account
+2. Click on the manage button for the project that ou wish to add a token for
+3. Go to settings
+4. Click on "Create a token for your-package-name-here"
+5. Create the token and follow the steps above to open up the `.pypirc` file
+6. Replace the old account wide token with your new package token.
+
+And you're all done!
+
+## You have published your package to (test) PyPI!
+
+Congratulations. You have now successfully published your package to testPyPI. If you have a package that is ready for real-world use on the real pyPi, then you can follow the same steps to publish it on PyPI.org .
+
+Once you publish on PyPI.org, you can then easily add your package to the conda-forge ecosystem using the [grayskull](https://conda-forge.org/blog/posts/2020-03-05-grayskull/) tool.
+
+`__init__.py`
+
+You will learn how to do that in the next lesson.
diff --git a/tutorials/7-publish-conda-forge.md b/tutorials/7-publish-conda-forge.md
new file mode 100644
index 000000000..4604f8ef5
--- /dev/null
+++ b/tutorials/7-publish-conda-forge.md
@@ -0,0 +1,49 @@
+# Publish to conda-forge
+
+In the previous lessons, you've learned the following:
+
+1. How to structure your code into a package like format that can be installed into a Python environment.
+2. How to add a `README` and `LICENSE` file to your package
+3. How to setup your `pyproject.toml` file with all of the metadata that PyPI requires and also metadata that will be helpful for users to find your package.
+
+If you have gone through all of the above lessons, you are now ready to
+build your package's distribution files which are needed for you to publish
+to PyPI. Here, you will learn how to set things up on
+PyPI and how to manually publish to (test) PyPI using twine.
+
+:::{figure-md} build-workflow-tutorial
+
+
+You need to build your Python package in order to publish it to PyPI (or Conda). The build process organizes your code and metadata into a distribution format that can be uploaded to PyPI and subsequently downloaded and installed by users. NOTE: you need to publish a sdist to PyPI in order for conda-forge to properly build your package automatically.
+:::
+
+
+
+
+You can follow the same steps to setup your package on the real PyPI. However
+in this lesson we will use testPyPI as a practice run.
+
+in xx lesson, you will learn how to setup an automated release workflow on GitHub
+using GitHub actions that will automate the PyPI publication process whenever
+you create a new software release.
+
+:::{admonition} Learning Objectives
+:class: tip
+
+In this lesson you will learn how to:
+
+- how to build your package's sdist and wheel distributions
+- Setup an account on testPyPI (the process is similar for the real PyPI)
+- Publish your package to PyPI
+
+Once your package is on PyPI you can then easily publish it to conda-forge
+using the [grayskull](https://conda.github.io/grayskull/) tool. You do not need to build the package specifically
+for conda, conda-forge will build from your PyPI source distribution file (sdist).
+
+:::
diff --git a/tutorials/extras/0-create-repo.md b/tutorials/extras/0-create-repo.md
new file mode 100644
index 000000000..79bdcd94a
--- /dev/null
+++ b/tutorials/extras/0-create-repo.md
@@ -0,0 +1,11 @@
+# A lesson around creating a fresh repo
+
+select readme, license and .gitignore file to add to your repo.
+
+Create
+then clone it
+
+the license page / lesson can then be potentially skipped and we can link to
+
+1. either create the license a thte beginning
+2. options we see int he ecosystem
diff --git a/tutorials/extras/1-create-environment.md b/tutorials/extras/1-create-environment.md
new file mode 100644
index 000000000..f693c8ba2
--- /dev/null
+++ b/tutorials/extras/1-create-environment.md
@@ -0,0 +1,125 @@
+# Create a Python development environment
+
+To get started working on your package you will want to create a
+shiny new Python environment. While this is not required, we strongly
+encourage you to create an environment that is dedicated to your Python
+package.
+
+Below you learn how to create an environment using both:
+
+- venv - which is the environment manager that comes with Python
+- conda - an environment manager that is tailored to the scientific ecosystem.
+
+There is no right or wrong environment option. Chose the option that you prefer.
+
+:::{tip}
+If you are a scientist and you work with spatial data or are creating a package that is not pure python, you may prefer conda. But in general pick
+the environment tool that works best for you.
+:::
+
+## Create a package development environment Using venv
+
+venv comes with any Python installation. Thus, you may find it easy to
+use it for your package development needs. Instructions on how to create a
+new development environment using venv are below.
+
+1. **Open Terminal or Command Prompt:**
+
+ - Open your terminal or command prompt.
+
+2. **Navigate to the Project Directory:**
+
+ ```bash
+ cd path/to/your/project-directory
+ ```
+
+3. **Create a Virtual Environment:**
+
+ ```bash
+ python -m venv pyos-dev
+ ```
+
+4. **Activate the Virtual Environment:**
+
+ - **On Windows:**
+
+ ```bash
+ pyos-dev\Scripts\activate
+ ```
+
+ - **On macOS/Linux:**
+
+ ```bash
+ source pyos-dev/bin/activate
+ ```
+
+5. **Install/Manage Dependencies:**
+
+ ```bash
+ pip install package_name
+ ```
+
+6. **Work on Your Project:**
+
+7. **Deactivate the Virtual Environment:**
+ ```bash
+ deactivate
+ ```
+
+### Additional Notes:
+
+- Remember to reactivate the environment each time you want to work on your project.
+- To delete the virtual environment, delete the `pyos-dev` folder.
+
+## Creating a PyOS-Dev Environment Using Conda
+
+Some scientists prefer to use a conda environment for their package development. If that is your preference, follow the steps below.
+
+1. **Create an Environment File (`env.yml`):**
+
+ - Use a text editor to create a file named `env.yml` and specify the required packages in the YAML format. For instance:
+
+ ```yaml
+ name: pyos-dev
+ channels:
+ - defaults
+ dependencies:
+ - python=3.8
+ - package_name1
+ - package_name2
+ # Add other necessary packages
+ ```
+
+2. **Create the Conda Environment from the Environment File:**
+
+ - Open your terminal or command prompt.
+
+ ```bash
+ conda env create -f env.yml
+ ```
+
+ This command will read the `env.yml` file and create a Conda environment named `pyos-dev` with the specified packages.
+
+3. **Activate the Conda Environment:**
+
+ - Once the environment is created, activate it.
+ - **On Windows:**
+
+ ```bash
+ conda activate pyos-dev
+ ```
+
+ - **On macOS/Linux:**
+
+ ```bash
+ source activate pyos-dev
+ ```
+
+4. **Work on Your Project:**
+ - You're now working in the `pyos-dev` Conda environment.
+
+### Additional Notes:
+
+- Remember to activate the environment each time you want to work on your project.
+- To deactivate the environment, use `conda deactivate`.
+- To delete the environment, you can use `conda env remove -n pyos-dev`.
diff --git a/tutorials/extras/2-connect-repo-to-zenodo.md b/tutorials/extras/2-connect-repo-to-zenodo.md
new file mode 100644
index 000000000..926476dd8
--- /dev/null
+++ b/tutorials/extras/2-connect-repo-to-zenodo.md
@@ -0,0 +1 @@
+# Create zenodo doi for your Python Package
diff --git a/tutorials/extras/6-setuppy-to-pyproject-toml.md b/tutorials/extras/6-setuppy-to-pyproject-toml.md
new file mode 100644
index 000000000..56aab239d
--- /dev/null
+++ b/tutorials/extras/6-setuppy-to-pyproject-toml.md
@@ -0,0 +1,220 @@
+# How to migrate from setup.py to pyproject.toml
+
+Authors: Leah Wasser, Filipe Fernandes
+
+If you have an existing Python package that has a `setup.py` or a `setup.cfg` file storing your project's metadata, then this tutorial is for you!
+
+Here you will learn how to migrate your project's metadata and build information over to a `pyproject.toml` file.
+
+## Setup.py files serve a purpose
+
+Sometimes you may need a setup.py file if you have a complex build
+that is compiling extensions in other languages or wrapping other
+languages. Even if you require a setup.py file, to build your package,
+you should still have a pyproject.toml file to store project metadata.
+
+Below we discuss migrating your information from a setup.py or setup.cfg file to a pyproject.toml file.
+
+## If your metadata are in a setup.cfg
+
+If your metadata are already in a setup.cfg file, then moving your metadata to a pyproject.toml file, should be a straightforward process.
+
+- Here you can see an example of [example setup.cfg file](https://github.com/stravalib/stravalib/blob/65f903248b6562febf4109d2699b38a1744e50fb/setup.cfg) from the stravalib project.
+- And here you can see the what the [migration looked like to pyproject.toml](https://github.com/lwasser/stravalib/blob/ac9c683751969457730743ea435dcf827e015b7d/pyproject.toml)
+
+The biggest difference that you will see between the setup.cfg and pyproject.toml is that instead of using `[metadata]` as a defined section to store your project metadata, you use `[project]` in the pyproject.toml. There are some other small differences in how the pyproject.toml file is structured; however if you include all of the required elements discussed below, you will be in good shape.
+
+Below is an example from a setup.cfg file from the stravalib package.
+
+```toml
+[metadata]
+name = stravalib
+fullname = stravalib
+description = A Python package that makes it easy to access and download data from the Strava V3 REST API.
+```
+
+We migrated the package over to pyproject.toml file, and the same metadata looked like this:
+
+```toml
+[project]
+name = "stravalib"
+description = "A Python package that makes it easy to access and download data from the Strava V3 REST API."
+```
+
+## If metadata are in a setup.py
+
+The process for moving your metadata from a setup.py file is similar to the setup.cfg. You can completely remove the setup() method if it only contains metadata information and all of the package metadata then gets migrated to your pyproject.toml file.
+
+```python
+from setuptools import setup # Package metadata
+
+name = "mypackage"
+version = "1.0.0"
+author = "Your Name"
+author_email = "your@email.com"
+description = "A brief description of your package"
+```
+
+Then you call a **setup()** method. This method will
+ensure that the metadata is placed into the proper
+METADATA file when your package's wheel is created.
+
+```python
+setup(name=name,
+ version=version,
+ author=author,
+ author_email=author_email)
+```
+
+In this case, with a setup.py file, you can remove all of the content within the `setup()` method that declares the projectβs metadata. And then you can migrate the metadata items one by one over to your pyproject.toml file.
+
+## Pyproject.toml vs Setup.py, setup.cfg
+
+There are numerous advantages to using pyproject.toml over the setup.cfg of the setup.py files.
+
+pyproject.toml:
+
+- Allows you to declare what build system you are using. This makes it easier for a new contributor to quickly understand how your package is built.
+- Allows you to specify metadata for all pypi classifiers and also core package metadata in one place using consistent formatting
+- Pyproject.toml file allows you to also add configuration for other tools such as black and flake8 making it a goto spot for anyone to understand the setup of your packages structure
+
+## Migrating from setup.py to pyproject.toml
+
+Here we will use the GEMGIS package as an example of a pure python project that
+migrated from a setup.py file to a **pyproject.toml** file.
+
+Below is the setup.py file. Note that this specific setup.py file is comprised only
+of package metadata. Thus, this package is a perfect candidate for migrating over to
+a pyproject.toml file and removing the setup.py file altogether from the project build.
+
+Note that in the package structure below,
+
+- setuptools is being called.
+ this is your build back end that will create your packages distributions (sdist and wheel)
+- Version is being updated / maintained manually. this isn't ideal as it leaves more room for human error.
+
+Also note the readability of the file. It's a bit less human readable than we might like.
+
+```python
+from setuptools import setup, find_packages
+from os import path
+
+version = '1.0.12'
+
+# Loading Readme for Description on PyPi
+this_directory = path.abspath(path.dirname(__file__))
+with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
+ long_description = f.read()
+
+setup(
+ name='gemgis',
+ version=version,
+ packages=find_packages(exclude=('test', 'data', 'notebooks')),
+ include_package_data=True,
+ install_requires=[],
+ url='https://github.com/cgre-aachen/gemgis',
+ license='LGPL v3',
+ author='Alexander JΓΌstel, Arthur Endlein Correia, Florian Wellmann, Marius Pischke',
+ author_email='alexander.juestel@rwth-aachen.de',
+ description="GemGIS is a Python-based, open-source spatial data processing library.
+ It is capable of preprocessing spatial data such as vector data
+ raster data, data obtained from online services and many more data formats.",
+ keywords=['geology', 'structural geology', 'GIS', 'spatial data'],
+ long_description=long_description,
+ long_description_content_type='text/markdown'
+)
+```
+
+Below all of the information above has been moved to a pyproject.toml file.
+
+At the top you specify the build system which in this case is setuptools.
+This specific package is also referencing using setuptools_scm which is a versioning
+tool that works with setuptools to deermine your package's current version at
+build time using git tags.
+
+```toml
+[build-system]
+requires = ["setuptools>=68.1.2",
+ "setuptools_scm[toml]>=6.2"]
+build-backend = "setuptools.build_meta"
+```
+
+Below the metadata from the setup.py file, has been migrated over to the [project] table
+in the pyproject.toml file.
+
+```toml
+[build-system]
+requires = ["setuptools>=68.1.2"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "GemGIS"
+authors = [
+ {name = "Alexander JΓΌstel", email = "alexander.juestel@rwth-aachen.de"},
+]
+# add any other maintainers here
+maintainers = [
+ {name = "Alexander JΓΌstel", email = "alexander.juestel@rwth-aachen.de"},
+]
+description = "Spatial data processing for geomodeling"
+keywords = ['geology', 'structural geology', 'GIS', 'spatial data']
+readme = "README.md"
+license = {file = LICENSE}
+version = 1.0.12
+requires-python = ">=3.9"
+classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Science/Research',
+ 'Topic :: Scientific/Engineering :: Information Analysis',
+ 'Programming Language :: Python :: 3.9',
+ 'Programming Language :: Python :: 3.10',
+ 'Programming Language :: Python :: 3.11',
+ 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
+ 'Operating System :: OS Independent',
+]
+dependencies = [
+ "geopandas",
+ "rasterio",
+ "pyvista",
+]
+
+[project.urls]
+Documentation = 'https://gemgis.readthedocs.io/'
+Home = 'https://gemgis.readthedocs.io/'
+"Bug Tracker" = 'https://github.com/cgre-aachen/gemgis/issues'
+Repository = 'https://github.com/cgre-aachen/gemgis'
+Source Code = 'https://github.com/cgre-aachen/gemgis'
+```
+
+**breakout**
+Note that above under project.urls some of the key values are in quotes. This is because those keys contain two words with a space in between. if you have more
+than a single word on the left of the equal sign in a toml file,
+then you will need quotes.
+
+## Using dynamic versioning
+
+If you want to use a tool such as setuptools_Scm or pdm's dynamic versioning you can make a few small modifications to the above file.
+
+Rather than setting up the version like this in the project table:
+
+`version = 1.0.12`
+
+You set the version to be dynamic like this:
+
+`dynamic=["version"]`
+
+and then you'd add `setuptools_Scm[toml]` to your build setup:
+
+```toml
+[build-system]
+requires = ["setuptools>=68.1.2", "setuptools_scm[toml]>=6.2"]
+build-backend = "setuptools.build_meta"
+```
+
+A few reminders
+
+1. make sure that the classifiers that you select are from the [PyPI list of accepted classifiers](https://pypi.org/classifiers/)
+2. Most of the project table metadata is optional. You really only NEED the version and name of your project. However we strongly recommend that you use other project metadata as it will help users find your project on pypi and better understand what it does.
+
+breakout:
+If you want to see an example of a pyproject.toml file that uses setuptools and setuptools_scm plus the recommended package src/ layout, [click here.](https://github.com/stravalib/stravalib/blob/main/pyproject.toml)
diff --git a/tutorials/image.png b/tutorials/image.png
new file mode 100644
index 000000000..4d8830667
Binary files /dev/null and b/tutorials/image.png differ