diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index bddcaff..f99e7f4 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -32,14 +32,14 @@ runs: echo "run_script=uv run --frozen" | Tee-Object -Append $env:GITHUB_ENV - name: Setup uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "**/uv.lock" python-version: ${{ inputs.python-version }} - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 with: # Passing a repo token reduces the likelihood of API rate limit exceeded repo-token: ${{ inputs.token }} @@ -102,7 +102,7 @@ runs: echo "PY=$hash" | Tee-Object -Append $env:GITHUB_ENV - name: Cache pre-commit environments - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.cache/pre-commit key: pre-commit|${{ env.PY }}|${{ hashFiles(format('{0}/.pre-commit-config.yaml', inputs.working-directory)) }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 49da940..e80f55a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,7 @@ jobs: - name: Run SBOM generation run: task -v sbom - name: Upload SBOM artifacts - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: sbom-files path: | @@ -89,7 +89,7 @@ jobs: - name: Check license compliance run: task -v license-check - name: Upload license check results - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: license-check-results path: license-check.json @@ -97,7 +97,7 @@ jobs: - name: Run vulnerability scan run: task -v vulnscan - name: Upload vulnerability scan results - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: vuln-scan-results path: vulns.json @@ -111,11 +111,14 @@ jobs: # out the repo on Windows. Instead, cookiecutter fetches the template # directly from the remote branch. - name: Setup uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v7 with: python-version: ${{ env.python_version }} + # No checkout in this job (NTFS-illegal chars in template dir), so disable cache + enable-cache: false + ignore-empty-workdir: true - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Generate project from template @@ -138,11 +141,13 @@ jobs: curl -fsSL "$scriptUrl" -o "$tmpdir/extract_template_zip.py" repoDir=$(python3 "$tmpdir/extract_template_zip.py" "$tmpdir/template.zip" "$tmpdir/src") - uvx --with gitpython cookiecutter "$repoDir" --no-input --output-dir "$RUNNER_TEMP" + uvx --with gitpython cookiecutter "$repoDir" --no-input \ + project_name="ci-test-project" \ + --output-dir "$RUNNER_TEMP" - name: Verify generated project shell: pwsh run: | - $project = Join-Path $env:RUNNER_TEMP "replace-me" + $project = Join-Path $env:RUNNER_TEMP "ci-test-project" # Verify the project directory was created if (-not (Test-Path $project)) { @@ -219,25 +224,25 @@ jobs: - name: Initialize generated project shell: bash run: | - cd "$RUNNER_TEMP/replace-me" + cd "$RUNNER_TEMP/ci-test-project" task -v init - name: Run unit tests shell: bash # Integration tests require Docker (Linux images) which is not # available on Windows runners; those are covered by the Linux CI job. run: | - cd "$RUNNER_TEMP/replace-me" + cd "$RUNNER_TEMP/ci-test-project" task -v unit-test - name: Build Docker image shell: bash run: | - cd "$RUNNER_TEMP/replace-me" + cd "$RUNNER_TEMP/ci-test-project" task -v build - name: Verify Docker image shell: bash run: | - docker run --rm zenable-io/replace-me:latest --version - docker run --rm zenable-io/replace-me:latest --help + docker run --rm zenable-io/ci-test-project:latest --version + docker run --rm zenable-io/ci-test-project:latest --help - name: Verify zenable CLI shell: bash run: | diff --git a/Taskfile.yml b/Taskfile.yml index 132d026..6b4f016 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -49,7 +49,7 @@ tasks: # This fixes an "ERROR: Multiple platforms feature is currently not supported for docker driver" pipeline error # Only create our multiplatform builder if it doesn't already exist; otherwise list information about the one that exists # It suppresses the inspect output when it's not running in a GitHub Action - - docker buildx inspect multiplatform {{if ne .GITHUB_ACTIONS "true"}}>/dev/null{{end}} || docker buildx create --name multiplatform --driver docker-container --use + - docker buildx inspect multiplatform {{if ne .GITHUB_ACTIONS "true"}}>/dev/null{{end}} 2>/dev/null || docker buildx create --name multiplatform --driver docker-container --use init: desc: Initialize the repo for local use; intended to be run after git clone @@ -152,6 +152,8 @@ tasks: -w /src \ anchore/syft:latest \ /src \ + --source-name={{.PROJECT_SLUG}} \ + --source-version={{.VERSION}} \ -o syft-json=sbom.syft.json \ -o spdx-json=sbom.spdx.json \ -o cyclonedx-json=sbom.cyclonedx.json diff --git a/pyproject.toml b/pyproject.toml index 695297b..6521262 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [project] name = "ai-native-python" +# Automatically updated by the release task via python-semantic-release (see Taskfile.yml and [tool.semantic_release] below) version = "0.4.1" description = "The AI-Native python paved road generator" authors = [ @@ -50,8 +51,12 @@ ignore = [ plugins = [] [tool.pytest.ini_options] -addopts = "--cov=ai_native_python --cov-append --no-cov-on-fail --cov-fail-under=0 --cov-report=html --cov-report=xml --cov-report=term-missing" +addopts = "--cov=ai_native_python --cov=hooks --cov-append --no-cov-on-fail --cov-fail-under=0 --cov-report=html --cov-report=xml --cov-report=term-missing" pythonpath = ['.'] +filterwarnings = [ + # Tests invoke hooks via cookiecutter subprocess, so coverage can't track them directly + "ignore:No data was collected:coverage.exceptions.CoverageWarning", +] markers = [ "unit: marks tests as unit tests (deselect with '-m \"not unit\"')", "integration: marks tests as integration tests (deselect with '-m \"not integration\"')", diff --git a/scripts/scan_workflow_logs.sh b/scripts/scan_workflow_logs.sh index fab9bb5..f83c846 100755 --- a/scripts/scan_workflow_logs.sh +++ b/scripts/scan_workflow_logs.sh @@ -82,6 +82,11 @@ find . -type f -name "*.txt" | while IFS= read -r logfile; do # Sanitize content to prevent command injection and log poisoning sanitized_content=$(echo "$content" | tr -d '\n\r' | cut -c1-200) + # Skip JSON data lines (e.g. SBOM/license check output with package names like "deprecated") + if echo "$content" | grep -qE '"(id|name)":\s*"'; then + continue + fi + # Determine the type of issue and output both annotation and count if echo "$content" | grep -qiE '\berror\b'; then echo "::error file=$job_name,line=$line_num::$sanitized_content" diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" index e8494d7..d6eaa59 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" +++ "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/actions/bootstrap/action.yml" @@ -24,14 +24,14 @@ runs: echo "run_script=${run_script}" | tee -a "${GITHUB_ENV}" - name: Setup uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v7 with: enable-cache: true cache-dependency-glob: "**/uv.lock" python-version: ${{ "{{ inputs.python-version }}" }} - name: Install Task - uses: go-task/setup-task@v1 + uses: go-task/setup-task@v2 with: # Passing a repo token reduces the likelihood of API rate limit exceeded repo-token: ${{ "{{ inputs.token }}" }} @@ -74,7 +74,7 @@ runs: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" | tee -a "${GITHUB_ENV}" - name: Cache pre-commit environments - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.cache/pre-commit key: pre-commit|${{ "{{ env.PY }}" }}|${{ "{{ hashFiles(format('{0}/.pre-commit-config.yaml', inputs.working-directory)) }}" }} diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" index f4d6e90..41590b2 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" +++ "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/ci.yml" @@ -101,7 +101,7 @@ jobs: - name: Set env var for unique artifact uploads run: echo SANITIZED_PLATFORM="$(echo "${{ "{{ matrix.platform }}" }}" | sed 's/\//_/g')" | tee -a "${GITHUB_ENV}" - name: Upload SBOM artifacts - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: sbom-${{ "{{ env.SANITIZED_PLATFORM }}" }} path: | @@ -112,7 +112,7 @@ jobs: env: PLATFORM: ${{ "{{ matrix.platform }}" }} - name: Upload license check results - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: license-check-${{ "{{ env.SANITIZED_PLATFORM }}" }} path: license-check.*.json @@ -122,7 +122,7 @@ jobs: env: PLATFORM: ${{ "{{ matrix.platform }}" }} - name: Upload vulnerability scan results - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: vulns-${{ "{{ env.SANITIZED_PLATFORM }}" }} path: vulns.*.json diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" index e37c78f..cc49f45 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" +++ "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/.github/workflows/commit.yml" @@ -76,7 +76,7 @@ jobs: - name: Set env var for unique artifact uploads run: echo SANITIZED_PLATFORM="$(echo "${{ "{{ matrix.platform }}" }}" | sed 's/\//_/g')" | tee -a "${GITHUB_ENV}" - name: Upload SBOM artifacts - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: sbom-${{ "{{ env.SANITIZED_PLATFORM }}" }} path: | @@ -87,7 +87,7 @@ jobs: env: PLATFORM: ${{ "{{ matrix.platform }}" }} - name: Upload license check results - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: license-check-${{ "{{ env.SANITIZED_PLATFORM }}" }} path: license-check.*.json @@ -97,7 +97,7 @@ jobs: env: PLATFORM: ${{ "{{ matrix.platform }}" }} - name: Upload vulnerability scan results - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: vulns-${{ "{{ env.SANITIZED_PLATFORM }}" }} path: vulns.*.json diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" index 4673cd6..2a9b823 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" +++ "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/Taskfile.yml" @@ -60,7 +60,7 @@ tasks: # This fixes an "ERROR: Multiple platforms feature is currently not supported for docker driver" pipeline error # Only create our multiplatform builder if it doesn't already exist; otherwise list information about the one that exists # It suppresses the inspect output when it's not running in a GitHub Action - - docker buildx inspect multiplatform {{ '{{if ne .GITHUB_ACTIONS "true"}}' }}>/dev/null{{ '{{end}}' }} || docker buildx create --name multiplatform --driver docker-container --use + - docker buildx inspect multiplatform {{ '{{if ne .GITHUB_ACTIONS "true"}}' }}>/dev/null{{ '{{end}}' }} 2>/dev/null || docker buildx create --name multiplatform --driver docker-container --use init: desc: Initialize the repo for local use; intended to be run after git clone diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" index db02899..60e03d6 100644 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" +++ "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/pyproject.toml" @@ -35,6 +35,7 @@ pythonpath = ['src'] markers = [ "unit: marks tests as unit tests (deselect with '-m \"not unit\"')", "integration: marks tests as integration tests (deselect with '-m \"not integration\"')", + "slow: marks tests as slow tests (deselect with '-m \"not slow\"')", ] [tool.uv] diff --git "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" index 8ae9470..f2bbc0c 100755 --- "a/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" +++ "b/{{cookiecutter.project_name|replace(\" \", \"\")}}/src/main.py" @@ -10,10 +10,11 @@ def main(): """Main entry point for the application.""" - parser = argparse.ArgumentParser(description="{{ cookiecutter.project_short_description | replace('"', '\\"') | replace("'", "\\\\'") }}") - parser.add_argument( - "--version", action="version", version=f"%(prog)s {__version__}" + parser = argparse.ArgumentParser( + prog="{{ cookiecutter.project_slug }}", + description="{{ cookiecutter.project_short_description | replace('"', '\\"') | replace("'", "\\\\'") }}", ) + parser.add_argument("--version", action="version", version=__version__) parser.parse_args() log = config.setup_logging()