diff --git a/.github/actions/package-and-upload-artifacts/action.yaml b/.github/actions/package-and-upload-artifacts/action.yaml new file mode 100644 index 0000000..a06b83c --- /dev/null +++ b/.github/actions/package-and-upload-artifacts/action.yaml @@ -0,0 +1,49 @@ +name: Package and upload artifacts +description: Package a Python project and upload the artifacts and release notes +inputs: + tag-name: + description: 'The name of the tag for the GitHub release' + required: true +runs: + using: 'composite' + steps: + - name: Parse changelog for release notes + shell: bash + run: | + function extract_version_content() { + changelog=$1 + target_version=$2 + + awk -v target="$target_version" ' + /^## / { + if (found) exit; + version=$2; + if (version == target) found=1; + next; + } + found { print; } + ' <<< "$changelog" + } + + changelog=$(cat "CHANGELOG.md") + target_version=${{ inputs.tag-name }} + echo "TAG_NAME=$target_version" >> $GITHUB_ENV + content=$(extract_version_content "$changelog" "$target_version") + + if [ -n "$content" ]; then + echo "::notice::Found release notes for ${target_version}" + echo "$content" >> release-notes.md + else + echo "::warning::Did not find release notes for ${target_version}" + touch release-notes.md + fi + + - name: Upload release notes + uses: actions/upload-artifact@v4 + with: + name: release-notes + path: release-notes.md + + - name: Package and upload artifacts + if: ${{ env.PACKAGE == 'true' }} + uses: hynek/build-and-inspect-python-package@v2 diff --git a/.github/actions/release/action.yaml b/.github/actions/release/action.yaml new file mode 100644 index 0000000..649bb60 --- /dev/null +++ b/.github/actions/release/action.yaml @@ -0,0 +1,39 @@ +name: Release +description: Create a GitHub release and upload the package to PyPI +inputs: + pypi-api-token: + description: 'PyPI API token' + required: true + tag-name: + description: 'The name of the tag for the GitHub release' + required: true + github-token: + description: 'GitHub token' + required: true + +runs: + using: "composite" + steps: + - name: Download packages built by build-and-inspect-python-package + uses: actions/download-artifact@v4 + with: + name: Packages + path: dist + + - name: Download release notes + uses: actions/download-artifact@v4 + with: + name: release-notes + + - name: Create a GitHub release + uses: softprops/action-gh-release@v1 + with: + files: dist/* + tag_name: "${{ inputs.tag-name }}" + body_path: release-notes.md + token: ${{ inputs.github-token }} + + - name: Upload package to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ inputs.pypi-api-token }} diff --git a/.github/actions/setup-python-and-git/action.yaml b/.github/actions/setup-python-and-git/action.yaml new file mode 100644 index 0000000..44b7247 --- /dev/null +++ b/.github/actions/setup-python-and-git/action.yaml @@ -0,0 +1,23 @@ +name: checkout-and-setup-python +description: 'Checkout the repository and setup Python' +inputs: + python-version: + description: 'Python version to use' + required: false + default: '3.11' +runs: + using: 'composite' + steps: + - uses: actions/setup-python@v4 + name: Setup Python + with: + python-version: ${{ inputs.python-version }} + cache: 'pip' # caching pip dependencies + + - name: Git check + run: | + git config --global user.email "action@github.actions" + git config --global user.name "Testing Git" + git --version + git config --list + shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..be006de --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# Keep GitHub Actions up to date with GitHub's Dependabot... +# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + groups: + github-actions: + patterns: + - "*" # Group all Actions updates into a single larger pull request + schedule: + interval: weekly diff --git a/.github/workflows/bumpversion.yaml b/.github/workflows/bumpversion.yaml deleted file mode 100644 index 07ec6c4..0000000 --- a/.github/workflows/bumpversion.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: Auto-bump the version -on: - pull_request: - types: [closed] - branches: [master] - workflow_dispatch: - inputs: - dry_run: - description: Don't actually do the work, just describe it - default: true - type: boolean - new_version: - description: Set the version to a specific value - required: false - type: string - verbose: - description: The amount of output detail - default: 0 - type: choice - options: - - "0" - - "1" - - "2" - -jobs: - bumpversion: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - token: ${{ secrets.PAT }} - - - uses: actions/setup-python@v4 - with: - python-version: '3.11' - cache: 'pip' # caching pip dependencies - - - name: Install requirements - run: | - python -m pip install generate-changelog bump-my-version build - - - name: Git check - run: | - git config --global user.email "bump-my-version@github.actions" - git config --global user.name "Testing Git" - git --version - git config --list - - - name: Generate the changelog and get the release hint - id: generate-changelog - run: | - RELEASE_KIND=$(generate-changelog --output release-hint) - echo "::notice::Suggested release type is: ${RELEASE_KIND}" - echo "RELEASE_KIND=$RELEASE_KIND" >> $GITHUB_ENV - echo "release-kind=$RELEASE_KIND" >> $GITHUB_OUTPUT - - - name: Bump Version auto - if: ${{ github.event_name != 'workflow_dispatch' }} - shell: bash - run: | - if [[ $RELEASE_KIND != "no-release" ]]; then - bump-my-version -v "$RELEASE_KIND" - git push - git push --tags - fi - - - name: Bump Version manual - if: ${{ github.event_name == 'workflow_dispatch' }} - shell: bash - env: - BUMPVERSION_DRY_RUN: ${{ inputs.dry_run }} - BUMPVERSION_NEW_VERSION: ${{ inputs.tags }} - BUMPVERSION_VERBOSE: ${{ inputs.verbose }} - run: | - PR_NUMBER=$(gh pr view --json number -q .number || echo "") - REVISION=$(git describe --tags --long | awk -F- '{print $2}') - export PR_NUMBER REVISION - case "$RELEASE_KIND" in - major|minor|patch) - bump-my-version bump "$RELEASE_KIND" - if [[ BUMPVERSION_DRY_RUN == "false" ]]; then - git push - git push --tags - fi - ;; - dev) - bump-my-version bump -v --no-tag --no-commit "dev" - python -m build - ;; - esac - - name: Publish package - if: ${{ github.event_name == 'workflow_dispatch' && success() }} - uses: pypa/gh-action-pypi-publish@v1.5.0 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index f5eafe5..0000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,72 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ master ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ master ] - schedule: - - cron: '37 21 * * 5' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'python' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/docs-final.yml b/.github/workflows/docs-final.yml new file mode 100644 index 0000000..71200ad --- /dev/null +++ b/.github/workflows/docs-final.yml @@ -0,0 +1,31 @@ +name: Deploy final documentation + +on: + push: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.PAT }} + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + git pull --all + python -m pip install ".[docs]" + + - name: Build and deploy documentation + run: | + mkdocs gh-deploy --strict -v diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml new file mode 100644 index 0000000..db4fbd1 --- /dev/null +++ b/.github/workflows/docs-preview.yml @@ -0,0 +1,38 @@ +name: Deploy PR previews + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - closed + +concurrency: preview-${{ github.ref }} + +jobs: + deploy-preview: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install dependencies + run: | + python -m pip install ".[docs]" + + - name: Build documentation + run: | + mkdocs build --strict -v + + - name: Deploy preview + uses: rossjrw/pr-preview-action@v1 + with: + source-dir: ./site/ diff --git a/.github/workflows/publish-docs.yaml b/.github/workflows/publish-docs.yaml deleted file mode 100644 index d2fe14f..0000000 --- a/.github/workflows/publish-docs.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: Publish Documentation - -on: - push: - tags: - - '*' - -jobs: - publish-docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v3 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements/docs.txt - - name: Build and publish docs - run: make pubdocs diff --git a/.github/workflows/publish-package.yaml b/.github/workflows/publish-package.yaml deleted file mode 100644 index 63174b5..0000000 --- a/.github/workflows/publish-package.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries - -name: Upload Python Package - -on: - push: - tags: - - '*' - -jobs: - publish-package: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.11' - cache: 'pip' # caching pip dependencies - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@v1.5.0 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..def335a --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,78 @@ +name: Bump the version on merge +on: + pull_request: + types: [closed] + branches: [master] + +jobs: + version: + permissions: + id-token: write + pull-requests: read + contents: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout the repository + with: + fetch-depth: 0 + token: ${{ secrets.PAT }} + + - name: Setup Python and Git + uses: ./.github/actions/setup-python-and-git + with: + python-version: '3.11' + + - name: Install requirements + run: | + python -m pip install generate-changelog bump-my-version + + - name: Generate the changelog and get the release hint + id: generate-changelog + run: | + RELEASE_KIND=$(generate-changelog --output release-hint) + echo "::notice::Suggested release type for this branch is: ${RELEASE_KIND}" + echo "RELEASE_KIND=$RELEASE_KIND" >> $GITHUB_ENV + echo "release-kind=$RELEASE_KIND" >> $GITHUB_OUTPUT + echo "PACKAGE=false" >> $GITHUB_ENV + + - name: Get Pull Request Number + id: pr + run: | + PR_NUMBER=$(gh pr view --json number -q .number || echo "${{ github.event.number }}") + echo "pull_request_number=${PR_NUMBER}" >> $GITHUB_OUTPUT + echo "::notice::PR_NUMBER is ${PR_NUMBER}" + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + + - name: Bump version + if: ${{ env.RELEASE_KIND != 'no-release' }} + shell: bash + run: | + case "$RELEASE_KIND" in + major|minor|patch) + bump-my-version bump --allow-dirty --verbose "$RELEASE_KIND" + echo "TAG_NAME=$(bump-my-version show current_version)" >> $GITHUB_ENV + git push + git push --tags + echo "PACKAGE=true" >> $GITHUB_ENV + ;; + dev) + echo "Intentionally not bumping version for dev release" + ;; + esac + + - name: Package and upload artifacts + if: ${{ env.PACKAGE == 'true' }} + uses: ./.github/actions/package-and-upload-artifacts + with: + tag-name: ${{ env.TAG_NAME }} + + - name: Create a GitHub release + if: ${{ env.PACKAGE == 'true' }} + uses: ./.github/actions/release + with: + tag-name: ${{ env.TAG_NAME }} + github-token: ${{ secrets.PAT }} + pypi-api-token: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 18efdbe..a83b2d2 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,45 +1,94 @@ -name: Test +name: CI on: - # Trigger the workflow on push or pull request, - # but only for the main branch - push: - branches: - - master pull_request: - branches: - - master - # Also trigger on page_build, as well as release created events - page_build: - release: - types: # Does not affect page_build above. - - created + types: [opened, synchronize] + branches: [master] + +defaults: + run: + shell: bash + +env: + PYTHONUTF8: "1" jobs: - test: - runs-on: ubuntu-latest + test: strategy: fail-fast: false matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + os: + - ubuntu-latest + - windows-latest + python-version: + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + runs-on: ${{ matrix.os }} + steps: + - uses: ./.github/actions/setup-python + with: + python-version: ${{ matrix.python-version }} + - name: Check git is working + run: | + git config --global user.email "test-git@github.actions" + git config --global user.name "Testing Git" + git --version + git config --list + - name: Install test dependencies + run: pip install '.[test]' + - name: Test + run: pytest --cov-report=xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: test-reports/coverage.xml + flags: python-${{ matrix.python-version }} + verbose: true # optional (default = false) + env_vars: OS,PYTHON + # Upload to Test PyPI. + release-test-pypi: + name: Publish in-dev package to test.pypi.org + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements/test.txt - - name: Test with pytest - run: | - pytest - coverage lcov - - uses: codecov/codecov-action@v3 - with: - files: ./coverage.xml - flags: python-${{ matrix.python-version }} - verbose: true # optional (default = false) + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + token: ${{ secrets.PAT }} + ref: ${{ github.head_ref }} + + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + cache: 'pip' # caching pip dependencies + + - name: Install requirements + shell: bash + run: | + python -m pip install --disable-pip-version-check --no-python-version-warning build + python -m pip install --disable-pip-version-check --no-python-version-warning -e . + + - name: Set dev version + shell: bash + run: | + export PR_NUMBER=$(gh pr view --json number -q .number || echo "") + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV + echo "::notice::PR_NUMBER is: ${PR_NUMBER}" + bump-my-version bump dev bumpversion/__init__.py --no-commit --no-tag --no-configured-files -v + env: + GH_TOKEN: ${{ secrets.PAT }} + + - name: Package + shell: bash + run: | + python -m build + + - name: Upload package to Test PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/version-preview.yaml b/.github/workflows/version-preview.yaml new file mode 100644 index 0000000..e65a9ab --- /dev/null +++ b/.github/workflows/version-preview.yaml @@ -0,0 +1,69 @@ +name: Display the version hint +on: + pull_request: + types: [synchronize, opened, reopened, ready_for_review] + branches: [master] + +jobs: + preview-version-hint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + name: Checkout the repository + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + token: ${{ secrets.PAT }} + + - name: Setup Python and Git + uses: ./.github/actions/setup-python-and-git + with: + python-version: '3.11' + + - name: Install requirements + run: | + python -m pip install generate-changelog bump-my-version + + - name: Get the release hint + id: generate-changelog + run: | + RELEASE_KIND=$(generate-changelog --output release-hint --branch-override ${{ github.base_ref }} --skip-output-pipeline) + echo "::notice::Suggested release type upon merge to ${{ github.base_ref }}: ${RELEASE_KIND}" + echo "RELEASE_KIND=$RELEASE_KIND" >> $GITHUB_ENV + echo "release-kind=$RELEASE_KIND" >> $GITHUB_OUTPUT + + - name: Get Pull Request Number + id: pr + run: | + PR_NUMBER=$(gh pr view --json number -q .number || echo "${{ github.event.number }}") + echo "pull_request_number=${PR_NUMBER}" >> $GITHUB_OUTPUT + echo "::notice::PR_NUMBER is ${PR_NUMBER}" + echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV + env: + GITHUB_TOKEN: ${{ secrets.PAT }} + + - name: Bump version dry run + if: ${{ env.RELEASE_KIND != 'no-release' }} + shell: bash + run: | + # This will display a full log of what would happen if we were to bump the version. + bump-my-version bump --dry-run --verbose "$RELEASE_KIND" + + # This retrieves the current and new version numbers as a JSON-formatted string. + VERSION_INFO=$(bump-my-version show --format json --increment "$RELEASE_KIND" current_version new_version) + echo "CURRENT_VERSION=$(echo $VERSION_INFO | jq -r .current_version)" >> $GITHUB_ENV + echo "NEW_VERSION=$(echo $VERSION_INFO | jq -r .new_version)" >> $GITHUB_ENV + + - name: Set no-release information + if: ${{ env.RELEASE_KIND == 'no-release' }} + run: | + echo "CURRENT_VERSION=$(bump-my-version show current_version)" >> $GITHUB_ENV + echo "NEW_VERSION=$(bump-my-version show current_version)" >> $GITHUB_ENV + + - name: Display the version hint + uses: s-gehring/singleton-comment@v1 + with: + comment-body: | + **Version hint:** ${{ env.RELEASE_KIND }} + **Current version:** ${{ env.CURRENT_VERSION }} + **New version (when merged):** ${{ env.NEW_VERSION }}