├── .github └── workflows │ ├── publish.yml │ ├── setup.yml │ └── test.yml └── README.md /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Python Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 16 | steps: 17 | - uses: actions/checkout@v4 18 | - name: Set up Python ${{ matrix.python-version }} 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: ${{ matrix.python-version }} 22 | cache: pip 23 | cache-dependency-path: pyproject.toml 24 | - name: Install dependencies 25 | run: | 26 | pip install '.[test]' 27 | - name: Run tests 28 | run: | 29 | pytest 30 | deploy: 31 | runs-on: ubuntu-latest 32 | needs: [test] 33 | environment: release 34 | permissions: 35 | id-token: write 36 | steps: 37 | - uses: actions/checkout@v4 38 | - name: Set up Python 39 | uses: actions/setup-python@v5 40 | with: 41 | python-version: "3.12" 42 | cache: pip 43 | cache-dependency-path: pyproject.toml 44 | - name: Install dependencies 45 | run: | 46 | pip install setuptools wheel build 47 | - name: Build 48 | run: | 49 | python -m build 50 | - name: Publish 51 | uses: pypa/gh-action-pypi-publish@release/v1 -------------------------------------------------------------------------------- /.github/workflows/setup.yml: -------------------------------------------------------------------------------- 1 | name: Execute template to populate repository 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | permissions: 8 | contents: write 9 | 10 | jobs: 11 | setup-repo: 12 | if: ${{ github.repository != 'simonw/click-app-template-repository' }} 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 18 | ref: ${{ github.head_ref }} 19 | 20 | - name: Install cookiecutter 21 | run: pip3 install cookiecutter 22 | 23 | - uses: actions/github-script@v4 24 | id: fetch-repo-and-user-details 25 | with: 26 | script: | 27 | const query = `query($owner:String!, $name:String!) { 28 | repository(owner:$owner, name:$name) { 29 | name 30 | description 31 | owner { 32 | login 33 | ... on User { 34 | name 35 | } 36 | ... on Organization { 37 | name 38 | } 39 | } 40 | } 41 | }`; 42 | const variables = { 43 | owner: context.repo.owner, 44 | name: context.repo.repo 45 | } 46 | const result = await github.graphql(query, variables) 47 | console.log(result) 48 | return result 49 | 50 | - name: Rebuild contents using cookiecutter 51 | env: 52 | INFO: ${{ steps.fetch-repo-and-user-details.outputs.result }} 53 | run: | 54 | export REPO_NAME=$(echo $INFO | jq -r '.repository.name') 55 | # Run cookiecutter 56 | pushd /tmp 57 | cookiecutter gh:simonw/click-app --no-input \ 58 | app_name=$REPO_NAME \ 59 | description="$(echo $INFO | jq -r .repository.description | sed 's/\"/\\\"/g')" \ 60 | github_username="$(echo $INFO | jq -r .repository.owner.login)" \ 61 | author_name="$(echo $INFO | jq -r .repository.owner.name)" 62 | popd 63 | # Move generated content to root directory of repo 64 | mv /tmp/$REPO_NAME/* . 65 | # And .gitignore too: 66 | mv /tmp/$REPO_NAME/.gitignore . 67 | # Delete the setup.yml workflow, it has served its purpose 68 | rm .github/workflows/setup.yml 69 | 70 | - name: Force push new repo contents 71 | uses: stefanzweifel/git-auto-commit-action@v4 72 | with: 73 | commit_message: "Initial library structure" 74 | push_options: --force 75 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up Python ${{ matrix.python-version }} 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: ${{ matrix.python-version }} 20 | cache: pip 21 | cache-dependency-path: pyproject.toml 22 | - name: Install dependencies 23 | run: | 24 | pip install '.[test]' 25 | - name: Run tests 26 | run: | 27 | pytest -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Template repository for creating new Python Click CLI tools 2 | 3 | This GitHub [template repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/creating-a-repository-from-a-template) can be used to create a new repository with the skeleton of a Python [Click](https://click.palletsprojects.com/) CLI tool, based on the [click-app](https://github.com/simonw/click-app) cookiecutter. 4 | 5 | Start here: https://github.com/simonw/click-app-template-repository/generate 6 | 7 | The name of your repository will be the name of the CLI tool, and also the name of the Python package that you publish to [PyPI](https://pypi.org/) - so make sure that name is not taken already! 8 | 9 | Add a one-line description of your CLI tool, then click "Create repository from template". 10 | 11 | ![Screenshot of the create repository interface](https://user-images.githubusercontent.com/9599/131272183-d2f1bb50-1ca1-42f2-936d-f23a6cbdbe13.png) 12 | 13 | Once created, your new repository will execute a GitHub Actions workflow that uses cookiecutter to rewrite the repository to the desired state. This make take 30 seconds or so. 14 | 15 | You can see an example of a repository generated using this template here: 16 | 17 | - https://github.com/simonw/click-app-template-repository-demo 18 | 19 | ## GitHub Actions setup by this repository 20 | 21 | The `test.yml` GitHub Actions workflow will run your tests automatically any time you push a change to the repo. 22 | 23 | The `publish.yml` Action runs when you create a new GitHub release. It can build and upload your package to [PyPI](https://pypi.org/). 24 | 25 | For this to work, you need to create an API token for your PyPI account and add that to your repository as a secret called `PYPI_TOKEN`. 26 | 27 | See [Publishing your library as a package to PyPI](https://github.com/simonw/click-app#publishing-your-library-as-a-package-to-pypi) for details. --------------------------------------------------------------------------------