From 9b10151e933066bd8a54d54a21a7f864f3bebdc8 Mon Sep 17 00:00:00 2001 From: Matthew Watkins Date: Fri, 6 Sep 2024 13:27:45 +0100 Subject: [PATCH] Chore: Replace legacy workflows Signed-off-by: Matthew Watkins --- .github/workflows/bootstrap.yaml | 342 ---------------------- .github/workflows/builds.yaml | 99 ------- .github/workflows/codeql.yml | 81 ------ .github/workflows/dependencies.yaml | 51 +++- .github/workflows/documentation.yaml | 60 ---- .github/workflows/new-release.yaml | 223 -------------- .github/workflows/release.yaml | 184 ------------ .github/workflows/repository.yaml | 421 +++++++++++++++++++++++++++ .github/workflows/security.yaml | 68 ----- .github/workflows/test-release.yaml | 152 ---------- .github/workflows/testing.yaml | 68 ----- 11 files changed, 460 insertions(+), 1289 deletions(-) delete mode 100644 .github/workflows/bootstrap.yaml delete mode 100644 .github/workflows/builds.yaml delete mode 100644 .github/workflows/codeql.yml delete mode 100644 .github/workflows/documentation.yaml delete mode 100644 .github/workflows/new-release.yaml delete mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/repository.yaml delete mode 100644 .github/workflows/security.yaml delete mode 100644 .github/workflows/test-release.yaml delete mode 100644 .github/workflows/testing.yaml diff --git a/.github/workflows/bootstrap.yaml b/.github/workflows/bootstrap.yaml deleted file mode 100644 index 4886b3b4..00000000 --- a/.github/workflows/bootstrap.yaml +++ /dev/null @@ -1,342 +0,0 @@ ---- -name: "♻️ Update shared DevOps tooling" - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - schedule: - - cron: "0 8 * * MON" - -jobs: - update-actions: - name: "Update DevOps tooling" - runs-on: ubuntu-latest - permissions: - # IMPORTANT: mandatory to create or update content/actions/pr - contents: write - actions: write - pull-requests: write - - steps: - - name: "Checkout primary repository" - uses: actions/checkout@v4 - - - name: "Pull devops content from repository" - uses: actions/checkout@v4 - with: - repository: "os-climate/devops-toolkit" - path: ".devops" - - - name: "Update repository workflows and create PR" - id: update-repository - env: - GH_TOKEN: ${{ github.token }} - # yamllint disable rule:line-length - run: | - #SHELLCODESTART - - set -euo pipefail - # set -x - - # Define variables - - DEVOPS_DIR=".devops" - AUTOMATION_BRANCH="update-devops-tooling" - REPO_DIR=$(git rev-parse --show-toplevel) - GIT_ORIGIN=$(git config --get remote.origin.url) - REPO_NAME=$(basename -s .git "$GIT_ORIGIN") - EXCLUDE_FILE=".devops-exclusions" - DEVOPS_REPO='git@github.com:os-climate/devops-toolkit.git' - HEAD_BRANCH=$(git rev-parse --abbrev-ref HEAD) - - # Content folder defines the files and folders to update - FILES="$DEVOPS_DIR/content/files.txt" - FOLDERS="$DEVOPS_DIR/content/folders.txt" - - # Define functions - - perform_folder_operation() { - FS_PATH="$1" - if [ -d "$DEVOPS_DIR"/"$FS_PATH" ]; then - echo "Scanning target folder content at: $FS_PATH" - return 0 - else - echo "Upstream folder NOT found: $FS_PATH [skipping]" - return 1 - fi - } - - # Allows for selective opt-out components on a per-path basis - perform_operation() { - FS_PATH="$1" - if [ ! -f "$DEVOPS_DIR"/"$FS_PATH" ]; then - echo "Skipping missing upstream file at: $FS_PATH" - return 1 - fi - # Elements excluded from processing return exit status 1 - if [ ! -f "$EXCLUDE_FILE" ]; then - return 0 - elif [ "$FS_PATH" = "$EXCLUDE_FILE" ]; then - # The exclusion file itself is never updated by automation - return 1 - elif (grep -Fxq "$FS_PATH" "$EXCLUDE_FILE" > /dev/null); then - # Element listed; exclude from processing - return 1 - else - # Element not found in exclusion file; process it - return 0 - fi - } - - # Only updates file if it has changed - selective_file_copy() { - # Receives a single file path as argument - # SHA_SRC=$(sha1sum "$DEVOPS_DIR/$1" | awk '{print $1}') - # SHA_DST=$(sha1sum "$1" 2>/dev/null | awk '{print $1}' || :) - # if [ "$SHA_SRC" != "$SHA_DST" ]; then - if ! (cmp "$DEVOPS_DIR/$1" "$1"); then - echo "Copying: $1" - cp "$DEVOPS_DIR/$1" "$1" - git add "$1" - fi - } - - check_pr_for_author() { - AUTHOR="$1" - printf "Checking for pull requests by: %s" "$AUTHOR" - # Capture the existing PR number - PR_NUM=$(gh pr list --state open -L 1 \ - --author "$AUTHOR" --json number | \ - grep "number" | sed "s/:/ /g" | awk '{print $2}' | \ - sed "s/}//g" | sed "s/]//g") - if [ -z "$PR_NUM" ]; then - echo " [none]" - return 1 - else - echo " [$PR_NUM]" - echo "Running: gh pr checkout $PR_NUM" - if (gh pr checkout "$PR_NUM"); then - return 0 - else - echo "Failed to checkout GitHub pull request" - echo "Check errors/output for the cause" - return 2 - fi - fi - } - - check_prs() { - # Define users to check for pre-existing pull requests - AUTOMATION_USER="github-actions[bot]" - if [[ -n ${GH_TOKEN+x} ]]; then - GITHUB_USERS="$AUTOMATION_USER" - else - GITHUB_USERS=$(gh api user | jq -r '.login') - # Check local user account first, if enumerated - GITHUB_USERS+=" $AUTOMATION_USER" - fi - - # Check for existing pull requests opened by this automation - for USER in $GITHUB_USERS; do - if (check_pr_for_author "$USER"); then - return 0 - else - STATUS="$?" - fi - if [ "$STATUS" -eq 1 ]; then - continue - elif [ "$STATUS" -eq 2 ]; then - echo "Failed to checkout pull request"; exit 1 - fi - done - return 1 - } - - # Check if script is running in GHA workflow - in_github() { - if [ -z ${GITHUB_RUN_ID+x} ]; then - echo "Script is NOT running in GitHub" - return 1 - else - echo "Script is running in GitHub" - return 0 - fi - } - - # Check if user is logged into GitHub - logged_in_github() { - if (gh auth status); then - echo "Logged in and authenticated to GitHb" - return 0 - else - echo "Not logged into GitHub, some script operations unavailable" - return 1 - fi - } - - # Main script entry point - - echo "Repository name and HEAD branch: $REPO_NAME [$HEAD_BRANCH]" - - # Ensure working from top-level of GIT repository - CURRENT_DIR=$(pwd) - if [ "$REPO_DIR" != "$CURRENT_DIR" ]; then - echo "Changing directory to: $REPO_DIR" - if ! (cd "$REPO_DIR"); then - echo "Error: unable to change directory"; exit 1 - fi - fi - - # Stashing only used during development/testing - # Check if there are unstaged changes - # if ! (git diff --exit-code --quiet); then - # echo "Stashing unstaged changes in current repository" - # git stash -q - # fi - - # Configure GIT environment only if NOT already configured - # i.e. when running in a GitHub Actions workflow - TEST=$(git config -l > /dev/null 2>&1) - if [ -n "$TEST" ]; then - git config user.name "github-actions[bot]" - git config user.email \ - "41898282+github-actions[bot]@users.noreply.github.com" - fi - - - if ! (check_prs); then - # No existing open pull requests found for this repository - - # Remove remote branch if it exists - git push origin --delete "$AUTOMATION_BRANCH" > /dev/null 2>&1 || : - git branch -D "$AUTOMATION_BRANCH" || : - git checkout -b "$AUTOMATION_BRANCH" - else - git fetch origin "$AUTOMATION_BRANCH" - git switch -c "$AUTOMATION_BRANCH" "origin/$AUTOMATION_BRANCH" - fi - - # Only if NOT running in GitHub - # (checkout is otherwise performed by earlier steps) - if ! (in_github); then - # Remove any stale local copy of the upstream repository - if [ -d "$DEVOPS_DIR" ]; then - rm -Rf "$DEVOPS_DIR" - fi - printf "Cloning DevOps repository into: %s" "$DEVOPS_DIR" - if (git clone "$DEVOPS_REPO" "$DEVOPS_DIR" > /dev/null 2>&1); then - echo " [success]" - else - echo " [failed]"; exit 1 - fi - fi - - # Process upstream DevOps repository content and update - - LOCATIONS="" - # Populate list of files to be updated/sourced - while read -ra LINE; - do - for FILE in "${LINE[@]}"; - do - LOCATIONS+="$FILE " - done - done < "$FILES" - - # Gather files from specified folders and append to locations list - while read -ra LINE; - do - for FOLDER in "${LINE[@]}"; - do - # Check to see if this folder should be skipped - if (perform_folder_operation "$FOLDER"); then - # If necessary, create target folder - if [ ! -d "$FOLDER" ]; then - echo "Creating target folder: $FOLDER" - mkdir "$FOLDER" - fi - # Add folder contents to list of file LOCATIONS - FILES=$(cd "$DEVOPS_DIR/$FOLDER"; find . -maxdepth 1 -type f -exec basename {} \;) - for LOCATION in $FILES; do - # Also check if individual files in the folder are excluded - if (perform_operation "$FOLDER/$LOCATION"); then - LOCATIONS+=" $FOLDER/$LOCATION" - fi - done - else - echo "Opted out of folder: $FOLDER" - continue - fi - done; - done < "$FOLDERS" - - # Copy specified files into repository root - for LOCATION in ${LOCATIONS}; do - if (perform_operation "$LOCATION"); then - selective_file_copy "$LOCATION" - else - echo "Not updating: $LOCATION" - fi - done - - # If no changes required, do not throw an error - if [ -z "$(git status --porcelain)" ]; then - echo "No updates/changes to commit"; exit 0 - fi - - # Temporarily disable exit on unbound variable - set +eu +o pipefail - - # Next step is only performed if running as GitHub Action - if [[ -n ${GH_TOKEN+x} ]]; then - # Script is running in a GitHub actions workflow - # Set outputs for use by the next actions/steps - # shellcheck disable=SC2129 - echo "changed=true" >> "$GITHUB_OUTPUT" - echo "branchname=$AUTOMATION_BRANCH" >> "$GITHUB_OUTPUT" - echo "headbranch=$HEAD_BRANCH" >> "$GITHUB_OUTPUT" - # Move to the next workflow step to raise the PR - git push --set-upstream origin "$AUTOMATION_BRANCH" - exit 0 - fi - - # If running shell code locally, continue to raise the PR - - # Reinstate exit on unbound variables - set -euo pipefail - - git status - if ! (git commit -as -S -m "Chore: Update DevOps tooling from central repository [skip ci]" \ - -m "This commit created by automation/scripting" --no-verify); then - echo "Commit failed; aborting"; exit 1 - else - # Push branch to remote repository - git push --set-upstream origin "$AUTOMATION_BRANCH" - # Create PR request - gh pr create \ - --title "Chore: Pull DevOps tooling from upstream repository" \ - --body 'Automated by a GitHub workflow: bootstrap.yaml' - fi - # echo "Unstashing unstaged changes, if any exist" - # git stash pop -q || : - #SHELLCODEEND - - - name: Create Pull Request - if: steps.update-repository.outputs.changed == 'true' - uses: peter-evans/create-pull-request@v6 - # env: - # GITHUB_TOKEN: ${{ github.token }} - with: - # Note: Requires a specific/defined Personal Access Token - token: ${{ secrets.ACTIONS_WORKFLOW }} - commit-message: "Chore: Update DevOps tooling from central repository [skip ci]" - signoff: "true" - base: ${{ steps.update-repository.outputs.headbranch }} - branch: ${{ steps.update-repository.outputs.branchname }} - delete-branch: true - title: "Chore: Update DevOps tooling from central repository [skip ci]" - body: | - Update repository with content from upstream: os-climate/devops-toolkit - labels: | - automated pr - draft: false diff --git a/.github/workflows/builds.yaml b/.github/workflows/builds.yaml deleted file mode 100644 index 0ba22d1f..00000000 --- a/.github/workflows/builds.yaml +++ /dev/null @@ -1,99 +0,0 @@ ---- -name: "🧱 Builds (Matrix)" - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - pull_request: - types: [opened, reopened, edited, synchronize] - branches: - - "*" - - "!update-devops-tooling" - -env: - package-path: "dist/" - -jobs: - get-python-versions: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.parse-project-metadata.outputs.python-matrix-versions }} - - steps: - - uses: actions/checkout@v4 - - - name: "Populate environment variables" - id: parse-project-metadata - # yamllint disable-line rule:line-length - uses: os-climate/devops-reusable-workflows/.github/actions/python-versions-matrix@main - - builds: - name: "Python builds" - needs: [get-python-versions] - runs-on: "ubuntu-latest" - continue-on-error: true - # Don't run when pull request is merged - if: github.event.pull_request.merged == false - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.get-python-versions.outputs.matrix) }} - - steps: - - name: "Populate environment variables" - id: setenv - run: | - echo "Action triggered by user: ${GITHUB_TRIGGERING_ACTOR}" - set -x - datetime=$(date +'%Y%m%d%H%M') - export datetime - echo "datetime=${datetime}" >> "$GITHUB_OUTPUT" - vernum="${{ matrix.python-version }}.${datetime}" - echo "vernum=${vernum}" >> "$GITHUB_OUTPUT" - - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: "Set up Python ${{ matrix.python-version }}" - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: "Tag for test release" - # Delete all local tags, then create a synthetic tag for testing - # Use the date/time to avoid conflicts uploading to Test PyPI - run: | - scripts/dev-versioning.sh "${{ steps.setenv.outputs.vernum }}" - git tag | xargs -L 1 | xargs git tag --delete - git tag "v${{ steps.setenv.outputs.vernum }}" - git checkout "tags/v${{ steps.setenv.outputs.vernum }}" - grep version pyproject.toml - - - name: "Performing build" - run: | - python -m pip install --upgrade pip - if [ -f tox.ini ]; then - pip install tox tox-gh-actions - echo "Found file: tox.ini" - echo "Building with command: tox -e build" - tox -e build - elif [ -f pyproject.toml ]; then - echo "Found file: pyproject.toml" - echo "Building with command: pdm build" - pdm build - else - echo "Neither file found: tox.ini/pyproject.toml" - pip install --upgrade build - echo "Attempting build with: python -m build" - python -m build - fi - - - name: "Validate Artefacts with Twine" - id: twine-check-artefacts - env: - package-path: ${{ env.package-path }} - uses: os-climate/devops-reusable-workflows/.github/actions/twine-check-artefacts@main diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 808a860b..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,81 +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: [ "main", "gh-pages", "master" ] - pull_request: - branches: [ "main", "gh-pages", "master" ] - schedule: - - cron: '26 1 * * 2' - -jobs: - analyze: - name: Analyze (${{ matrix.language }}) - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners (GitHub.com only) - # Consider using larger runners or machines with greater resources for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} - permissions: - # required for all workflows - security-events: write - - # required to fetch internal or private CodeQL packs - packages: read - - # only required for workflows in private repositories - actions: read - contents: read - - strategy: - fail-fast: false - matrix: - include: - - language: javascript-typescript - build-mode: none - - language: python - build-mode: none - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - build-mode: ${{ matrix.build-mode }} - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # ℹ️ 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: matrix.build-mode == 'manual' - shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependencies.yaml b/.github/workflows/dependencies.yaml index 8edd9818..dbc748be 100644 --- a/.github/workflows/dependencies.yaml +++ b/.github/workflows/dependencies.yaml @@ -1,15 +1,46 @@ --- -name: "♻️ Update dependencies" +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: 2024 The Linux Foundation + +name: "♻️ Update Dependencies" # yamllint disable-line rule:truthy on: workflow_dispatch: schedule: - cron: "0 8 1 * *" + pull_request: + types: [opened, reopened, edited, synchronize, closed] + branches: + - "**/**" + - "!update-devops-tooling" + - "!**/update-pdm-lock" + +env: + DEFAULT-PYTHON: "3.11" jobs: + classify-content: + name: "Inspect Repository" + runs-on: ubuntu-latest + outputs: + python: ${{ steps.classify.outputs.python }} + notebooks: ${{ steps.classify.outputs.notebooks }} + + steps: + - uses: actions/checkout@v4 + + - name: "Classify content" + id: classify + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/repository-content-classify@main + update-dependencies: - name: "Update dependencies" + name: "Update Python Modules" + needs: + - classify-content + # Only run if Python code is present in repository + if: needs.classify-content.outputs.python == 'true' runs-on: ubuntu-latest permissions: # IMPORTANT: mandatory to raise the PR @@ -27,22 +58,18 @@ jobs: - name: "Set up Python" uses: actions/setup-python@v5 + with: + python-version: ${{ env.default-python }} - name: "Update Python dependencies" - uses: pdm-project/update-deps-action@v1 + uses: pdm-project/update-deps-action@v1.9 + env: + python-version: ${{ env.default-python }} with: sign-off-commit: "true" - token: ${{ secrets.GH_TOKEN }} + token: ${{ github.token }} commit-message: "Chore: Update dependencies and pdm.lock [skip ci]" pr-title: "Update Python module dependencies" update-strategy: eager # Whether to install PDM plugins before update install-plugins: "false" - - - name: "Export dependencies" - run: | - pdm export --without-hashes -o requirements.txt - - # Ideally, we should export requirements.txt then amend the earlier PR - # update-deps-action could be modified to export PR number as as output - # Or we add the option to export the requirements.txt in that action diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml deleted file mode 100644 index 09a0ffa2..00000000 --- a/.github/workflows/documentation.yaml +++ /dev/null @@ -1,60 +0,0 @@ ---- -name: "📘 Documentation build/publish" - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - pull_request: - types: [closed] - branches: - - "*" - - "!update-devops-tooling" - -jobs: - build_and_deploy: - # Only run when pull request is merged - if: github.event.pull_request.merged == true - name: "Rebuild documentation" - runs-on: ubuntu-latest - continue-on-error: true - - permissions: - # IMPORTANT: mandatory for documentation updates; used in final step - id-token: write - pull-requests: write - contents: write - repository-projects: write - - steps: - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: "Set up Python" - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - - - name: "Install dependencies" - run: | - python -m pip install --upgrade pip - pdm lock - pdm export -o requirements.txt - if [ -f docs/requirements.txt ]; then - pip install -r docs/requirements.txt; fi - - - name: "Build documentation: (tox/sphinx)" - run: | - pip install --upgrade tox - tox -e docs - - - name: "Publish documentation" - if: success() - uses: peaceiris/actions-gh-pages@v4 - with: - publish_branch: gh-pages - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs/_build/html/ - keep_files: true diff --git a/.github/workflows/new-release.yaml b/.github/workflows/new-release.yaml deleted file mode 100644 index c979e91f..00000000 --- a/.github/workflows/new-release.yaml +++ /dev/null @@ -1,223 +0,0 @@ ---- -name: "📦 Release and Publish" - -# GitHub/PyPI trusted publisher documentation: -# https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - #pull_request: - # branches: [ main, master ] - # types: [closed] - #push: - # branches: [ main, master ] - # tags: - # - 'v*.*.*' - -env: - python-version: "3.10" - package-path: "dist" - -### BUILD ### - -jobs: - - build: - name: "🐍 Build Project" - if: - github.event.pull_request.merged == true || - github.event_name == 'push' || - github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - permissions: - contents: write - # id-token: write - outputs: - publish: ${{ steps.build.outputs.publish }} - - steps: - ### BUILDING ### - - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: "Setup Python" - uses: actions/setup-python@v5 - with: - python-version: ${{ env.python-version }} - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - with: - python-version: ${{ env.python-version }} - - - name: "Fetch current semantic tag" - id: fetch-tags - # yamllint disable-line rule:line-length - uses: os-climate/devops-reusable-workflows/.github/actions/latest-semantic-tag@main - - - name: "🏷️ Create initial tag" - id: set-initial-tag - if: steps.fetch-tags.outputs.missing == 'true' - # https://github.com/softprops/action-gh-release - uses: softprops/action-gh-release@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: true - tag_name: v0.0.1 - - - name: "Build with PDM backend" - id: build - # if: steps.fetch-tags.outputs.missing == 'false' - run: | - echo "Current semantic tag: ${{ steps.fetch-tags.outputs.tag }}" - echo "Github versioning: ${{ github.ref_name }}" - pdm build - if ! (ls ${{ env.package-path }}/*.dev*.*); then - echo "publish=true" >> "$GITHUB_OUTPUT" - fi - - ### SIGNING ### - - - name: "Sign packages with Sigstore" - uses: sigstore/gh-action-sigstore-python@v3.0.0 - env: - package-path: ${{ env.package-path }} - with: - inputs: >- - ./${{ env.package-path }}/*.tar.gz - ./${{ env.package-path }}/*.whl - - - name: Store the distribution packages - uses: actions/upload-artifact@v4 - with: - name: ${{ github.ref_name }} - path: ${{ env.package-path }} - - ### PUBLISH GITHUB ### - - github: - name: "📦 Publish to GitHub" - # Only publish on tag pushes - needs: build - runs-on: ubuntu-latest - permissions: - # IMPORTANT: mandatory to publish artefacts - contents: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: ${{ github.ref_name }} - path: ${{ env.package-path }} - - - name: "🌥️ Set environment variables" - id: setenv - run: | - # vernum="${{ env.python-version }}.$(date +'%Y%m%d%H%M')" - datetime="$(date +'%Y%m%d%H%M')" - echo "datetime=${datetime}" >> "$GITHUB_OUTPUT" - - - name: "📦 Publish DEVELOPMENT artefacts to GitHub" - if: startsWith(github.ref, 'refs/tags/') != true - # https://github.com/softprops/action-gh-release - uses: softprops/action-gh-release@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: true - tag_name: ${{ github.ref_name }}-dev - name: "Test/Development Build: ${{ github.ref_name }}" - # body_path: ${{ github.workspace }}/CHANGELOG.rst - files: | - ${{ env.package-path }}/*.tar.gz - ${{ env.package-path }}/*.whl - ${{ env.package-path }}/*.sigstore* - - - name: "📦 Publish PRODUCTION artefacts to GitHub" - if: startsWith(github.ref, 'refs/tags/') - # https://github.com/softprops/action-gh-release - uses: softprops/action-gh-release@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: false - tag_name: ${{ github.ref_name }} - name: "Test/Development Build: ${{ github.ref_name }}" - # body_path: ${{ github.workspace }}/CHANGELOG.rst - files: | - ${{ env.package-path }}/*.tar.gz - ${{ env.package-path }}/*.whl - ${{ env.package-path }}/*.sigstore* - - ### PUBLISH PYPI TEST ### - - testpypi: - name: "📦 Test PyPI publishing" - # Only publish on tag pushes - # if: startsWith(github.ref, 'refs/tags/') - needs: build - runs-on: ubuntu-latest - environment: - name: testpypi - permissions: - # IMPORTANT: mandatory for trusted publishing - id-token: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: ${{ github.ref_name }} - path: ${{ env.package-path }} - - - name: "Validate build artefacts" - id: files - run: | - if [ -f ${{ env.package-path }}/buildvars.txt ]; then - rm ${{ env.package-path }}/buildvars.txt - fi - if (ls ${{ env.package-path }}/*.sigstore*); then - rm ${{ env.package-path }}/*.sigstore* - fi - - - name: "Publish to test PyPI" - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ - verbose: true - packages-dir: ${{ env.package-path }} - - - ### PUBLISH PYPI ### - - pypi: - name: "📦 Publish to PyPI" - # Only publish on tag pushes - if: - startsWith(github.ref, 'refs/tags/') && - needs.build.outputs.publish == 'true' - # contains(github.event.head_commit.message, '[release]') - needs: [ build, testpypi ] - runs-on: ubuntu-latest - environment: - name: pypi - permissions: - # IMPORTANT: mandatory for trusted publishing - id-token: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: ${{ github.ref_name }} - path: ${{ env.package-path }} - - - name: "Remove files unsupported by PyPi" - run: | - if (ls ${{ env.package-path }}/*.sigstore*); then - rm ${{ env.package-path }}/*.sigstore* - fi - -# - name: "📦 Publish to PyPI" -# uses: pypa/gh-action-pypi-publish@release/v1 -# with: -# verbose: true -# packages-dir: ${{ env.package-path }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index ab5c6d5c..00000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,184 +0,0 @@ ---- -name: "🐍📦 Old Production build and release" - -# GitHub/PyPI trusted publisher documentation: -# https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ - -# yamllint disable-line rule:truthy -on: - push: - # Only invoked on release tag pushes - branches: - - 'main' - - 'master' - tags: - - 'v*.*.*' - -env: - python-version: "3.10" - -### BUILD ### - -jobs: - - build: - name: "🐍 Build packages" - # Only publish on tag pushes - # if: startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - permissions: - contents: write - id-token: write - - steps: - ### BUILDING ### - - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: "Setup Python" - uses: actions/setup-python@v5 - with: - python-version: ${{ env.python-version }} - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - - - name: "Fetch current semantic tag" - id: fetch-tags - # yamllint disable-line rule:line-length - uses: os-climate/devops-reusable-workflows/.github/actions/latest-semantic-tag@main - - - name: "Update version from tags for production release" - run: | - echo "Github tag/versioning: ${{ github.ref_name }}" - if (grep 'dynamic = \[\"version\"\]' pyproject.toml > /dev/null); then - echo "Proceeding build with dynamic versioning" - else - echo "Using legacy script to bump release version" - scripts/release-versioning.sh - fi - - - name: "Build with PDM backend" - run: | - pdm build - - ### SIGNING ### - - - name: "Sign packages with Sigstore" - # Use new action - uses: sigstore/gh-action-sigstore-python@v3.0.0 - with: - inputs: >- - ./dist/*.tar.gz - ./dist/*.whl - - - name: Store the distribution packages - uses: actions/upload-artifact@v4 - with: - name: ${{ github.ref_name }} - path: dist/ - - ### PUBLISH GITHUB ### - - github: - name: "📦 Publish to GitHub" - # Only publish on tag pushes - # if: startsWith(github.ref, 'refs/tags/') - needs: - - build - runs-on: ubuntu-latest - permissions: - # IMPORTANT: mandatory to publish artefacts - contents: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: ${{ github.ref_name }} - path: dist/ - - - name: "📦 Publish artefacts to GitHub" - # https://github.com/softprops/action-gh-release - uses: softprops/action-gh-release@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: false - tag_name: ${{ github.ref_name }} - name: "Test/Development Build \ - ${{ github.ref_name }}" - # body_path: ${{ github.workspace }}/CHANGELOG.rst - files: | - dist/*.tar.gz - dist/*.whl - dist/*.sigstore* - - ### PUBLISH PYPI TEST ### - - testpypi: - name: "📦 Publish to PyPi Test" - # Only publish on tag pushes - # if: startsWith(github.ref, 'refs/tags/') - needs: - - build - runs-on: ubuntu-latest - environment: - name: testpypi - permissions: - # IMPORTANT: mandatory for trusted publishing - id-token: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: ${{ github.ref_name }} - path: dist/ - - - name: "Remove files unsupported by PyPi" - run: | - if [ -f dist/buildvars.txt ]; then - rm dist/buildvars.txt - fi - rm dist/*.sigstore* - - - name: Publish distribution to Test PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ - verbose: true - - ### PUBLISH PYPI ### - - pypi: - name: "📦 Publish to PyPi" - # Only publish on tag pushes - # if: startsWith(github.ref, 'refs/tags/') - needs: - - testpypi - runs-on: ubuntu-latest - environment: - name: pypi - permissions: - # IMPORTANT: mandatory for trusted publishing - id-token: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: ${{ github.ref_name }} - path: dist/ - - - name: "Remove files unsupported by PyPi" - run: | - if [ -f dist/buildvars.txt ]; then - rm dist/buildvars.txt - fi - rm dist/*.sigstore* - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - - - name: "Publish release to PyPI" - uses: pypa/gh-action-pypi-publish@release/v1 - with: - verbose: true diff --git a/.github/workflows/repository.yaml b/.github/workflows/repository.yaml new file mode 100644 index 00000000..8027a755 --- /dev/null +++ b/.github/workflows/repository.yaml @@ -0,0 +1,421 @@ +--- +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: 2024 The Linux Foundation + +name: "🤖 DevOps Automation" + +# yamllint disable-line rule:truthy +on: + workflow_dispatch: + push: + paths: + - "**" + - "!.github/**" + +env: + DEFAULT_PYTHON: "3.10" + BUILD_ARTEFACTS: "dist" + # Configures publishing to PyPI + PYPI_PUBLISHING: "true" + # Create GitHub releases for all builds, not just production builds + GITHUB_RELEASE_ALWAYS: "true" + # Create an initial tag, if missing + CREATE_MISSING_TAG: "true" + +jobs: + one-password-verify: + name: "Verify 1Password Access" + uses: os-climate/osc-github-devops/.github/workflows/one-password-credentials.yaml@main + # Do NOT run until change is merged; secrets will NOT be available and workflow WILL fail + if: github.event_name != 'pull_request' + with: + VAULT_ITEM: "op://67hdehutbpddhfbgm6ffjvdsbu/Test Secure Note/notesPlain" + EXPORT: false + secrets: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.ONE_PASSWORD_DEVELOPMENT }} + + classify-content: + name: "Inspect Repository" + runs-on: ubuntu-latest + outputs: + python: ${{ steps.classify.outputs.python }} + notebooks: ${{ steps.classify.outputs.notebooks }} + + steps: + - uses: actions/checkout@v4 + + - name: "Classify content" + id: classify + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/repository-content-classify@main + + github-workflow-metadata: + name: "Gather Workflow Metadata" + runs-on: ubuntu-latest + outputs: + owner: ${{ steps.set.outputs.owner }} + repository: ${{ steps.set.outputs.repository }} + tagged: ${{ steps.set.outputs.tagged }} + + steps: + - name: "Capture workflow metadata" + id: set + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/github-workflow-metadata@main + + tagging: + name: "Retrieve/Verify GIT Tags" + runs-on: ubuntu-latest + outputs: + current: ${{ steps.repository-tags-fetch.outputs.tag }} + invalid: ${{ steps.repository-tags-fetch.outputs.invalid }} + + steps: + - name: "Retrieve/verify tags" + id: repository-tags-fetch + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/repository-tags-fetch@main + + # Only runs if no tags are found, pushes an initial v0.0.0 + - name: "Create initial v0.0.0 tag [conditional]" + id: set-initial-tag + if: steps.repository-tags-fetch.outputs.invalid == 'true' && env.CREATE_MISSING_TAG + uses: softprops/action-gh-release@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + prerelease: true + tag_name: v0.0.0 + + verify-github-environment: + name: "Verify GitHub Environment" + runs-on: ubuntu-latest + outputs: + present: ${{ steps.labelling.outputs.present }} + created: ${{ steps.labelling.outputs.created }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - name: "Verify/create release labels" + id: labelling + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/github-mandatory-labels@main + + - name: "Check for required secrets/API keys/tokens" + uses: os-climate/osc-github-devops/.github/actions/github-mandatory-secrets@main + with: + # Mandatory secrets/variables to check + pypi_development: ${{ secrets.PYPI_DEVELOPMENT }} + pypi_production: ${{ secrets.PYPI_PRODUCTION }} + + python-project: + name: "Python Content" + needs: + - classify-content + if: needs.classify-content.outputs.python == 'true' + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.python.outputs.matrixjson }} + + steps: + - uses: actions/checkout@v4 + + - name: "Extract Python versioning" + id: python + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/python-versions-matrix@main + + python-build: + name: "Build Project" + uses: os-climate/osc-github-devops/.github/workflows/python-build-matrix.yaml@main + needs: + - github-workflow-metadata + - python-project + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.python-project.outputs.matrix) }} + permissions: + contents: write + # Required by SigStore signing action + id-token: write + with: + python-version: ${{ matrix.python-version }} + + github-release: + name: "Create GitHub Release" + uses: os-climate/osc-github-devops/.github/workflows/github-release.yaml@main + + testpypi: + name: "Test Package Publishing" + needs: + - tagging + - github-workflow-metadata + - python-build + # Only test publishing on merge of pull requests or tag pushes + if: github.event.pull_request.merged == true || startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + environment: + name: development + permissions: + # IMPORTANT: mandatory for trusted publishing + id-token: write + steps: + - name: "Download build artefacts" + uses: actions/download-artifact@v4 + if: env.PYPI_PUBLISHING == 'true' + with: + name: ${{ github.ref_name }} + path: ${{ env.BUILD_ARTEFACTS }} + + - name: "Manicure artefacts directory" + id: files + run: | + # Remove file types unsupported by the Python Package Index + if [ ! -d ${{ env.BUILD_ARTEFACTS }} ]; then + echo "Early exit; build artefacts path NOT found: ${{ env.BUILD_ARTEFACTS }}" + exit 0 + fi + if [ -f ${{ env.BUILD_ARTEFACTS }}/buildvars.txt ]; then + rm ${{ env.BUILD_ARTEFACTS }}/buildvars.txt + else + echo "No buildvars.txt file to purge" + fi + # Remove outputs related to SigStore signing + if test -n "$(find ${{ env.BUILD_ARTEFACTS }} -maxdepth 1 -name '**.sigstore*' -print -quit)" + then + echo "Found SigStore signing artefacts to purge" + rm ${{ env.BUILD_ARTEFACTS }}/*.sigstore* + else + echo "No SigStore signing artefacts to purge" + fi + + - name: "Check PROJECT in Test PyPI" + id: testpypi-project-url-check + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/url-validity-check@main + with: + prefix: "https://test.pypi.org/project" + # Use project name, e.g. "/ITR" + string: "/${{ needs.github-workflow-metadata.outputs.repository }}" + suffix: "/" + + - name: "Check RELEASE in Test PyPI" + id: testpypi-release-url-check + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/url-validity-check@main + with: + prefix: "https://test.pypi.org/project" + # Use project name, e.g. "/ITR" + string: "/${{ needs.github-workflow-metadata.outputs.repository }}" + suffix: "/${{ needs.tagging.outputs.current }}/" + + - name: "Publish to Test PyPI [Trusted Publishing]" + uses: pypa/gh-action-pypi-publish@release/v1 + # Primary/default method uses trusted publishing + # yamllint disable-line rule:line-length + if: steps.testpypi-project-url-check.outputs.valid == 'true' && steps.testpypi-release-url-check.outputs.valid == 'false' + with: + repository-url: https://test.pypi.org/legacy/ + # Show checksum values + print-hash: true + packages-dir: ${{ env.BUILD_ARTEFACTS }} + # We already validate earlier in the pipeline + verify-metadata: false + # Test releases are always debugged + verbose: true + + - name: "Publish to Test PyPI [Fallback: API Key]" + uses: pypa/gh-action-pypi-publish@release/v1 + # Fallback method uses static organisation credentials + # Used initially when trusted publishing is unavailable + if: steps.testpypi-project-url-check.outputs.valid == 'false' + with: + repository-url: https://test.pypi.org/legacy/ + # Show checksum values + print-hash: true + packages-dir: ${{ env.BUILD_ARTEFACTS }} + # We already validate earlier in the pipeline + verify-metadata: false + # Test releases are always debugged + verbose: true + # Organisation secret/variable + # Defined/stored in 1Password + password: ${{ secrets.PYPI_DEVELOPMENT }} + + pypi: + name: "Publish Package" + if: + # Only publish on tag pushes or when a release is explicitly requested + startsWith(github.ref, 'refs/tags/') || contains(github.event.head_commit.message, '[release]') + # github.ref_name != 'main' && + # github.ref_name != 'master' + needs: [tagging, python-build, github-workflow-metadata, testpypi] + runs-on: ubuntu-latest + environment: + name: production + permissions: + # IMPORTANT: mandatory for trusted publishing + id-token: write + steps: + - name: "Download build artefacts" + uses: actions/download-artifact@v4 + with: + name: ${{ github.ref_name }} + path: ${{ env.BUILD_ARTEFACTS }} + + - name: "Remove unsupported artefacts/files" + run: | + # Remove unsupported artefacts/files + if (ls ${{ env.BUILD_ARTEFACTS }}/*.sigstore*); then + rm ${{ env.BUILD_ARTEFACTS }}/*.sigstore* + fi + + - name: "Check if PROJECT in PyPI" + id: pypi-project-url-check + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/url-validity-check@main + with: + prefix: "https://pypi.org/project" + # Use project name, e.g. "/ITR" + string: "/${{ needs.github-workflow-metadata.outputs.repository }}" + suffix: "/" + + - name: "Check for RELEASE in PyPI" + id: pypi-release-url-check + # yamllint disable-line rule:line-length + uses: os-climate/osc-github-devops/.github/actions/url-validity-check@main + with: + prefix: "https://pypi.org/project" + # Use project name, e.g. "/ITR" + string: "/${{ needs.github-workflow-metadata.outputs.repository }}" + suffix: "/${{ needs.tagging.outputs.current }}/" + + - name: "Publish to PyPI [Trusted Publishing]" + uses: pypa/gh-action-pypi-publish@release/v1 + # Primary/default method uses trusted publishing + # yamllint disable-line rule:line-length + if: steps.pypi-project-url-check.outputs.valid == 'true' && steps.pypi-release-url-check.outputs.valid == 'false' + with: + # Show checksum values + print-hash: true + packages-dir: ${{ env.BUILD_ARTEFACTS }} + # We already validate earlier in the pipeline + verify-metadata: false + + - name: "Publish to PyPI [Fallback: API Key]" + uses: pypa/gh-action-pypi-publish@release/v1 + # Fallback method uses static organisation credentials + # Used initially when trusted publishing is unavailable + if: steps.pypi-project-url-check.outputs.valid == 'false' + with: + # Show checksum values + print-hash: true + packages-dir: ${{ env.BUILD_ARTEFACTS }} + # We already validate earlier in the pipeline + verify-metadata: false + # Organisation secret/variable + # Defined/stored in 1Password + password: ${{ secrets.PYPI_PRODUCTION }} + + notebooks: + name: "Jupyter Notebooks" + needs: + - classify-content + - python-project + runs-on: "ubuntu-latest" + continue-on-error: false + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.python-project.outputs.matrix) }} + # Don't run when pull request is merged, only if Jupyter Notebooks are present + if: needs.classify-content.outputs.notebooks == 'true' + + steps: + - uses: actions/checkout@v4 + + - name: "Setup Python" + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: "Set up Python ${{ matrix.python-version }}" + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: "Install PDM tooling" + uses: pdm-project/setup-pdm@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: "Install package dependencies" + run: | + # Install build dependencies + python -m pip install -q --upgrade pip + pdm export -o requirements.txt + if [ -f requirements.txt ]; then + pip install -r requirements.txt + fi + pip install -q . + pip install -q pytest nbmake + + - name: "Testing Jupyter notebooks" + run: | + # Testing Jupyter notebooks + echo "Installing required dependencies" + pip install --upgrade -q pytest nbmake + # Consider enabling the line below when debugging/testing + # find . -name '*.ipynb' + echo "Running command: pytest --nbmake -- **/*.ipynb" + pytest --nbmake src/*/*.ipynb --cov=src/devops_reusable_workflows + # Might need an __init__.py file in tests folder for notebooks there to be tested? + # https://stackoverflow.com/questions/47287721/coverage-py-warning-no-data-was-collected-no-data-collected + # pytest --nbmake tests/test_*.ipynb --cov=tests + + # TEMP DISABLED - NEED TO CHECK - WHERE ARE THESE LOGS GENERATED??? + # - name: "Upload Logs" + # if: always() + # uses: actions/upload-artifact@v4 + # with: + # name: debug-logs + # path: /tmp/*.log + # retention-days: 14 + + security: + name: "Security Audit" + needs: + - classify-content + - python-project + if: needs.classify-content.outputs.python == 'true' + runs-on: "ubuntu-latest" + continue-on-error: true + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.python-project.outputs.matrix) }} + steps: + - name: "Checkout repository" + uses: actions/checkout@v4 + + - name: "Set up Python ${{ matrix.python-version }}" + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: "Install PDM tooling" + uses: pdm-project/setup-pdm@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: "Install dependencies" + run: | + pip install -q --upgrade pip + pdm lock + pdm export -o requirements.txt + python -m pip install -q -r requirements.txt + python -m pip install -q . + pip install --upgrade -q setuptools + pdm list --graph + + - name: "Perform package auditing" + uses: pypa/gh-action-pip-audit@v1.1.0 diff --git a/.github/workflows/security.yaml b/.github/workflows/security.yaml deleted file mode 100644 index 92e54b29..00000000 --- a/.github/workflows/security.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- -# This workflow will install Python dependencies -# run tests and lint with a variety of Python versions -# For more information see: -# https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - -name: "⛔️ Security auditing (Matrix)" - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - pull_request: - types: [opened, reopened, edited, synchronize] - branches: - - "*" - - "!update-devops-tooling" - -jobs: - get-python-versions: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.parse-project-metadata.outputs.python-matrix-versions }} - - steps: - - uses: actions/checkout@v4 - - - name: "Populate environment variables" - id: parse-project-metadata - # yamllint disable-line rule:line-length - uses: os-climate/devops-reusable-workflows/.github/actions/python-versions-matrix@main - - builds: - name: "Python builds" - needs: [get-python-versions] - runs-on: "ubuntu-latest" - continue-on-error: true - # Don't run when pull request is merged - if: github.event.pull_request.merged == false - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.get-python-versions.outputs.matrix) }} - - steps: - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: "Install dependencies" - run: | - pip install --upgrade pip - pdm lock - pdm export -o requirements.txt - python -m pip install -r requirements.txt - python -m pip install . - pip install --upgrade setuptools - pdm list --graph - - - name: "Run: pip-audit" - uses: pypa/gh-action-pip-audit@v1.1.0 diff --git a/.github/workflows/test-release.yaml b/.github/workflows/test-release.yaml deleted file mode 100644 index bafecb48..00000000 --- a/.github/workflows/test-release.yaml +++ /dev/null @@ -1,152 +0,0 @@ ---- -name: "🐍📦 Test build and release" - -# GitHub/PyPI trusted publisher documentation: -# https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - -env: - python-version: "3.10" - -### BUILD ### - -jobs: - build: - name: "🐍 Build packages" - runs-on: ubuntu-latest - permissions: - # IMPORTANT: mandatory for Sigstore - id-token: write - steps: - ### BUILDING ### - - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: "Setup Python 3.10" - uses: actions/setup-python@v5 - with: - python-version: ${{ env.python-version }} - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - with: - python-version: ${{ env.python-version }} - - - name: "Populate environment variables" - id: setenv - run: | - vernum="${{ env.python-version }}.$(date +'%Y%m%d%H%M')" - echo "vernum=${vernum}" >> "$GITHUB_OUTPUT" - echo "vernum=${vernum}" >> buildvars.txt - - - name: "Tag for test release" - # Delete all local tags, then create a synthetic tag for testing - # Use the date/time to avoid conflicts uploading to Test PyPI - run: | - scripts/dev-versioning.sh "${{ steps.setenv.outputs.vernum }}" - git tag | xargs -L 1 | xargs git tag --delete - git tag "v${{ steps.setenv.outputs.vernum }}" - git checkout "tags/v${{ steps.setenv.outputs.vernum }}" - grep version pyproject.toml - - - name: "Build with PDM backend" - run: | - pdm build - # Need to save the build environment for subsequent steps - mv buildvars.txt dist/buildvars.txt - - ### SIGNING ### - - - name: "Sign packages with Sigstore" - uses: sigstore/gh-action-sigstore-python@v2 - - with: - inputs: >- - ./dist/*.tar.gz - ./dist/*.whl - - - name: Store the distribution packages - uses: actions/upload-artifact@v4 - with: - name: Development - path: dist/ - - ### PUBLISH GITHUB ### - - github: - name: "📦 Test publish to GitHub" - needs: - - build - runs-on: ubuntu-latest - permissions: - # IMPORTANT: mandatory to publish artefacts - contents: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: Development - path: dist/ - - - name: "Source environment variables" - id: setenv - run: | - if [ -f dist/buildvars.txt ]; then - source dist/buildvars.txt - echo "vernum=${vernum}" >> "$GITHUB_OUTPUT" - else - echo "Build environment variables could not be sourced" - fi - echo "tarball=$(ls dist/*.tgz)" >> "$GITHUB_OUTPUT" - echo "wheel=$(ls dist/*.whl)" >> "$GITHUB_OUTPUT" - - - name: "📦 Publish artefacts to GitHub" - # https://github.com/softprops/action-gh-release - uses: softprops/action-gh-release@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - prerelease: true - tag_name: ${{ steps.setenv.outputs.vernum }} - name: "Test/Development Build \ - ${{ steps.setenv.outputs.vernum }}" - # body_path: ${{ github.workspace }}/CHANGELOG.rst - files: | - dist/*.tar.gz - dist/*.whl - dist/*.sigstore - - ### PUBLISH TEST PYPI ### - - testpypi: - name: "📦 Test publish to PyPi" - needs: - - build - runs-on: ubuntu-latest - environment: - name: testpypi - permissions: - # IMPORTANT: mandatory for trusted publishing - id-token: write - steps: - - name: "⬇ Download build artefacts" - uses: actions/download-artifact@v4 - with: - name: Development - path: dist/ - - - name: "Remove files unsupported by PyPi" - run: | - if [ -f dist/buildvars.txt ]; then - rm dist/buildvars.txt - fi - rm dist/*.sigstore - - - name: Publish distribution to Test PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - verbose: true - repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml deleted file mode 100644 index 125e06e9..00000000 --- a/.github/workflows/testing.yaml +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: "🧪 Unit tests (Matrix)" - -# yamllint disable-line rule:truthy -on: - workflow_dispatch: - pull_request: - types: [opened, reopened, edited, synchronize] - branches: - - "*" - - "!update-devops-tooling" - -jobs: - get-python-versions: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.parse-project-metadata.outputs.python-matrix-versions }} - - steps: - - uses: actions/checkout@v4 - - - name: "Populate environment variables" - id: parse-project-metadata - # yamllint disable-line rule:line-length - uses: os-climate/devops-reusable-workflows/.github/actions/python-versions-matrix@main - - testing: - name: "Run unit tests" - needs: [get-python-versions] - runs-on: ubuntu-latest - # Don't run when pull request is merged - if: github.event.pull_request.merged == false - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.get-python-versions.outputs.matrix) }} - - steps: - - name: "Checkout repository" - uses: actions/checkout@v4 - - - name: "Setup Python ${{ matrix.python-version }}" - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: "Setup PDM for build commands" - uses: pdm-project/setup-pdm@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: "Install dependencies" - run: | - python -m pip install --upgrade pip - pdm export -o requirements.txt - pip install -r requirements.txt - pip install --upgrade pytest pytest-cov - pip install . - - - name: "Run unit tests: pytest" - run: | - if [ -d test ]; then - python -m pytest test - elif [ -d tests ]; then - python -m pytest tests - else - echo "No test/tests directory could be found" - echo "Aborting testing without error"; exit 0 - fi