Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
"y",
"n"
],
"publish_to_pypi": [
"y",
"n"
"publish_python_package": [
"pypi",
"azure_artifacts",
"None"
],
"deptry": [
"y",
Expand Down
18 changes: 14 additions & 4 deletions docs/features/cicd.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,20 @@ files to set-up the environment, run the tests, and check the code
formatting.

`on-release-main.yml` does all of the former whenever a new release is
made on the `main` branch. In addition, `on-release-main.yml` also
publishes the project to PyPI if `publish_to_pypi` is set to
`"y"`, and it builds and deploys the documentation
if `mkdocs` is set to `"y"`. To learn more about these features,
made on the `main` branch.
In addition, `on-release-main.yml` also
publishes the project to different package indexes depending on `publish_python_package` value.
- pypi
- PyPI
- azure_artifacts
- Azure Artifacts
- None
- Doesn't publish

It also it builds and deploys the documentation
if `mkdocs` is set to `"y"`.

To learn more about these features,
see [Publishing to PyPI](./publishing.md) and [Documentation with MkDocs](./mkdocs.md)

Additionally, all workflows check for compatibility with multiple Python
Expand Down
10 changes: 7 additions & 3 deletions docs/features/publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

## Releasing from Github

When `publish_to_pypi` is set to `"y"`, the
`on-release-main.yml` workflow publishes the code to
[PyPI](https://pypi.org) whenever a [new release](./cicd.md#how-to-trigger-a-release) is made.
Depending on `publish_python_package` value, `on-release-main.yml` workflow publishes the code to different locations whenever a [new release](./cicd.md#how-to-trigger-a-release) is made.
- pypi
- [PyPI](https://pypi.org)
- azure_artifacts
- [Azure Artifacts](https://azure.microsoft.com/en-us/products/devops/artifacts)
- None
- Doesn't publish

Before you can succesfully publish your project from the release workflow, you need to add some secrets to your github repository so
they can be used as environment variables.
Expand Down
4 changes: 2 additions & 2 deletions docs/prompt_arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ A short description of your project.
workflows to setup the environment and run code formatting checks
and unittests.

**publish_to_pypi**
**publish_python_package**

`"y"` or `"n"`. Adds functionality to the
`"pypi"`, `"azure_artifacts"` or `"None"`. Adds functionality to the
`Makefile` and Github workflows to make publishing your code as
simple as creating a new release release on Github. For more info,
see
Expand Down
5 changes: 4 additions & 1 deletion hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ def move_dir(src: str, target: str) -> None:
if "{{cookiecutter.include_github_actions}}" != "y":
remove_dir(".github")
else:
if "{{cookiecutter.mkdocs}}" != "y" and "{{cookiecutter.publish_to_pypi}}" == "n":
if "{{cookiecutter.mkdocs}}" != "y" and "{{cookiecutter.publish_python_package}}" not in [
"pypi",
"azure_artifacts",
]:
remove_file(".github/workflows/on-release-main.yml")

if "{{cookiecutter.mkdocs}}" != "y":
Expand Down
26 changes: 22 additions & 4 deletions tests/test_cookiecutter.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,34 @@ def test_not_devcontainer(cookies, tmp_path):

def test_cicd_contains_pypi_secrets(cookies, tmp_path):
with run_within_dir(tmp_path):
result = cookies.bake(extra_context={"publish_to_pypi": "y"})
result = cookies.bake(extra_context={"publish_python_package": "pypi"})
assert result.exit_code == 0
assert is_valid_yaml(result.project_path / ".github" / "workflows" / "on-release-main.yml")
assert file_contains_text(f"{result.project_path}/.github/workflows/on-release-main.yml", "PYPI_TOKEN")
assert file_contains_text(f"{result.project_path}/Makefile", "build-and-publish")


def test_cicd_contains_azure_artifacts_secrets(cookies, tmp_path):
with run_within_dir(tmp_path):
result = cookies.bake(extra_context={"publish_python_package": "azure_artifacts"})
assert result.exit_code == 0
assert is_valid_yaml(result.project_path / ".github" / "workflows" / "on-release-main.yml")

assert file_contains_text(
f"{result.project_path}/.github/workflows/on-release-main.yml", "UV_INDEX_PRIVATE_REGISTRY_PASSWORD"
)
assert file_contains_text(
f"{result.project_path}/.github/workflows/on-release-main.yml", "UV_INDEX_PRIVATE_REGISTRY_USERNAME"
)
assert file_contains_text(f"{result.project_path}/.github/workflows/on-release-main.yml", "UV_PUBLISH_PASSWORD")
assert file_contains_text(f"{result.project_path}/.github/workflows/on-release-main.yml", "UV_PUBLISH_USERNAME")

assert file_contains_text(f"{result.project_path}/Makefile", "build-and-publish")


def test_dont_publish(cookies, tmp_path):
with run_within_dir(tmp_path):
result = cookies.bake(extra_context={"publish_to_pypi": "n"})
result = cookies.bake(extra_context={"publish_python_package": "None"})
assert result.exit_code == 0
assert is_valid_yaml(result.project_path / ".github" / "workflows" / "on-release-main.yml")
assert not file_contains_text(
Expand Down Expand Up @@ -153,11 +171,11 @@ def test_not_codecov(cookies, tmp_path):

def test_remove_release_workflow(cookies, tmp_path):
with run_within_dir(tmp_path):
result = cookies.bake(extra_context={"publish_to_pypi": "n", "mkdocs": "y"})
result = cookies.bake(extra_context={"publish_python_package": "None", "mkdocs": "y"})
assert result.exit_code == 0
assert os.path.isfile(f"{result.project_path}/.github/workflows/on-release-main.yml")

result = cookies.bake(extra_context={"publish_to_pypi": "n", "mkdocs": "n"})
result = cookies.bake(extra_context={"publish_python_package": "None", "mkdocs": "n"})
assert result.exit_code == 0
assert not os.path.isfile(f"{result.project_path}/.github/workflows/on-release-main.yml")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ on:
types: [published]

jobs:
{% if cookiecutter.publish_to_pypi == "y" %}
set-version:
runs-on: ubuntu-24.04
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push changes
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: main

- name: Export tag
id: vars
Expand All @@ -29,6 +33,13 @@ jobs:
name: pyproject-toml
path: pyproject.toml

- name: Commit pyproject.toml version update
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: {% raw %}"Update version to ${{ steps.vars.outputs.tag }}"{% endraw %}
file_pattern: pyproject.toml
if: {% raw %}${{ github.event_name == 'release' }}{% endraw %}
{% if cookiecutter.publish_python_package == "pypi" %}
publish:
runs-on: ubuntu-latest
needs: [set-version]
Expand All @@ -52,9 +63,37 @@ jobs:
env:
UV_PUBLISH_TOKEN: {% raw %}${{ secrets.PYPI_TOKEN }}{% endraw %}
{% endif %}
{% if cookiecutter.publish_python_package == "azure_artifacts" %}
publish:
runs-on: ubuntu-latest
needs: [set-version]
steps:
- name: Check out
uses: actions/checkout@v4

- name: Set up the environment
uses: ./.github/actions/setup-python-env

- name: Download updated pyproject.toml
uses: actions/download-artifact@v4
with:
name: pyproject-toml

- name: Build package
run: uv build
env:
UV_INDEX_PRIVATE_REGISTRY_USERNAME: {% raw %}${{ secrets.UV_INDEX_PRIVATE_REGISTRY_USERNAME }}{% endraw %}
UV_INDEX_PRIVATE_REGISTRY_PASSWORD: {% raw %}${{ secrets.UV_INDEX_PRIVATE_REGISTRY_PASSWORD }}{% endraw %}

- name: Publish package to Azure Artifacts
run: uv publish --index private-registry
env:
UV_PUBLISH_USERNAME: {% raw %}${{ secrets.UV_PUBLISH_USERNAME }}{% endraw %}
UV_PUBLISH_PASSWORD: {% raw %}${{ secrets.UV_PUBLISH_PASSWORD }}{% endraw %}
{% endif %}
{%- if cookiecutter.mkdocs == "y" %}
deploy-docs:
{%- if cookiecutter.publish_to_pypi == "y" %}
{%- if cookiecutter.publish_python_package in ["pypi", "azure_artifacts"] %}
needs: publish
{%- endif %}
runs-on: ubuntu-latest
Expand Down
13 changes: 12 additions & 1 deletion {{cookiecutter.project_name}}/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ clean-build: ## Clean build artifacts
@echo "🚀 Removing build artifacts"
@uv run python -c "import shutil; import os; shutil.rmtree('dist') if os.path.exists('dist') else None"

{%- if cookiecutter.publish_to_pypi == "y"%}
{%- if cookiecutter.publish_python_package == "pypi"%}

.PHONY: publish
publish: ## Publish a release to PyPI.
Expand All @@ -47,6 +47,17 @@ publish: ## Publish a release to PyPI.
build-and-publish: build publish ## Build and publish.
{%- endif%}

{%- if cookiecutter.publish_python_package == "azure_artifacts"%}

.PHONY: publish
publish: ## Publish a release to Azure Artifacts.
@echo "🚀 Publishing to Azure Artifacts."
@uv publish --index private-registry

.PHONY: build-and-publish
build-and-publish: build publish ## Build and publish.
{%- endif%}

{%- if cookiecutter.mkdocs == "y" %}

.PHONY: docs-test
Expand Down
11 changes: 10 additions & 1 deletion {{cookiecutter.project_name}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,23 @@ To enable the code coverage reports, see [here](https://fpgmaas.github.io/cookie

## Releasing a new version

{% if cookiecutter.publish_to_pypi == "y" -%}
{% if cookiecutter.publish_python_package == "pypi" -%}

- Create an API Token on [PyPI](https://pypi.org/).
- Add the API Token to your projects secrets with the name `PYPI_TOKEN` by visiting [this page](https://github.com/{{cookiecutter.author_github_handle}}/{{cookiecutter.project_name}}/settings/secrets/actions/new).
- Create a [new release](https://github.com/{{cookiecutter.author_github_handle}}/{{cookiecutter.project_name}}/releases/new) on Github.
- Create a new tag in the form `*.*.*`.

For more details, see [here](https://fpgmaas.github.io/cookiecutter-uv/features/cicd/#how-to-trigger-a-release).

{%- endif %}

{% if cookiecutter.publish_python_package == "azure_artifacts" -%}

### How to Publish Python Package to Azure DevOps Artifacts

https://gist.github.com/Riveascore/3776b514ce9d133d786d51a685b9a2c7

{%- endif %}

---
Expand Down