From 30b034b2f65f54d64886ca17f90f1da6d4077a28 Mon Sep 17 00:00:00 2001 From: Tuomas Haapala Date: Wed, 10 Jun 2026 01:16:21 +0300 Subject: [PATCH 1/6] deps: pin all workflow actions to SHAs Refs: RATY-350 --- .github/workflows/build-python-dists.yml | 2 +- .github/workflows/ci-django-api.yml | 24 ++++++++++++------------ .github/workflows/ci-node.yml | 24 ++++++++++++------------ .github/workflows/ci-npm-publish.yml | 8 ++++---- .github/workflows/ci-pnpm-node.yml | 20 ++++++++++---------- .github/workflows/ci-python-library.yml | 2 +- .github/workflows/ci-uv-django-api.yml | 22 +++++++++++----------- .github/workflows/sync-files.yml | 4 ++-- 8 files changed, 53 insertions(+), 53 deletions(-) diff --git a/.github/workflows/build-python-dists.yml b/.github/workflows/build-python-dists.yml index 66056e3..4e5921a 100644 --- a/.github/workflows/build-python-dists.yml +++ b/.github/workflows/build-python-dists.yml @@ -60,7 +60,7 @@ jobs: - name: Build the distributions run: ${{ inputs.build-command }} - name: Store the distributions - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: ${{ inputs.artifact-name }} path: ${{ inputs.artifact-path }} diff --git a/.github/workflows/ci-django-api.yml b/.github/workflows/ci-django-api.yml index 74781bb..1aefe6c 100644 --- a/.github/workflows/ci-django-api.yml +++ b/.github/workflows/ci-django-api.yml @@ -44,22 +44,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Run commitlint - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 pre-commit: name: Run pre-commit hooks runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Setup Python ${{ inputs.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ inputs.python-version }} - name: Run pre-commit - uses: pre-commit/action@v3.0.1 + uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 with: extra_args: ${{ inputs.working-directory != '.' && format('--files {0}/**', inputs.working-directory) || '' }} @@ -104,7 +104,7 @@ jobs: run: | sudo locale-gen fi_FI.UTF-8 - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 1 - name: Install system packages @@ -116,12 +116,12 @@ jobs: run: | sudo apt-get install gdal-bin - name: Setup Python ${{ inputs.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ inputs.python-version }} cache: pip - name: Cache pip packages - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 env: cache-name: cache-pip-modules with: @@ -150,7 +150,7 @@ jobs: run: | pytest -ra -vvv --cov=. --cov-report=xml - name: Store coverage.xml - uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: ${{ inputs.working-directory && format('{0}/', inputs.working-directory) || ''}}coverage.xml key: cache-${{ github.run_id }}-${{ github.run_attempt }} @@ -164,18 +164,18 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 # Required by SonarQube Cloud for blame information with: fetch-depth: 0 - name: Restore coverage.xml - uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: ${{ inputs.working-directory && format('{0}/', inputs.working-directory) || ''}}coverage.xml key: cache-${{ github.run_id }}-${{ github.run_attempt }} fail-on-cache-miss: true - name: SonarQube Cloud Scan - uses: SonarSource/sonarqube-scan-action@v6 + uses: SonarSource/sonarqube-scan-action@fd88b7d7ccbaefd23d8f36f73b59db7a3d246602 # v6.0.0 with: projectBaseDir: ${{ inputs.working-directory }} env: diff --git a/.github/workflows/ci-node.yml b/.github/workflows/ci-node.yml index fb3efcf..dc68771 100644 --- a/.github/workflows/ci-node.yml +++ b/.github/workflows/ci-node.yml @@ -58,9 +58,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Run commitlint - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 build: name: Lint and build @@ -70,17 +70,17 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Use Node.js ${{ inputs.node-version }} with dependency path if: ${{ inputs.working-directory != '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: 'yarn' cache-dependency-path: ${{ inputs.working-directory }}/package.json - name: Use Node.js ${{ inputs.node-version }} without dependency path if: ${{ inputs.working-directory == '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: 'yarn' @@ -114,7 +114,7 @@ jobs: run: yarn build - name: Upload build if: ${{ inputs.upload_artifacts }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: build-artifacts-${{ inputs.node-version }} path: | @@ -138,17 +138,17 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Use Node.js ${{ inputs.node-version }} with dependency path if: ${{ inputs.working-directory != '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: 'yarn' cache-dependency-path: ${{ inputs.working-directory }}/package.json - name: Use Node.js ${{ inputs.node-version }} without dependency path if: ${{ inputs.working-directory == '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: 'yarn' @@ -179,7 +179,7 @@ jobs: # Resolves the effective directory (app-directory if set, else working-directory), # then prefixes 'coverage' with '/' when running in a subdirectory. - name: Store coverage report - uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + uses: actions/cache/save@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: ${{ (inputs.app-directory == '.' && inputs.working-directory || inputs.app-directory) && format('{0}/', inputs.app-directory == '.' && inputs.working-directory || inputs.app-directory) || ''}}coverage key: cache-${{ github.run_id }}-${{ github.run_attempt }} @@ -193,14 +193,14 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 # Coverage path logic (keep in sync with tests job's cache/save step): # Resolves the effective directory (app-directory if set, else working-directory), # then prefixes 'coverage' with '/' when running in a subdirectory. - name: Restore coverage report - uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 + uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: ${{ (inputs.app-directory == '.' && inputs.working-directory || inputs.app-directory) && format('{0}/', inputs.app-directory == '.' && inputs.working-directory || inputs.app-directory) || ''}}coverage key: cache-${{ github.run_id }}-${{ github.run_attempt }} diff --git a/.github/workflows/ci-npm-publish.yml b/.github/workflows/ci-npm-publish.yml index 5ce2555..d9ee12b 100644 --- a/.github/workflows/ci-npm-publish.yml +++ b/.github/workflows/ci-npm-publish.yml @@ -50,8 +50,8 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} registry-url: ${{ inputs.registry-url }} @@ -59,7 +59,7 @@ jobs: run: npm install -g npm@latest - name: Download build artifacts if: ${{ inputs.use-ci-build }} - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: build-artifacts-${{ inputs.node-version }} path: ./ @@ -84,4 +84,4 @@ jobs: sed -i -E "s/\"version\": \"([^\"]+)\"/\"version\": \"\1-canary-${short_sha}\"/" package.json npm publish --ignore-scripts --access ${{ inputs.access }} --tag canary env: - NPM_CONFIG_PROVENANCE: true \ No newline at end of file + NPM_CONFIG_PROVENANCE: true diff --git a/.github/workflows/ci-pnpm-node.yml b/.github/workflows/ci-pnpm-node.yml index cd091e6..ebdf2e0 100644 --- a/.github/workflows/ci-pnpm-node.yml +++ b/.github/workflows/ci-pnpm-node.yml @@ -58,9 +58,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Run commitlint - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 build: name: Lint and build @@ -70,7 +70,7 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Setup pnpm uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 @@ -79,7 +79,7 @@ jobs: - name: Setup Node.js with dependency path if: ${{ inputs.working-directory != '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: pnpm @@ -87,7 +87,7 @@ jobs: - name: Setup Node.js without dependency path if: ${{ inputs.working-directory == '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: pnpm @@ -116,7 +116,7 @@ jobs: - name: Upload build if: ${{ inputs.upload-artifacts }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: build-artifacts-${{ inputs.node-version }} path: | @@ -142,7 +142,7 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Setup pnpm uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 @@ -151,7 +151,7 @@ jobs: - name: Setup Node.js with dependency path if: ${{ inputs.working-directory != '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: pnpm @@ -159,7 +159,7 @@ jobs: - name: Setup Node.js without dependency path if: ${{ inputs.working-directory == '.' }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} cache: pnpm @@ -197,7 +197,7 @@ jobs: run: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 diff --git a/.github/workflows/ci-python-library.yml b/.github/workflows/ci-python-library.yml index adb8a8c..c0bccc0 100644 --- a/.github/workflows/ci-python-library.yml +++ b/.github/workflows/ci-python-library.yml @@ -35,7 +35,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Run commitlint - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 pre-commit: name: Run pre-commit hooks diff --git a/.github/workflows/ci-uv-django-api.yml b/.github/workflows/ci-uv-django-api.yml index a7303e9..0fe93da 100644 --- a/.github/workflows/ci-uv-django-api.yml +++ b/.github/workflows/ci-uv-django-api.yml @@ -44,22 +44,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Run commitlint - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 pre-commit: name: Run pre-commit hooks runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Setup Python ${{ inputs.python-version }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ inputs.python-version }} - name: Run pre-commit - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 with: extra_args: ${{ inputs.working-directory != '.' && format('--files {0}/**', inputs.working-directory) || '' }} @@ -104,7 +104,7 @@ jobs: run: | sudo locale-gen fi_FI.UTF-8 - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 1 - name: Install system packages @@ -116,7 +116,7 @@ jobs: run: | sudo apt-get install gdal-bin - name: Install uv and set up Python ${{ inputs.python-version }} - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: enable-cache: true python-version: ${{ inputs.python-version }} @@ -139,7 +139,7 @@ jobs: run: | uv run pytest -ra -vvv --cov=. --cov-report=xml - name: Store coverage.xml - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ inputs.working-directory && format('{0}/', inputs.working-directory) || ''}}coverage.xml key: cache-${{ github.run_id }}-${{ github.run_attempt }} @@ -153,18 +153,18 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 # Required by SonarQube Cloud for blame information with: fetch-depth: 0 - name: Restore coverage.xml - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ inputs.working-directory && format('{0}/', inputs.working-directory) || ''}}coverage.xml key: cache-${{ github.run_id }}-${{ github.run_attempt }} fail-on-cache-miss: true - name: SonarQube Cloud Scan - uses: SonarSource/sonarqube-scan-action@7006c4492b2e0ee0f816d36501671557c97f5995 # v8.1.0 + uses: SonarSource/sonarqube-scan-action@7006c4492b2e0ee0f816d36501671557c97f5995 # v8.1.0 with: projectBaseDir: ${{ inputs.working-directory }} env: diff --git a/.github/workflows/sync-files.yml b/.github/workflows/sync-files.yml index e189f05..803b804 100644 --- a/.github/workflows/sync-files.yml +++ b/.github/workflows/sync-files.yml @@ -9,8 +9,8 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repository - uses: actions/checkout@v4 - - uses: actions/create-github-app-token@v2 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 id: app-token with: app-id: ${{ vars.ORGANIZATIONTOKEN_APP_ID }} From 50eb3eada56ccc230cfed240279e550c80fc29b6 Mon Sep 17 00:00:00 2001 From: Tuomas Haapala Date: Tue, 9 Jun 2026 13:45:58 +0300 Subject: [PATCH 2/6] ci: add dependabot config Refs: RATY-350 --- .github/dependabot.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7b891f4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + groups: + github-actions: + patterns: + - "*" + cooldown: + default-days: 7 + commit-message: + prefix: "deps" From 4d8637061c90483b5ca453772eb4df1a9045c8d7 Mon Sep 17 00:00:00 2001 From: Tuomas Haapala Date: Thu, 11 Jun 2026 17:13:46 +0300 Subject: [PATCH 3/6] feat: add new optional inputs to reusable workflows Mostly to make testing the workflows possible. Refs: RATY-350 --- .github/workflows/ci-django-api-README.md | 3 +++ .github/workflows/ci-django-api.yml | 24 +++++++++++++++++-- .github/workflows/ci-node-README.md | 2 ++ .github/workflows/ci-node.yml | 9 ++++++- .github/workflows/ci-pnpm-node-README.md | 2 ++ .github/workflows/ci-pnpm-node.yml | 17 ++++++++++++- .github/workflows/ci-python-library-README.md | 2 ++ .github/workflows/ci-python-library.yml | 14 +++++++++++ .github/workflows/ci-uv-django-api-README.md | 3 +++ .github/workflows/ci-uv-django-api.yml | 24 +++++++++++++++++-- 10 files changed, 94 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-django-api-README.md b/.github/workflows/ci-django-api-README.md index a2abcdb..fd8f0b1 100644 --- a/.github/workflows/ci-django-api-README.md +++ b/.github/workflows/ci-django-api-README.md @@ -31,6 +31,9 @@ To use this reusable workflow, create a project-specific workflow file in your ` - **`use-postgis`** (boolean): Set to `true` to enable the PostGIS extension. Default is `false`. - **`extra-commands`** (string): Additional setup commands or checks to execute before running tests. Can be used to set environment variables: `echo "EXTRA_TEST_ENV_VAR=test" >> $GITHUB_ENV`. - **`working-directory`** (string): Repository subdirectory where to run tests. Default is repository root. +- **`skip-sonar`** (boolean): Set to `true` to skip the SonarQube Cloud scan. Default is `false`. +- **`commitlint-config-file`** (string): Path to the commitlint config file. If not set, commitlint uses its default config discovery. +- **`pre-commit-config-file`** (string): Path to `.pre-commit-config.yaml`. If empty, pre-commit uses its default discovery (file in repository root). ### ๐Ÿ”‘ Secrets diff --git a/.github/workflows/ci-django-api.yml b/.github/workflows/ci-django-api.yml index 1aefe6c..3fb4856 100644 --- a/.github/workflows/ci-django-api.yml +++ b/.github/workflows/ci-django-api.yml @@ -26,10 +26,25 @@ on: required: false type: string default: '.' + skip-sonar: + description: 'Set to true to skip SonarQube checks' + required: false + type: boolean + default: false + commitlint-config-file: + description: 'Path to commitlint config file' + required: false + type: string + default: '' + pre-commit-config-file: + description: 'Path to .pre-commit-config.yaml. If empty, pre-commit uses default discovery.' + required: false + type: string + default: '' secrets: SONAR_TOKEN: description: 'SonarQube Cloud token' - required: true + required: false workflow_dispatch: env: @@ -47,6 +62,8 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + with: + configFile: ${{ inputs.commitlint-config-file }} pre-commit: name: Run pre-commit hooks @@ -61,7 +78,9 @@ jobs: - name: Run pre-commit uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 with: - extra_args: ${{ inputs.working-directory != '.' && format('--files {0}/**', inputs.working-directory) || '' }} + extra_args: >- + ${{ inputs['pre-commit-config-file'] != '' && format('--config {0}', inputs['pre-commit-config-file']) || '' }} + ${{ inputs.working-directory != '.' && format('--files {0}/**', inputs.working-directory) || '' }} tests: name: Run tests @@ -157,6 +176,7 @@ jobs: sonarcloud: name: Run SonarQube Cloud Scan + if: ${{ !inputs.skip-sonar }} runs-on: ubuntu-latest needs: tests defaults: diff --git a/.github/workflows/ci-node-README.md b/.github/workflows/ci-node-README.md index 1b78025..d54ec4a 100644 --- a/.github/workflows/ci-node-README.md +++ b/.github/workflows/ci-node-README.md @@ -40,6 +40,8 @@ To use this reusable workflow, create a project-specific workflow file in your ` - **`typecheck`** (boolean): Run typecheck command. Default is `false`. - **`working-directory`** (string): Repository working directory where to run yarn installation and the tests. Default is `.` (the repository root). - **`app-directory`** (string): The subdirectory of the application where the tests are run. Default is **`working-directory`**. +- **`commitlint-config-file`** (string): Path to the commitlint config file. If not set, commitlint uses its default config discovery. +- **`artifact-name`** (string): Name for the uploaded build artifact. If empty, defaults to `build-artifacts-`. ### ๐Ÿ”‘ Secrets diff --git a/.github/workflows/ci-node.yml b/.github/workflows/ci-node.yml index dc68771..75e581a 100644 --- a/.github/workflows/ci-node.yml +++ b/.github/workflows/ci-node.yml @@ -36,6 +36,11 @@ on: required: false type: boolean default: false + commitlint-config-file: + description: 'Path to commitlint config file' + required: false + type: string + default: '' working-directory: description: 'Working directory for the yarn installation, build and tests jobs.' required: false @@ -49,7 +54,7 @@ on: secrets: SONAR_TOKEN: description: 'SonarQube Cloud token' - required: true + required: false workflow_dispatch: jobs: @@ -61,6 +66,8 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + with: + configFile: ${{ inputs.commitlint-config-file }} build: name: Lint and build diff --git a/.github/workflows/ci-pnpm-node-README.md b/.github/workflows/ci-pnpm-node-README.md index 3501b3a..30b12bb 100644 --- a/.github/workflows/ci-pnpm-node-README.md +++ b/.github/workflows/ci-pnpm-node-README.md @@ -52,6 +52,8 @@ To use this reusable workflow, create a project-specific workflow file in your ` - **`skip-sonar`** (boolean): Set to true to skip SonarQube checks. Default is `false`. - **`skip-build`** (boolean): Set to true to skip the build phase. Default is `false`. - **`ignore-scripts`** (boolean): Set to true to skip lifecycle scripts during install (`--ignore-scripts`). Default is `true`. +- **`pnpm-version`** (string): pnpm version to use (passed to `pnpm/action-setup`). If empty, the version is read from the `packageManager` field in `package.json`. +- **`commitlint-config-file`** (string): Path to the commitlint config file. If not set, commitlint uses its default config discovery. ### ๐Ÿ”‘ Secrets diff --git a/.github/workflows/ci-pnpm-node.yml b/.github/workflows/ci-pnpm-node.yml index ebdf2e0..e078046 100644 --- a/.github/workflows/ci-pnpm-node.yml +++ b/.github/workflows/ci-pnpm-node.yml @@ -46,10 +46,23 @@ on: required: false type: boolean default: true + pnpm-version: + description: > + pnpm version to use (passed to pnpm/action-setup). + If empty, pnpm/action-setup reads the version from the + packageManager field in package.json. + required: false + type: string + default: "" + commitlint-config-file: + description: 'Path to commitlint config file' + required: false + type: string + default: '' secrets: SONAR_TOKEN: description: 'SonarQube Cloud token' - required: true + required: false workflow_dispatch: jobs: @@ -61,6 +74,8 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + with: + configFile: ${{ inputs.commitlint-config-file }} build: name: Lint and build diff --git a/.github/workflows/ci-python-library-README.md b/.github/workflows/ci-python-library-README.md index 9771e38..b5f3048 100644 --- a/.github/workflows/ci-python-library-README.md +++ b/.github/workflows/ci-python-library-README.md @@ -28,6 +28,8 @@ To use this reusable workflow, create a project-specific workflow file in your ` - **`python-version`** (string): Python version to use for pre-commit checks. Defaults to `"3.x"`. - **`enable-sonar`** (boolean): Whether to run the SonarQube Cloud Scan job after tests. Defaults to `true`. +- **`commitlint-config-file`** (string): Path to the commitlint config file. If not set, commitlint uses its default config discovery. +- **`pre-commit-config-file`** (string): Path to `.pre-commit-config.yaml`. If empty, pre-commit uses its default discovery (file in repository root). ### Secrets diff --git a/.github/workflows/ci-python-library.yml b/.github/workflows/ci-python-library.yml index c0bccc0..2e01a60 100644 --- a/.github/workflows/ci-python-library.yml +++ b/.github/workflows/ci-python-library.yml @@ -17,6 +17,16 @@ on: required: false type: boolean default: true + commitlint-config-file: + description: 'Path to commitlint config file. If empty, commitlint uses its default config discovery.' + required: false + type: string + default: '' + pre-commit-config-file: + description: 'Path to .pre-commit-config.yaml. If empty, pre-commit uses default discovery.' + required: false + type: string + default: '' secrets: sonar-token: description: "Token for SonarQube Cloud Scan. Required if enable-sonar is set to true." @@ -36,6 +46,8 @@ jobs: - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + with: + configFile: ${{ inputs.commitlint-config-file }} pre-commit: name: Run pre-commit hooks @@ -51,6 +63,8 @@ jobs: - name: Run pre-commit uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + with: + extra_args: ${{ inputs['pre-commit-config-file'] != '' && format('--config {0}', inputs['pre-commit-config-file']) || '' }} tests: name: Python ${{ matrix.python_version }} diff --git a/.github/workflows/ci-uv-django-api-README.md b/.github/workflows/ci-uv-django-api-README.md index 5b745df..b3abf3e 100644 --- a/.github/workflows/ci-uv-django-api-README.md +++ b/.github/workflows/ci-uv-django-api-README.md @@ -38,6 +38,9 @@ To use this reusable workflow, create a project-specific workflow file in your ` - **`use-postgis`** (boolean): Set to `true` to enable the PostGIS extension. Default is `false`. - **`extra-commands`** (string): Additional setup commands or checks to execute before running tests. Can be used to set environment variables or install extra packages: `echo "EXTRA_TEST_ENV_VAR=test" >> $GITHUB_ENV`. - **`working-directory`** (string): Repository subdirectory where to run tests. Default is repository root. +- **`skip-sonar`** (boolean): Set to `true` to skip the SonarQube Cloud scan. Default is `false`. +- **`commitlint-config-file`** (string): Path to the commitlint config file. If not set, commitlint uses its default config discovery. +- **`pre-commit-config-file`** (string): Path to `.pre-commit-config.yaml`. If empty, pre-commit uses its default discovery (file in repository root). ### ๐Ÿ”‘ Secrets diff --git a/.github/workflows/ci-uv-django-api.yml b/.github/workflows/ci-uv-django-api.yml index 0fe93da..1134524 100644 --- a/.github/workflows/ci-uv-django-api.yml +++ b/.github/workflows/ci-uv-django-api.yml @@ -26,10 +26,25 @@ on: required: false type: string default: '.' + skip-sonar: + description: 'Set to true to skip SonarQube checks' + required: false + type: boolean + default: false + commitlint-config-file: + description: 'Path to commitlint config file' + required: false + type: string + default: '' + pre-commit-config-file: + description: 'Path to .pre-commit-config.yaml. If empty, pre-commit uses default discovery.' + required: false + type: string + default: '' secrets: SONAR_TOKEN: description: 'SonarQube Cloud token' - required: true + required: false workflow_dispatch: env: @@ -47,6 +62,8 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 + with: + configFile: ${{ inputs.commitlint-config-file }} pre-commit: name: Run pre-commit hooks @@ -61,7 +78,9 @@ jobs: - name: Run pre-commit uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 with: - extra_args: ${{ inputs.working-directory != '.' && format('--files {0}/**', inputs.working-directory) || '' }} + extra_args: >- + ${{ inputs['pre-commit-config-file'] != '' && format('--config {0}', inputs['pre-commit-config-file']) || '' }} + ${{ inputs.working-directory != '.' && format('--files {0}/**', inputs.working-directory) || '' }} tests: name: Run tests @@ -146,6 +165,7 @@ jobs: sonarcloud: name: Run SonarQube Cloud Scan + if: ${{ !inputs.skip-sonar }} runs-on: ubuntu-latest needs: tests defaults: From 6e821fcdbf114f5a96404eee23c9764aa2ab838b Mon Sep 17 00:00:00 2001 From: Tuomas Haapala Date: Thu, 11 Jun 2026 17:13:51 +0300 Subject: [PATCH 4/6] test: add tests for reusable workflows Refs: RATY-350 --- .github/dependabot.yml | 22 ++ .../commitlint.config.mjs | 6 + .../django-api-uv/.pre-commit-config.yaml | 1 + .../django-api-uv/config/__init__.py | 1 + .../django-api-uv/config/settings.py | 48 +++ .../django-api-uv/config/urls.py | 10 + .../django-api-uv/config/wsgi.py | 7 + .../django-api-uv/dummy/__init__.py | 0 .../dummy/migrations/0001_initial.py | 15 + .../dummy/migrations/__init__.py | 0 .../django-api-uv/dummy/models.py | 8 + .../django-api-uv/manage.py | 14 + .../django-api-uv/pyproject.toml | 15 + .../django-api-uv/pytest.ini | 2 + .../django-api-uv/tests/test_smoke.py | 8 + .../django-api-uv/uv.lock | 285 ++++++++++++++++++ .../django-api/.pre-commit-config.yaml | 1 + .../django-api/config/__init__.py | 1 + .../django-api/config/settings.py | 48 +++ .../django-api/config/urls.py | 10 + .../django-api/config/wsgi.py | 7 + .../django-api/dummy/__init__.py | 0 .../dummy/migrations/0001_initial.py | 15 + .../django-api/dummy/migrations/__init__.py | 0 .../django-api/dummy/models.py | 8 + .../django-api/manage.py | 14 + .../django-api/pytest.ini | 2 + .../django-api/requirements-dev.txt | 3 + .../django-api/requirements.txt | 2 + .../django-api/tests/test_smoke.py | 8 + .../node-pnpm/package.json | 11 + .../node-pnpm/pnpm-lock.yaml | 8 + .../node-yarn/package.json | 10 + .../node-yarn/yarn.lock | 2 + .github/workflows/ci-django-api.yml | 2 +- .github/workflows/ci-node.yml | 9 +- .github/workflows/ci-pnpm-node.yml | 2 +- .github/workflows/ci-python-library.yml | 4 +- .github/workflows/ci-uv-django-api.yml | 2 +- .../test-reusable-workflows-README.md | 16 + .github/workflows/test-reusable-workflows.yml | 117 +++++++ 41 files changed, 737 insertions(+), 7 deletions(-) create mode 100644 .github/workflow-test-fixtures/commitlint.config.mjs create mode 100644 .github/workflow-test-fixtures/django-api-uv/.pre-commit-config.yaml create mode 100644 .github/workflow-test-fixtures/django-api-uv/config/__init__.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/config/settings.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/config/urls.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/config/wsgi.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/dummy/__init__.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/dummy/migrations/0001_initial.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/dummy/migrations/__init__.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/dummy/models.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/manage.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/pyproject.toml create mode 100644 .github/workflow-test-fixtures/django-api-uv/pytest.ini create mode 100644 .github/workflow-test-fixtures/django-api-uv/tests/test_smoke.py create mode 100644 .github/workflow-test-fixtures/django-api-uv/uv.lock create mode 100644 .github/workflow-test-fixtures/django-api/.pre-commit-config.yaml create mode 100644 .github/workflow-test-fixtures/django-api/config/__init__.py create mode 100644 .github/workflow-test-fixtures/django-api/config/settings.py create mode 100644 .github/workflow-test-fixtures/django-api/config/urls.py create mode 100644 .github/workflow-test-fixtures/django-api/config/wsgi.py create mode 100644 .github/workflow-test-fixtures/django-api/dummy/__init__.py create mode 100644 .github/workflow-test-fixtures/django-api/dummy/migrations/0001_initial.py create mode 100644 .github/workflow-test-fixtures/django-api/dummy/migrations/__init__.py create mode 100644 .github/workflow-test-fixtures/django-api/dummy/models.py create mode 100644 .github/workflow-test-fixtures/django-api/manage.py create mode 100644 .github/workflow-test-fixtures/django-api/pytest.ini create mode 100644 .github/workflow-test-fixtures/django-api/requirements-dev.txt create mode 100644 .github/workflow-test-fixtures/django-api/requirements.txt create mode 100644 .github/workflow-test-fixtures/django-api/tests/test_smoke.py create mode 100644 .github/workflow-test-fixtures/node-pnpm/package.json create mode 100644 .github/workflow-test-fixtures/node-pnpm/pnpm-lock.yaml create mode 100644 .github/workflow-test-fixtures/node-yarn/package.json create mode 100644 .github/workflow-test-fixtures/node-yarn/yarn.lock create mode 100644 .github/workflows/test-reusable-workflows-README.md create mode 100644 .github/workflows/test-reusable-workflows.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7b891f4..e0fb15c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -12,3 +12,25 @@ updates: default-days: 7 commit-message: prefix: "deps" + + - package-ecosystem: "pip" + directory: "/.github/workflow-test-fixtures/django-api" + schedule: + interval: "weekly" + groups: + workflow-test-fixtures: + applies-to: security-updates + patterns: + - "*" + open-pull-requests-limit: 0 + + - package-ecosystem: "uv" + directory: "/.github/workflow-test-fixtures/django-api-uv" + schedule: + interval: "weekly" + groups: + workflow-test-fixtures: + applies-to: security-updates + patterns: + - "*" + open-pull-requests-limit: 0 diff --git a/.github/workflow-test-fixtures/commitlint.config.mjs b/.github/workflow-test-fixtures/commitlint.config.mjs new file mode 100644 index 0000000..d0b87f9 --- /dev/null +++ b/.github/workflow-test-fixtures/commitlint.config.mjs @@ -0,0 +1,6 @@ +// No rules enforced โ€” any commit message is accepted. +// This config exists so that workflows using wagoid/commitlint-github-action +// do not fail with "no config found" when called from this repository. +export default { + rules: {}, +}; diff --git a/.github/workflow-test-fixtures/django-api-uv/.pre-commit-config.yaml b/.github/workflow-test-fixtures/django-api-uv/.pre-commit-config.yaml new file mode 100644 index 0000000..4e6a92c --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/.pre-commit-config.yaml @@ -0,0 +1 @@ +repos: [] diff --git a/.github/workflow-test-fixtures/django-api-uv/config/__init__.py b/.github/workflow-test-fixtures/django-api-uv/config/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/config/__init__.py @@ -0,0 +1 @@ + diff --git a/.github/workflow-test-fixtures/django-api-uv/config/settings.py b/.github/workflow-test-fixtures/django-api-uv/config/settings.py new file mode 100644 index 0000000..656ce31 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/config/settings.py @@ -0,0 +1,48 @@ +import os +from pathlib import Path + +BASE_DIR = Path(__file__).resolve().parent.parent + +SECRET_KEY = os.environ.get("SECRET_KEY", "compat-secret") +DEBUG = True +ALLOWED_HOSTS: list[str] = ["*"] + +INSTALLED_APPS = [ + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "dummy", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", +] + +ROOT_URLCONF = "config.urls" +TEMPLATES: list[dict[str, object]] = [] +WSGI_APPLICATION = "config.wsgi.application" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": os.environ.get("POSTGRES_DB", "test_db"), + "USER": os.environ.get("POSTGRES_USER", "test_user"), + "PASSWORD": os.environ.get("POSTGRES_PASSWORD", "test_password"), + "HOST": os.environ.get("POSTGRES_HOST", "localhost"), + "PORT": os.environ.get("POSTGRES_PORT", "5432"), + } +} + +LANGUAGE_CODE = "en-us" +TIME_ZONE = "UTC" +USE_I18N = True +USE_TZ = True +STATIC_URL = "static/" +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/.github/workflow-test-fixtures/django-api-uv/config/urls.py b/.github/workflow-test-fixtures/django-api-uv/config/urls.py new file mode 100644 index 0000000..1883db9 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/config/urls.py @@ -0,0 +1,10 @@ +from django.http import HttpResponse +from django.urls import path + + +def healthz(_request): + return HttpResponse("ok") + +urlpatterns = [ + path("healthz/", healthz), +] diff --git a/.github/workflow-test-fixtures/django-api-uv/config/wsgi.py b/.github/workflow-test-fixtures/django-api-uv/config/wsgi.py new file mode 100644 index 0000000..8509335 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/config/wsgi.py @@ -0,0 +1,7 @@ +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_wsgi_application() diff --git a/.github/workflow-test-fixtures/django-api-uv/dummy/__init__.py b/.github/workflow-test-fixtures/django-api-uv/dummy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflow-test-fixtures/django-api-uv/dummy/migrations/0001_initial.py b/.github/workflow-test-fixtures/django-api-uv/dummy/migrations/0001_initial.py new file mode 100644 index 0000000..bbe83a9 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/dummy/migrations/0001_initial.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + dependencies: list = [] + operations = [ + migrations.CreateModel( + name="DummyModel", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=100)), + ], + ), + ] diff --git a/.github/workflow-test-fixtures/django-api-uv/dummy/migrations/__init__.py b/.github/workflow-test-fixtures/django-api-uv/dummy/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflow-test-fixtures/django-api-uv/dummy/models.py b/.github/workflow-test-fixtures/django-api-uv/dummy/models.py new file mode 100644 index 0000000..5fc8b6f --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/dummy/models.py @@ -0,0 +1,8 @@ +from django.db import models + + +class DummyModel(models.Model): + name = models.CharField(max_length=100) + + class Meta: + app_label = "dummy" diff --git a/.github/workflow-test-fixtures/django-api-uv/manage.py b/.github/workflow-test-fixtures/django-api-uv/manage.py new file mode 100644 index 0000000..7f082c5 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/manage.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +import os +import sys + + +def main() -> None: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/.github/workflow-test-fixtures/django-api-uv/pyproject.toml b/.github/workflow-test-fixtures/django-api-uv/pyproject.toml new file mode 100644 index 0000000..35bd853 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/pyproject.toml @@ -0,0 +1,15 @@ +[project] +name = "workflow-fixture-django-api-uv" +version = "0.1.0" +requires-python = ">=3.12" +dependencies = [ + "django>=5.2,<6", + "psycopg2-binary>=2.9", +] + +[dependency-groups] +dev = [ + "pytest>=8.4", + "pytest-cov>=6.1", + "pytest-django>=4.11", +] diff --git a/.github/workflow-test-fixtures/django-api-uv/pytest.ini b/.github/workflow-test-fixtures/django-api-uv/pytest.ini new file mode 100644 index 0000000..d4fb73f --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +DJANGO_SETTINGS_MODULE = config.settings diff --git a/.github/workflow-test-fixtures/django-api-uv/tests/test_smoke.py b/.github/workflow-test-fixtures/django-api-uv/tests/test_smoke.py new file mode 100644 index 0000000..9ef0f5f --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/tests/test_smoke.py @@ -0,0 +1,8 @@ +import pytest +from dummy.models import DummyModel + + +@pytest.mark.django_db +def test_db_connection(): + DummyModel.objects.create(name="test") + assert DummyModel.objects.count() == 1 diff --git a/.github/workflow-test-fixtures/django-api-uv/uv.lock b/.github/workflow-test-fixtures/django-api-uv/uv.lock new file mode 100644 index 0000000..f295023 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api-uv/uv.lock @@ -0,0 +1,285 @@ +version = 1 +revision = 3 +requires-python = ">=3.12" + +[[package]] +name = "asgiref" +version = "3.11.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/40/f03da1264ae8f7cfdbf9146542e5e7e8100a4c66ab48e791df9a03d3f6c0/asgiref-3.11.1.tar.gz", hash = "sha256:5f184dc43b7e763efe848065441eac62229c9f7b0475f41f80e207a114eda4ce", size = 38550, upload-time = "2026-02-03T13:30:14.33Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/0a/a72d10ed65068e115044937873362e6e32fab1b7dce0046aeb224682c989/asgiref-3.11.1-py3-none-any.whl", hash = "sha256:e8667a091e69529631969fd45dc268fa79b99c92c5fcdda727757e52146ec133", size = 24345, upload-time = "2026-02-03T13:30:13.039Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "coverage" +version = "7.14.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/fd/0ab2772530e946e1be1abd0bc09e647ec9b02e88f0867857601fefca8953/coverage-7.14.1.tar.gz", hash = "sha256:30c08f7d90415aa98b3c990385dea2939b0da55f38515e5b369b83655f8523be", size = 920132, upload-time = "2026-05-26T20:41:36.783Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/b7/bdbb725ba02c5b42825b200c940f38b7a54fcad24627b7192f78f8110d76/coverage-7.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a06c76364a9360e33d6d23769aefdf7f66f38e2ffb60ceb1baaa4989d83b695c", size = 220022, upload-time = "2026-05-26T20:39:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/72/81/fdc0898a55c6219223291ec1a1fe89966ef212ce82276aa0899df84b5de0/coverage-7.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fad54e871165f6ec2f536063ac74c3104508a12963e64072ba44bd822de52b0c", size = 220379, upload-time = "2026-05-26T20:39:05.381Z" }, + { url = "https://files.pythonhosted.org/packages/de/72/de048c4a25e13bce59ac6a339351c10bdf2515e07459afcdaf04dc3143a2/coverage-7.14.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:84b535f00655ecafe1d929d1fb00ed5d6fa3051ea643ab2c161a3887b86f294b", size = 251888, upload-time = "2026-05-26T20:39:07.367Z" }, + { url = "https://files.pythonhosted.org/packages/28/30/300c343f68beb9d4cbb64ec81e58c5b6b80b56927f72d2b38654ac26e013/coverage-7.14.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b6b0853b895fe0e98cbfc580d1ec3393d9302b4b1e96a77b3f5c91fdab899e6", size = 254624, upload-time = "2026-05-26T20:39:09.037Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ed/7b25642496e8170b6bac14adce00537c6e5fa2d586159401a4de3e8b49e6/coverage-7.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442cc9c952b2df400cda54bb04ab87330cf2cd08a8692cbbea36773531eb6f37", size = 255739, upload-time = "2026-05-26T20:39:10.889Z" }, + { url = "https://files.pythonhosted.org/packages/7f/a2/abd210b8c4e29c24e4624916db97bb519097a91034aaeb767f937e7da794/coverage-7.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8270544c361ed405a27a060dbc9ed2c124b084d96dfdc2d9a2510482aef981ad", size = 257998, upload-time = "2026-05-26T20:39:12.722Z" }, + { url = "https://files.pythonhosted.org/packages/7f/24/7c50beed3792fe62f6ce0545c6686ce83379719e2c0276179333d97eae92/coverage-7.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:48b283b1dd6372e8de2a7a9a4c4d5dc06f4d4fd209b876f3c88a7a205a0c8f84", size = 252296, upload-time = "2026-05-26T20:39:14.259Z" }, + { url = "https://files.pythonhosted.org/packages/15/05/0f874628ebcbfc77ead559ff210281ef06a97db08481832e7dd39274a135/coverage-7.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5b0c99ba93a07d56f6df340bb79be53202a082b2fdb81bfe6190b741a3470d54", size = 253658, upload-time = "2026-05-26T20:39:15.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/6f/ca6ad067364b337ef997802115e7ecad2abd2248b05471464b0dea02b4d4/coverage-7.14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e471bc5769ff073b058cfadb0d736b56ce067c8560eabeb0da88462df98c23e7", size = 251803, upload-time = "2026-05-26T20:39:17.537Z" }, + { url = "https://files.pythonhosted.org/packages/c0/30/b9b4d377cd9f40baf228068f5a81faf8450c6228503011bd499708483a50/coverage-7.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f497a1ea81d4cd7c10ddcaa685135b9aabd291af3d55775a9ddf3cb7a364cdd9", size = 255873, upload-time = "2026-05-26T20:39:19.414Z" }, + { url = "https://files.pythonhosted.org/packages/3c/21/7c721a9e5e6bb88547d30a787aefb97512d3f54c1324c7488d9b3743f7f9/coverage-7.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2222be86d0b54f5dd5a38f45f17f315f737245e857bf0bdedc70734f84a13c02", size = 251372, upload-time = "2026-05-26T20:39:21.169Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f8ae5a2200130e1503cd7661a6cd3b2b7bacef98277fbf3571fb13f8b766/coverage-7.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:85e85586565842f6932abebd4c18bcb1074223dc0b3576e7d173ca710622813a", size = 253245, upload-time = "2026-05-26T20:39:23.097Z" }, + { url = "https://files.pythonhosted.org/packages/34/62/70a9024672a5f6910517d9628c52c9afbdd3cf8f46426af52bb148a56fff/coverage-7.14.1-cp312-cp312-win32.whl", hash = "sha256:4a28fd227808366b196a75476dced2eb35b351d6766ba9c858dc93319e87f4f1", size = 222567, upload-time = "2026-05-26T20:39:24.868Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/8b7cd386839b039ebe1855733b9f9449a8dec5d79564018234f185a7fa70/coverage-7.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:54acdb6674a4661768d7bf7db32dfb9f46ab1d764f8aba6df75ce1a6a088724e", size = 223372, upload-time = "2026-05-26T20:39:26.603Z" }, + { url = "https://files.pythonhosted.org/packages/ae/ba/b44d472022f620d289d95fa830143235c0c36461c6f2437ea8d51e5481ed/coverage-7.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:99cd41ff91afd94896fea3bc002706b6ae4ce95727d06e4a0f39c0a8d8bd8b1a", size = 221989, upload-time = "2026-05-26T20:39:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9e/5f6d56327c62b185225d145191c607e07515294a0aa6338e58805cd4a5ac/coverage-7.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:be9f2c802dcfce3f71298303aa5dad0dce440a76c52f2f60dacd8656dab78793", size = 220044, upload-time = "2026-05-26T20:39:29.902Z" }, + { url = "https://files.pythonhosted.org/packages/75/92/e82aca356744cbbc0f77a0b623e38918c1872361963413a3bab5d0340393/coverage-7.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6223a72fd0e4c7156353ec0f08a5f93623e1d3034d0e2683b9bb8ea674131b1d", size = 220412, upload-time = "2026-05-26T20:39:31.561Z" }, + { url = "https://files.pythonhosted.org/packages/27/c9/385bde0bf7ed0f4bf3a7ee5367060a86b5d218718cfd6fb943c0f836b34f/coverage-7.14.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7279d2110a28cebc738b6459ecda2771735a4c18465fbbd36b3288fe5ed92247", size = 251412, upload-time = "2026-05-26T20:39:33.337Z" }, + { url = "https://files.pythonhosted.org/packages/51/8c/23faf6a2343a0d17f960a4bd56c43bc7eb4cf312f774dd6ceebd82c7d8fc/coverage-7.14.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9eeb3fcbc13ba40dfbdb22d01d196a28e9cef9ed4c29b60061a1e0e823a9929d", size = 254008, upload-time = "2026-05-26T20:39:35.009Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/36f4aa9ca8a815e6036156e80706a67828bb97bd826948244f6996dda957/coverage-7.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f0cfc27c539f07cf5c0a4cfe211d0b6cae039f8f40526dbaa71944e64b50a7b", size = 255241, upload-time = "2026-05-26T20:39:36.71Z" }, + { url = "https://files.pythonhosted.org/packages/ca/79/95266316352f90f6b1c6736bb413302edfde2453fb32422d3911642691b3/coverage-7.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:221c70f316241a78e77e607c227cefc8808d4e08f28d99c04f35694690e940be", size = 257373, upload-time = "2026-05-26T20:39:38.412Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9c/58316d1f66c488b5fca8a0eb3e98348807813efa8a0d0833b9021be27488/coverage-7.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:da028256b04ec30e5e0114b6f76172938c313991f0a2d3d894271315cf5d5e43", size = 251635, upload-time = "2026-05-26T20:39:40.268Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5a/ca2398a568e16fed7bb713e84ba3603a7164fb65779abe645c565ec890d5/coverage-7.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76a085d7005236a767e3426148b2c407e53ad61695c562f8a81da2d373324901", size = 253373, upload-time = "2026-05-26T20:39:42.145Z" }, + { url = "https://files.pythonhosted.org/packages/6e/2c/0396562c32deaebe7be51d865b3a41e9a87d7561acafe1a28f53b07e019a/coverage-7.14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b553d04b5e778a8e56d57eb134aff42a92718ecba45e79c4764ecfa40efd92ff", size = 251341, upload-time = "2026-05-26T20:39:43.907Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8f/a94f9221184c9cae1ee115820e3798e48b6b17777a9f19e46fb9a0c8dc74/coverage-7.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:46f714d2fb8ae2f4f29f23ada7f1e79b759fff5a70f94a1dac23af204c3ec9e4", size = 255497, upload-time = "2026-05-26T20:39:46.166Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/505d70e47db1eaebcd002c39759707621ef184cd6b1ae084d9f41293f323/coverage-7.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:1896f5e19ff3f0431c7ce2172adc54890fd97f86b59ced8ca1649145d9ffe35d", size = 251159, upload-time = "2026-05-26T20:39:48.03Z" }, + { url = "https://files.pythonhosted.org/packages/e0/aa/58681c383aa33a9d2ed40a02d7a22fbf780d1fa4d575396365777828198c/coverage-7.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:62fd185ef9df3c33d1c8178c5af105f762afbad96038de9a4ae100aa6297ca33", size = 252934, upload-time = "2026-05-26T20:39:49.872Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/11c928cd6bdffc7074bb5965c173d9ebf517fb00205e1da524b98d29ef92/coverage-7.14.1-cp313-cp313-win32.whl", hash = "sha256:ab4af6352741a604c431c6072fce5bee33bf0f20dc7a56618d6bf6bb89e9810c", size = 222584, upload-time = "2026-05-26T20:39:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/6f/92/fb416fc26d340dcba19518c418d6048e913186e17243982c5e435e41fa7a/coverage-7.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:7af486dabe8954d03b087f0021540897afe084f04e16ff5579e08cc46f871416", size = 223394, upload-time = "2026-05-26T20:39:53.472Z" }, + { url = "https://files.pythonhosted.org/packages/73/c6/02d56e3867972f77d5036de924643f26c056e848f00452cafb4dbc3c29b4/coverage-7.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:2224f89ffd0c5605ccce1ed7a584da162bc7c55f601ab1c946bc9de31a486b42", size = 222015, upload-time = "2026-05-26T20:39:55.374Z" }, + { url = "https://files.pythonhosted.org/packages/4d/9e/fcc77914050df73f7662fa1f00902774c79c075a8388ab334074574bf77e/coverage-7.14.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de286598cc65d2b489411174b1faec2f5a7775fb3201fd925db2a76b4030f37d", size = 220733, upload-time = "2026-05-26T20:39:57.189Z" }, + { url = "https://files.pythonhosted.org/packages/f7/67/2963cbdaf5cbadec44efa3a1e39eaa1f02df4079585f05387607a221e126/coverage-7.14.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:042c46ded7c288aeb07cf14a28b6c1e10b78fcba40171c3fa1e939377eeef0b5", size = 221086, upload-time = "2026-05-26T20:39:59.019Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/8701645574e11881f2f47d8930f98bc48b5d43b25eb5b4430dfc4a2f9f48/coverage-7.14.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f4ddbe407477f04c45115d1a4e5bc480f753553b534d338d4c3358b1cdd0ea52", size = 262381, upload-time = "2026-05-26T20:40:00.822Z" }, + { url = "https://files.pythonhosted.org/packages/7c/28/7a64d73598263e0c5abd5084211a8474488d31b3c552ff531c719dfcff62/coverage-7.14.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d13e6725992e2d2fd7d81d4f5241952d13740121dfd501da09201be39b2c003a", size = 264458, upload-time = "2026-05-26T20:40:02.506Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d8/4969179db9f7eb4df218e69540adf829d1c835f59452513d065d15446802/coverage-7.14.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f747dc8edcfe740130f28f32f3995e955494285717e86ee25af51db2219df08a", size = 266884, upload-time = "2026-05-26T20:40:04.421Z" }, + { url = "https://files.pythonhosted.org/packages/a6/78/a45d5794dbc9bafd97afc96a4377c86c7820d78b6cf51b89bc1d4e919275/coverage-7.14.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ced2f09ef276fd58611a1ef502164ad266d2b75174e5a40cabbdb4033f9f6cf2", size = 268022, upload-time = "2026-05-26T20:40:06.298Z" }, + { url = "https://files.pythonhosted.org/packages/21/cb/4f5e354e9e3e67af96bd4e57113e6db6b22298c7168b13eec408a549903d/coverage-7.14.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b84800013769a78ccb9ef4659402e26d06867e337b61ec365f77ad008adea80e", size = 261631, upload-time = "2026-05-26T20:40:08.226Z" }, + { url = "https://files.pythonhosted.org/packages/ec/49/eced49af4cb996d5d8b7e94e736175c513e4facd3398507b89892b4326d8/coverage-7.14.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ea8cd6ca0ee9f616aaef3afc6882e32c2cbf18b00d96313ffd76af650574034d", size = 264443, upload-time = "2026-05-26T20:40:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/f1/d8/5603a88a7c5913a6b54f6cb1a8c46f7b39cbb30f27cd3f492908da09b2d7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:aa5e304a873fabddc11e484e9b6b738bd38bd7bed17b09aa84eecf5332e8b8bb", size = 262069, upload-time = "2026-05-26T20:40:11.999Z" }, + { url = "https://files.pythonhosted.org/packages/f0/59/2ae3cb79da554a06c8619d6c88ea19dd1e4aed4b834b6a83bb1fa243bdc5/coverage-7.14.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:5a1c5215be81035e629d5bc756650634d0bf31991038db7a0eccb90f025ce16d", size = 265780, upload-time = "2026-05-26T20:40:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/af/5f/b130c1dc999031f2648bd25317fbce505ad8d5562079b4ed81e736a84967/coverage-7.14.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:79058c47dae6788504b5effb319961bcd72d7240551464b91d474bc0ed186d69", size = 260970, upload-time = "2026-05-26T20:40:16.142Z" }, + { url = "https://files.pythonhosted.org/packages/87/d1/ec13ccddeb48ec963bdfa72a11224bac2584bd045ba13beca82f8113e9c7/coverage-7.14.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:370c5afae3fa0658e11694a32b24c2778f6bc2d17718121f94ee185e69f26b54", size = 263157, upload-time = "2026-05-26T20:40:18.382Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c2/cd91ead503045161092d3845f7bb95ea2f25131ce96d3e314dd835d91b9c/coverage-7.14.1-cp313-cp313t-win32.whl", hash = "sha256:3758dd0a7f1fa57365ef2e781df0f0731d38b6e3772259d13dae4bd8a958d4b1", size = 223259, upload-time = "2026-05-26T20:40:20.381Z" }, + { url = "https://files.pythonhosted.org/packages/71/9f/1e28d97e6bd2c76b07f38b7c02870f1371255ff6717f54eca578fcbbdd0e/coverage-7.14.1-cp313-cp313t-win_amd64.whl", hash = "sha256:6ff665fb023a77386fe11685190cee1f60a7d635994a30d9b0a061533d470fce", size = 224320, upload-time = "2026-05-26T20:40:22.316Z" }, + { url = "https://files.pythonhosted.org/packages/a9/e0/d936e908f0e1efa55e52b91e01b52f1055cef5e1ab2718493390ed8e2fb8/coverage-7.14.1-cp313-cp313t-win_arm64.whl", hash = "sha256:17a5a241e5997621a956a7f402a7433ef4221e5152809b785bec79e2323799f1", size = 222577, upload-time = "2026-05-26T20:40:24.894Z" }, + { url = "https://files.pythonhosted.org/packages/d6/34/fc2f101b151af3799a101f0550b0454aa008afdc0add677394ec4aa8ea10/coverage-7.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d5ed429d0b8edaac649e889b4ffcedb6c80b06629a3f93050e3dddfb99235bee", size = 220091, upload-time = "2026-05-26T20:40:27.249Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a7/1ebae2ab5b961b5c79bb09fe7b3ac99edb190d8be4a8c510b2cf66f46468/coverage-7.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8011224a62280e50dab346960c03cf47aca1a1e09e608c0fb33fd6e0cc8e9500", size = 220421, upload-time = "2026-05-26T20:40:30.084Z" }, + { url = "https://files.pythonhosted.org/packages/5e/90/92aca9cf0acc95123c96cd1eb1f08917897a7f5dee01e15738922971ec31/coverage-7.14.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:12c42ec1e14f553c4f817e989365982e646e27211f10a0f717855b94a79c8906", size = 251466, upload-time = "2026-05-26T20:40:32.542Z" }, + { url = "https://files.pythonhosted.org/packages/26/2b/78048cbe3b999f6cbf9cc0d90abba6a88a3e0863a8c1c6cbc762f3f8802f/coverage-7.14.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:06144cd511cf2624873a035c5069cf297144f6e77a73ee3d7a55b605ec5efb42", size = 253973, upload-time = "2026-05-26T20:40:34.473Z" }, + { url = "https://files.pythonhosted.org/packages/8e/21/c2e33b29d1cfde484a19d437afc343c6cd30b08d78cbbf9f5aff14e57b2b/coverage-7.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a311d8e1da24be5c1ccf85cbfb06315dbaa1703d5a1eab3f6432c72b837917c8", size = 255318, upload-time = "2026-05-26T20:40:38.154Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ee/aad2f108d63b769121005302f16bf66db8625c88ceaba466942e09a2607e/coverage-7.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c79cead5b5bc584d9c71451cb984d0e3a84e0c0937379c8efcbf27c8d661b851", size = 257633, upload-time = "2026-05-26T20:40:40.164Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f8/11a2c29b4fd76d9849f81d0bb812ec0017a9396df3217214e38934a8c837/coverage-7.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:dcbf65f1f66a26cdd88c35cf68fb4729c5d1cd2e88added72420541dfb212034", size = 251488, upload-time = "2026-05-26T20:40:42.631Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b8/9a5820de4b8ac2b71d85e3b5fb49108d7469c665f0e2ad0dd7569023e305/coverage-7.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fd86572566fb40189a8260446158235159bc7a82dfbc87a3b39cf4fb57fcec1c", size = 253329, upload-time = "2026-05-26T20:40:45.208Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ff/f33e4823667e27548e8fd8df44217515303f9808d0ff29817db56f87d990/coverage-7.14.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:7771b601718fdde84832c3a434ca9bbf4ae9adbc49d84198b4110700c3c77c36", size = 251291, upload-time = "2026-05-26T20:40:47.502Z" }, + { url = "https://files.pythonhosted.org/packages/68/9b/489db0ebb209054766b90a9014a45f6d26eb724c02ec21311c3733b5a644/coverage-7.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:39b21e212c55af06fa375e3dbf90a8a8e38792f3a910c580066d23563830ddd5", size = 255564, upload-time = "2026-05-26T20:40:49.372Z" }, + { url = "https://files.pythonhosted.org/packages/27/b5/16bc2d4c2409b23c7737edb68c83bc89e345f378050549fe1d75ac7d34d5/coverage-7.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f2302660e32562a532b442480121aef8aa61a5bdb20b30bf0adab29f10a5a4b4", size = 251107, upload-time = "2026-05-26T20:40:51.677Z" }, + { url = "https://files.pythonhosted.org/packages/7d/0c/2629997469a00cd069d588a41c9dc887610f2775ae89d250c4791e65272a/coverage-7.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:03a6f93c1ec3b7f2e77b5dbcc5573a2c21f12529a5c6bbe0f16f72303cc2fa4d", size = 252764, upload-time = "2026-05-26T20:40:54.267Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ee/f78d63c8f079e0d7211c7e2401fa17e311514534ba61bae03e4b287ce4ab/coverage-7.14.1-cp314-cp314-win32.whl", hash = "sha256:8a3ce026d73290f42f08dafecbd82c193a74df280461fbf97300fec51fd133ee", size = 222837, upload-time = "2026-05-26T20:40:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b9/be539854f93a70dfbeec69117f33ec70dc42ff0b65b5b07ab8d40d04228e/coverage-7.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:114c95ef29302423b87d159075805f4ab973254a2638a5d7d046c94887cc87d7", size = 223650, upload-time = "2026-05-26T20:40:58.351Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/24e2842fef40f35ac82ba3a7719c8023d011bf3bf652d0675316a9d088a1/coverage-7.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:a07891c3f4805442b31b71e84ba3cf29ed1aa9a428284e06deeb4b23e5b46343", size = 222218, upload-time = "2026-05-26T20:41:00.321Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1d/ac0a9df5fe31c1e8bdd658074905fc12844a05c1a7e3fdb8417e97c31e23/coverage-7.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1101a5ebb083aecb625ebb6209d4105b58f647b093cb2dc8122d7b33f743cfe1", size = 220822, upload-time = "2026-05-26T20:41:02.281Z" }, + { url = "https://files.pythonhosted.org/packages/32/cf/f964fd9aff20323f9f1a726c97135f8a76bcd87b92dad141a456a43f3c64/coverage-7.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:851b9e1e4e8a4608e77c79714b2e77c0970d2ed7202a05e92ae407817481887b", size = 221084, upload-time = "2026-05-26T20:41:04.593Z" }, + { url = "https://files.pythonhosted.org/packages/d8/5e/7e5ef2aba844de2b80d678619fcf0841b42e3f37f16411226f3fe4c1016f/coverage-7.14.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d5b89cdfb2ee051b71e8c3c70bd81a9eff81100f736a269136fe1a68efe00474", size = 262454, upload-time = "2026-05-26T20:41:06.641Z" }, + { url = "https://files.pythonhosted.org/packages/64/62/75809bded87015cc4935524218a2a8ed8dd1a8498bfed30a2f4f7a4b4d34/coverage-7.14.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0177614a0370f227888b4e436a7c55686d6a9f90eb1ade2b624ba685a1686e86", size = 264578, upload-time = "2026-05-26T20:41:08.556Z" }, + { url = "https://files.pythonhosted.org/packages/f3/42/d33392dc14633525012d2d504fa1a33b05538bf535f5c1d64675e5754b78/coverage-7.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2d69af5dea2de76fc485a83032a630523f985198b7e25be901ec60181587b01e", size = 266981, upload-time = "2026-05-26T20:41:10.824Z" }, + { url = "https://files.pythonhosted.org/packages/2a/49/0157c4428c2aca7f1e09d5565930586fd5ae36f1655f08b0daa7cf1fcae1/coverage-7.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:35ab22d91de736e8966b980dc355cbcdd2c6dbbcfe275f9a2991bc8a91b3df65", size = 268112, upload-time = "2026-05-26T20:41:12.966Z" }, + { url = "https://files.pythonhosted.org/packages/96/26/86b9ce71f4092b1ed325ce1421698081df1286b833400b6836912834d6e0/coverage-7.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:357d4e32935c36588aaba057d734fa32428c360c9fc2e4442afbf1b646beee6e", size = 261558, upload-time = "2026-05-26T20:41:15Z" }, + { url = "https://files.pythonhosted.org/packages/20/4c/c311210c5472cf5401d8422b0d7812cdd520f24417673afabda6c323faca/coverage-7.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:51bd64741cc6fa065abd300ede1afe5a5291ece9c31da8b24884deda48bcc3f8", size = 264447, upload-time = "2026-05-26T20:41:17.369Z" }, + { url = "https://files.pythonhosted.org/packages/fb/71/59513f8710ed3e6b0ac0a050a5b7e977bb9c9e880354863b5d00d8809256/coverage-7.14.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:9132cd363a68a4c3daa7c8704a654b1e39d3360f6f5b8ddd470608a945236c07", size = 262048, upload-time = "2026-05-26T20:41:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/bceed32dc494f5bbf50f775cd2e78ca814953942b5ea28d3c1c3ac316f14/coverage-7.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:07c6290b1697b862c0478eab545eec949a0d0e4d6d03497f446d706da3b4f2de", size = 265781, upload-time = "2026-05-26T20:41:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c5/9348fe40dbfd4991aaf78df2c6c3098bfb2cc834d1fd362a64b4efef855a/coverage-7.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:5ea0c297e27133853b4d8a3eb799bff5a2dbd9f2f41537a240d337ac9b4df890", size = 260896, upload-time = "2026-05-26T20:41:23.428Z" }, + { url = "https://files.pythonhosted.org/packages/ca/92/1ea0f03929da7cf87206b1fa24f4c8e9c158be0455481af29ec0a1f3503f/coverage-7.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:01b7733daad0237daa01ef80fe2dfceffc911e6a17fa7b55d14aa8214eaaaecd", size = 263214, upload-time = "2026-05-26T20:41:25.419Z" }, + { url = "https://files.pythonhosted.org/packages/f6/a9/b2493c054c0e01a643266742ab45e15744e60743f9260cd930c7142b1124/coverage-7.14.1-cp314-cp314t-win32.whl", hash = "sha256:6adc5a36984624a70bf11d7184e20fa0a49aa7c47ffab43804106a1a695ea22e", size = 223624, upload-time = "2026-05-26T20:41:27.795Z" }, + { url = "https://files.pythonhosted.org/packages/fc/bd/3e1e6a57fccd2d7c83fcdf338e93ba98eb85c6e877dd34731ac585375490/coverage-7.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ddf799247318f34dbcd2efa8c95a8d0642674e926bb1774cf9b63dfd2a389d1c", size = 224728, upload-time = "2026-05-26T20:41:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/d7/31066cf1d2f0c6c797fce911bcfa01dd35642dc6da992a950256097c5860/coverage-7.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:145986fe66647eb489f18d9a997567a3fd358584c4b5a808769113abc07466af", size = 222752, upload-time = "2026-05-26T20:41:32.123Z" }, + { url = "https://files.pythonhosted.org/packages/8a/3c/1a983b9a745d7f83d53f057bcc5bf79ba6a2bbc08266b3f0c7d6fe630c9b/coverage-7.14.1-py3-none-any.whl", hash = "sha256:a252f21c27e38347e60111a3266b03827422a7d5525951aceee313aa68bab1d2", size = 211815, upload-time = "2026-05-26T20:41:34.078Z" }, +] + +[[package]] +name = "django" +version = "5.2.15" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asgiref" }, + { name = "sqlparse" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/e3/31722f7284c9f43333daff9aee9184678e4487adcb5506af0db8cea09ce1/django-5.2.15.tar.gz", hash = "sha256:5154a9bf84ac01dde011e367f355c07dbb329532e06810dcf3ef2af269e236e7", size = 10873669, upload-time = "2026-06-03T13:03:35.892Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/b5/38140b1643c00d5c46ce69c78e6980fd285aee223100319631bedee4f5e7/django-5.2.15-py3-none-any.whl", hash = "sha256:0eb4a9bb1853a35b0286dbc6d916bd352c8c2687195a7f2d6f80cefd840e4970", size = 8311957, upload-time = "2026-06-03T13:03:31.329Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "packaging" +version = "26.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2a/60/a3624f79acea344c16fbef3a94d28b89a8042ddfb8f3e4ca83f538671409/psycopg2_binary-2.9.12.tar.gz", hash = "sha256:5ac9444edc768c02a6b6a591f070b8aae28ff3a99be57560ac996001580f294c", size = 379686, upload-time = "2026-04-21T09:40:34.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/9f/ef4ef3c8e15083df90ca35265cfd1a081a2f0cc07bb229c6314c6af817f4/psycopg2_binary-2.9.12-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5cdc05117180c5fa9c40eea8ea559ce64d73824c39d928b7da9fb5f6a9392433", size = 3712459, upload-time = "2026-04-20T23:34:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/b5/01/3dd14e46ba48c1e1a6ec58ee599fa1b5efa00c246d5046cd903d0eeb1af1/psycopg2_binary-2.9.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d3227a3bc228c10d21011a99245edca923e4e8bf461857e869a507d9a41fe9f6", size = 3822936, upload-time = "2026-04-20T23:34:32.77Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/0640e4901119d8a9f7a1784b927f494e2198e213ceb593753d1f2c8b1b30/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:995ce929eede89db6254b50827e2b7fd61e50d11f0b116b29fffe4a2e53c4580", size = 4578676, upload-time = "2026-04-20T23:34:35.18Z" }, + { url = "https://files.pythonhosted.org/packages/b0/55/44df3965b5f297c50cc0b1b594a31c67d6127a9d133045b8a66611b14dfb/psycopg2_binary-2.9.12-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9fe06d93e72f1c048e731a2e3e7854a5bfaa58fc736068df90b352cefe66f03f", size = 4274917, upload-time = "2026-04-20T23:34:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4b/74535248b1eac0c9336862e8617c765ac94dac76f9e25d7c4a79588c8907/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40e7b28b63aaf737cb3a1edc3a9bbc9a9f4ad3dcb7152e8c1130e4050eddcb7d", size = 5894843, upload-time = "2026-04-20T23:34:40.856Z" }, + { url = "https://files.pythonhosted.org/packages/f2/ba/f1bf8d2ae71868ad800b661099086ee52bc0f8d9f05be1acd8ebb06757cc/psycopg2_binary-2.9.12-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:89d19a9f7899e8eb0656a2b3a08e0da04c720a06db6e0033eab5928aabe60fa9", size = 4110556, upload-time = "2026-04-20T23:34:44.016Z" }, + { url = "https://files.pythonhosted.org/packages/45/46/c15706c338403b7c420bcc0c2905aad116cc064545686d8bf85f1999ea00/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:612b965daee295ae2da8f8218ce1d274645dc76ef3f1abf6a0a94fd57eff876d", size = 3655714, upload-time = "2026-04-20T23:34:46.233Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7c/a2d5dc09b64a4564db242a0fe418fde7d33f6f8259dd2c5b9d7def00fb5a/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b9a339b79d37c1b45f3235265f07cdeb0cb5ad7acd2ac7720a5920989c17c24e", size = 3301154, upload-time = "2026-04-20T23:34:49.528Z" }, + { url = "https://files.pythonhosted.org/packages/c0/e8/cc8c9a4ce71461f9ec548d38cadc41dc184b34c73e6455450775a9334ccd/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:3471336e1acfd9c7fe507b8bad5af9317b6a89294f9eb37bd9a030bb7bebcdc6", size = 3048882, upload-time = "2026-04-20T23:34:51.86Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/31e2296bc0787c5ab75d3d118e40b239db8151b5192b90b77c72bc9256e9/psycopg2_binary-2.9.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7af18183109e23502c8b2ae7f6926c0882766f35b5175a4cd737ad825e4d7a1b", size = 3351298, upload-time = "2026-04-20T23:34:54.124Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a8/75f4e3e11203b590150abed2cf7794b9c9c9f7eceddae955191138b44dde/psycopg2_binary-2.9.12-cp312-cp312-win_amd64.whl", hash = "sha256:398fcd4db988c7d7d3713e2b8e18939776fd3fb447052daae4f24fa39daede4c", size = 2757230, upload-time = "2026-04-20T23:34:56.242Z" }, + { url = "https://files.pythonhosted.org/packages/91/bb/4608c96f970f6e0c56572e87027ef4404f709382a3503e9934526d7ba051/psycopg2_binary-2.9.12-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7c729a73c7b1b84de3582f73cdd27d905121dc2c531f3d9a3c32a3011033b965", size = 3712419, upload-time = "2026-04-20T23:34:58.754Z" }, + { url = "https://files.pythonhosted.org/packages/5e/af/48f76af9d50d61cf390f8cd657b503168b089e2e9298e48465d029fcc713/psycopg2_binary-2.9.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4413d0caef93c5cf50b96863df4c2efe8c269bf2267df353225595e7e15e8df7", size = 3822990, upload-time = "2026-04-20T23:35:00.821Z" }, + { url = "https://files.pythonhosted.org/packages/7a/df/aba0f99397cd811d32e06fc0cc781f1f3ce98bc0e729cb423925085d781a/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4dfcf8e45ebb0c663be34a3442f65e17311f3367089cd4e5e3a3e8e62c978777", size = 4578696, upload-time = "2026-04-20T23:35:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/95/9c/eaa74021ac4e4d5c2f83d82fc6615a63f4fe6c94dc4e94c3990427053f67/psycopg2_binary-2.9.12-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c41321a14dd74aceb6a9a643b9253a334521babfa763fa873e33d89cfa122fb5", size = 4274982, upload-time = "2026-04-20T23:35:05.583Z" }, + { url = "https://files.pythonhosted.org/packages/35/ed/c25deff98bd26187ba48b3b250a3ffc3037c46c5b89362534a15d200e0db/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83946ba43979ebfdc99a3cd0ee775c89f221df026984ba19d46133d8d75d3cd9", size = 5894867, upload-time = "2026-04-20T23:35:07.902Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/8d0e21ca77373c6c9589e5c4528f6e8f0c08c62cafc76fb0bddb7a2cee22/psycopg2_binary-2.9.12-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:411e85815652d13560fbe731878daa5d92378c4995a22302071890ec3397d019", size = 4110578, upload-time = "2026-04-20T23:35:10.149Z" }, + { url = "https://files.pythonhosted.org/packages/00/fc/f481e2435bd8f742d0123309174aae4165160ad3ef17c1b99c3622c241d2/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c8ad4c08e00f7679559eaed7aff1edfffc60c086b976f93972f686384a95e2c", size = 3655816, upload-time = "2026-04-20T23:35:12.56Z" }, + { url = "https://files.pythonhosted.org/packages/53/79/b9f46466bdbe9f239c96cde8be33c1aace4842f06013b47b730dc9759187/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:00814e40fa23c2b37ef0a1e3c749d89982c73a9cb5046137f0752a22d432e82f", size = 3301307, upload-time = "2026-04-20T23:35:15.029Z" }, + { url = "https://files.pythonhosted.org/packages/3f/19/7dc003b32fe35024df89b658104f7c8538a8b2dcbde7a4e746ce929742e7/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:98062447aebc20ed20add1f547a364fd0ef8933640d5372ff1873f8deb9b61be", size = 3048968, upload-time = "2026-04-20T23:35:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/91/58/2dbd7db5c604d45f4950d988506aae672a14126ec22998ced5021cbb76bb/psycopg2_binary-2.9.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:66a7685d7e548f10fb4ce32fb01a7b7f4aa702134de92a292c7bd9e0d3dbd290", size = 3351369, upload-time = "2026-04-20T23:35:18.933Z" }, + { url = "https://files.pythonhosted.org/packages/42/ee/dee8dcaad07f735824de3d6563bc67119fa6c28257b17977a8d624f02fab/psycopg2_binary-2.9.12-cp313-cp313-win_amd64.whl", hash = "sha256:b6937f5fe4e180aeee87de907a2fa982ded6f7f15d7218f78a083e4e1d68f2a0", size = 2757347, upload-time = "2026-04-20T23:35:21.283Z" }, + { url = "https://files.pythonhosted.org/packages/13/1b/708c0dca874acfad6d65314271859899a79007686f3a1f74e82a2ed4b645/psycopg2_binary-2.9.12-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6f3b3de8a74ef8db215f22edffb19e32dc6fa41340456de7ec99efdc8a7b3ec2", size = 3712428, upload-time = "2026-04-20T23:35:23.453Z" }, + { url = "https://files.pythonhosted.org/packages/d6/39/ddbea9d4b4de6aca9431b6ed253f530f8a02d3b8f9bcfd0dbfe2b3de6fe4/psycopg2_binary-2.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1006fb62f0f0bc5ce256a832356c6262e91be43f5e4eb15b5eaf38079464caf2", size = 3823184, upload-time = "2026-04-20T23:35:25.92Z" }, + { url = "https://files.pythonhosted.org/packages/bf/a0/bc2fef74b106fa345567122a0659e6d94512ed7dc0131ec44c9e5aba3725/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:840066105706cd2eb29b9a1c2329620056582a4bf3e8169dec5c447042d0869f", size = 4579157, upload-time = "2026-04-20T23:35:28.542Z" }, + { url = "https://files.pythonhosted.org/packages/57/d7/d4e3b2005d3de607ca4fbb0e8742e248056e52184a6b94ebda3c1c2c329b/psycopg2_binary-2.9.12-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:863f5d12241ebe1c76a72a04c2113b6dc905f90b9cef0e9be0efd994affd9354", size = 4274970, upload-time = "2026-04-20T23:35:30.418Z" }, + { url = "https://files.pythonhosted.org/packages/2e/42/c9853f8db3967fe08bcde11f53d53b85d351750cae726ce001cb68afa9c1/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a99eaab34a9010f1a086b126de467466620a750634d114d20455f3a824aae033", size = 5895175, upload-time = "2026-04-20T23:35:33.584Z" }, + { url = "https://files.pythonhosted.org/packages/eb/fd/b82b5601a97630308bef079f545ffec481bbbc795c2ba5ec416a01d03f60/psycopg2_binary-2.9.12-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ffdd7dc5463ccd61845ac37b7012d0f35a1548df9febe14f8dd549be4a0bc81e", size = 4110658, upload-time = "2026-04-20T23:35:35.638Z" }, + { url = "https://files.pythonhosted.org/packages/62/8c/32ca69b0389ef25dd22937bf9e8fbe2ce27aea20b05ded48c4ce4cb42475/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:54a0dfecab1b48731f934e06139dfe11e24219fb6d0ceb32177cf0375f14c7b5", size = 3656251, upload-time = "2026-04-20T23:35:37.854Z" }, + { url = "https://files.pythonhosted.org/packages/c4/29/96992a2b59e3b9d730fcf9612d0a387305025dc867a9fc490a9e496e074e/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:96937c9c5d891f772430f418a7a8b4691a90c3e6b93cf72b5bd7cad8cbca32a5", size = 3301810, upload-time = "2026-04-20T23:35:39.927Z" }, + { url = "https://files.pythonhosted.org/packages/56/ad/44b06659949b243ae10112cd3b20a197f9bf3e81d5651379b9eb889bfaad/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:77b348775efd4cdab410ec6609d81ccecd1139c90265fa583a7255c8064bc03d", size = 3048977, upload-time = "2026-04-20T23:35:41.806Z" }, + { url = "https://files.pythonhosted.org/packages/1d/f2/10a1bcebadb6aa55e280e1f58975c36a7b560ea525184c7aa4064c466633/psycopg2_binary-2.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:527e6342b3e44c2f0544f6b8e927d60de7f163f5723b8f1dfa7d2a84298738cd", size = 3351466, upload-time = "2026-04-20T23:35:43.993Z" }, + { url = "https://files.pythonhosted.org/packages/20/be/b732c8418ffa5bcfda002890f5dc4c869fc17db66ff11f53b17cfe44afc0/psycopg2_binary-2.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:f12ae41fcafadb39b2785e64a40f9db05d6de2ac114077457e0e7c597f3af980", size = 2848762, upload-time = "2026-04-20T23:35:46.421Z" }, +] + +[[package]] +name = "pygments" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage" }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/51/a849f96e117386044471c8ec2bd6cfebacda285da9525c9106aeb28da671/pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2", size = 55592, upload-time = "2026-03-21T20:11:16.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/7a/d968e294073affff457b041c2be9868a40c1c71f4a35fcc1e45e5493067b/pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678", size = 22876, upload-time = "2026-03-21T20:11:14.438Z" }, +] + +[[package]] +name = "pytest-django" +version = "4.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/13/2b/db9a193df89e5660137f5428063bcc2ced7ad790003b26974adf5c5ceb3b/pytest_django-4.12.0.tar.gz", hash = "sha256:df94ec819a83c8979c8f6de13d9cdfbe76e8c21d39473cfe2b40c9fc9be3c758", size = 91156, upload-time = "2026-02-14T18:40:49.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/a5/41d091f697c09609e7ef1d5d61925494e0454ebf51de7de05f0f0a728f1d/pytest_django-4.12.0-py3-none-any.whl", hash = "sha256:3ff300c49f8350ba2953b90297d23bf5f589db69545f56f1ec5f8cff5da83e85", size = 26123, upload-time = "2026-02-14T18:40:47.381Z" }, +] + +[[package]] +name = "sqlparse" +version = "0.5.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + +[[package]] +name = "workflow-fixture-django-api-uv" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "django" }, + { name = "psycopg2-binary" }, +] + +[package.dev-dependencies] +dev = [ + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "pytest-django" }, +] + +[package.metadata] +requires-dist = [ + { name = "django", specifier = ">=5.2,<6" }, + { name = "psycopg2-binary", specifier = ">=2.9" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8.4" }, + { name = "pytest-cov", specifier = ">=6.1" }, + { name = "pytest-django", specifier = ">=4.11" }, +] diff --git a/.github/workflow-test-fixtures/django-api/.pre-commit-config.yaml b/.github/workflow-test-fixtures/django-api/.pre-commit-config.yaml new file mode 100644 index 0000000..4e6a92c --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/.pre-commit-config.yaml @@ -0,0 +1 @@ +repos: [] diff --git a/.github/workflow-test-fixtures/django-api/config/__init__.py b/.github/workflow-test-fixtures/django-api/config/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/config/__init__.py @@ -0,0 +1 @@ + diff --git a/.github/workflow-test-fixtures/django-api/config/settings.py b/.github/workflow-test-fixtures/django-api/config/settings.py new file mode 100644 index 0000000..656ce31 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/config/settings.py @@ -0,0 +1,48 @@ +import os +from pathlib import Path + +BASE_DIR = Path(__file__).resolve().parent.parent + +SECRET_KEY = os.environ.get("SECRET_KEY", "compat-secret") +DEBUG = True +ALLOWED_HOSTS: list[str] = ["*"] + +INSTALLED_APPS = [ + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "dummy", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", +] + +ROOT_URLCONF = "config.urls" +TEMPLATES: list[dict[str, object]] = [] +WSGI_APPLICATION = "config.wsgi.application" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": os.environ.get("POSTGRES_DB", "test_db"), + "USER": os.environ.get("POSTGRES_USER", "test_user"), + "PASSWORD": os.environ.get("POSTGRES_PASSWORD", "test_password"), + "HOST": os.environ.get("POSTGRES_HOST", "localhost"), + "PORT": os.environ.get("POSTGRES_PORT", "5432"), + } +} + +LANGUAGE_CODE = "en-us" +TIME_ZONE = "UTC" +USE_I18N = True +USE_TZ = True +STATIC_URL = "static/" +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/.github/workflow-test-fixtures/django-api/config/urls.py b/.github/workflow-test-fixtures/django-api/config/urls.py new file mode 100644 index 0000000..1883db9 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/config/urls.py @@ -0,0 +1,10 @@ +from django.http import HttpResponse +from django.urls import path + + +def healthz(_request): + return HttpResponse("ok") + +urlpatterns = [ + path("healthz/", healthz), +] diff --git a/.github/workflow-test-fixtures/django-api/config/wsgi.py b/.github/workflow-test-fixtures/django-api/config/wsgi.py new file mode 100644 index 0000000..8509335 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/config/wsgi.py @@ -0,0 +1,7 @@ +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + +application = get_wsgi_application() diff --git a/.github/workflow-test-fixtures/django-api/dummy/__init__.py b/.github/workflow-test-fixtures/django-api/dummy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflow-test-fixtures/django-api/dummy/migrations/0001_initial.py b/.github/workflow-test-fixtures/django-api/dummy/migrations/0001_initial.py new file mode 100644 index 0000000..bbe83a9 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/dummy/migrations/0001_initial.py @@ -0,0 +1,15 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + dependencies: list = [] + operations = [ + migrations.CreateModel( + name="DummyModel", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("name", models.CharField(max_length=100)), + ], + ), + ] diff --git a/.github/workflow-test-fixtures/django-api/dummy/migrations/__init__.py b/.github/workflow-test-fixtures/django-api/dummy/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflow-test-fixtures/django-api/dummy/models.py b/.github/workflow-test-fixtures/django-api/dummy/models.py new file mode 100644 index 0000000..5fc8b6f --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/dummy/models.py @@ -0,0 +1,8 @@ +from django.db import models + + +class DummyModel(models.Model): + name = models.CharField(max_length=100) + + class Meta: + app_label = "dummy" diff --git a/.github/workflow-test-fixtures/django-api/manage.py b/.github/workflow-test-fixtures/django-api/manage.py new file mode 100644 index 0000000..7f082c5 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/manage.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +import os +import sys + + +def main() -> None: + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings") + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/.github/workflow-test-fixtures/django-api/pytest.ini b/.github/workflow-test-fixtures/django-api/pytest.ini new file mode 100644 index 0000000..d4fb73f --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +DJANGO_SETTINGS_MODULE = config.settings diff --git a/.github/workflow-test-fixtures/django-api/requirements-dev.txt b/.github/workflow-test-fixtures/django-api/requirements-dev.txt new file mode 100644 index 0000000..837ea64 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/requirements-dev.txt @@ -0,0 +1,3 @@ +pytest +pytest-cov +pytest-django diff --git a/.github/workflow-test-fixtures/django-api/requirements.txt b/.github/workflow-test-fixtures/django-api/requirements.txt new file mode 100644 index 0000000..8300079 --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/requirements.txt @@ -0,0 +1,2 @@ +Django>=5.2,<6 +psycopg2-binary>=2.9 diff --git a/.github/workflow-test-fixtures/django-api/tests/test_smoke.py b/.github/workflow-test-fixtures/django-api/tests/test_smoke.py new file mode 100644 index 0000000..9ef0f5f --- /dev/null +++ b/.github/workflow-test-fixtures/django-api/tests/test_smoke.py @@ -0,0 +1,8 @@ +import pytest +from dummy.models import DummyModel + + +@pytest.mark.django_db +def test_db_connection(): + DummyModel.objects.create(name="test") + assert DummyModel.objects.count() == 1 diff --git a/.github/workflow-test-fixtures/node-pnpm/package.json b/.github/workflow-test-fixtures/node-pnpm/package.json new file mode 100644 index 0000000..978555f --- /dev/null +++ b/.github/workflow-test-fixtures/node-pnpm/package.json @@ -0,0 +1,11 @@ +{ + "name": "workflow-compat-node-pnpm", + "private": true, + "version": "1.0.0", + "packageManager": "pnpm@11.5.3", + "scripts": { + "lint": "echo lint-ok", + "build": "mkdir -p dist && echo \"console.log('ok')\" > dist/index.js", + "test:coverage": "mkdir -p coverage && echo '{}' > coverage/coverage-final.json" + } +} diff --git a/.github/workflow-test-fixtures/node-pnpm/pnpm-lock.yaml b/.github/workflow-test-fixtures/node-pnpm/pnpm-lock.yaml new file mode 100644 index 0000000..8928259 --- /dev/null +++ b/.github/workflow-test-fixtures/node-pnpm/pnpm-lock.yaml @@ -0,0 +1,8 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: {} diff --git a/.github/workflow-test-fixtures/node-yarn/package.json b/.github/workflow-test-fixtures/node-yarn/package.json new file mode 100644 index 0000000..8038b98 --- /dev/null +++ b/.github/workflow-test-fixtures/node-yarn/package.json @@ -0,0 +1,10 @@ +{ + "name": "workflow-compat-node-yarn", + "private": true, + "version": "1.0.0", + "scripts": { + "lint": "echo lint-ok", + "build": "mkdir -p dist && echo \"console.log('ok')\" > dist/index.js", + "test:coverage": "mkdir -p coverage && echo '{}' > coverage/coverage-final.json" + } +} diff --git a/.github/workflow-test-fixtures/node-yarn/yarn.lock b/.github/workflow-test-fixtures/node-yarn/yarn.lock new file mode 100644 index 0000000..4a58018 --- /dev/null +++ b/.github/workflow-test-fixtures/node-yarn/yarn.lock @@ -0,0 +1,2 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 diff --git a/.github/workflows/ci-django-api.yml b/.github/workflows/ci-django-api.yml index 3fb4856..fc89091 100644 --- a/.github/workflows/ci-django-api.yml +++ b/.github/workflows/ci-django-api.yml @@ -35,7 +35,7 @@ on: description: 'Path to commitlint config file' required: false type: string - default: '' + default: './commitlint.config.mjs' pre-commit-config-file: description: 'Path to .pre-commit-config.yaml. If empty, pre-commit uses default discovery.' required: false diff --git a/.github/workflows/ci-node.yml b/.github/workflows/ci-node.yml index 75e581a..dfd39af 100644 --- a/.github/workflows/ci-node.yml +++ b/.github/workflows/ci-node.yml @@ -40,7 +40,7 @@ on: description: 'Path to commitlint config file' required: false type: string - default: '' + default: './commitlint.config.mjs' working-directory: description: 'Working directory for the yarn installation, build and tests jobs.' required: false @@ -51,6 +51,11 @@ on: required: false type: boolean default: true + artifact-name: + description: 'Name for the uploaded build artifact. Defaults to build-artifacts-.' + required: false + type: string + default: '' secrets: SONAR_TOKEN: description: 'SonarQube Cloud token' @@ -123,7 +128,7 @@ jobs: if: ${{ inputs.upload_artifacts }} uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: build-artifacts-${{ inputs.node-version }} + name: ${{ inputs.artifact-name != '' && inputs.artifact-name || format('build-artifacts-{0}', inputs.node-version) }} path: | ${{ inputs.app-directory }}/dist ${{ inputs.app-directory }}/build diff --git a/.github/workflows/ci-pnpm-node.yml b/.github/workflows/ci-pnpm-node.yml index e078046..4665459 100644 --- a/.github/workflows/ci-pnpm-node.yml +++ b/.github/workflows/ci-pnpm-node.yml @@ -58,7 +58,7 @@ on: description: 'Path to commitlint config file' required: false type: string - default: '' + default: './commitlint.config.mjs' secrets: SONAR_TOKEN: description: 'SonarQube Cloud token' diff --git a/.github/workflows/ci-python-library.yml b/.github/workflows/ci-python-library.yml index 2e01a60..e3b8a76 100644 --- a/.github/workflows/ci-python-library.yml +++ b/.github/workflows/ci-python-library.yml @@ -18,10 +18,10 @@ on: type: boolean default: true commitlint-config-file: - description: 'Path to commitlint config file. If empty, commitlint uses its default config discovery.' + description: 'Path to commitlint config file.' required: false type: string - default: '' + default: './commitlint.config.mjs' pre-commit-config-file: description: 'Path to .pre-commit-config.yaml. If empty, pre-commit uses default discovery.' required: false diff --git a/.github/workflows/ci-uv-django-api.yml b/.github/workflows/ci-uv-django-api.yml index 1134524..38eb2d7 100644 --- a/.github/workflows/ci-uv-django-api.yml +++ b/.github/workflows/ci-uv-django-api.yml @@ -35,7 +35,7 @@ on: description: 'Path to commitlint config file' required: false type: string - default: '' + default: './commitlint.config.mjs' pre-commit-config-file: description: 'Path to .pre-commit-config.yaml. If empty, pre-commit uses default discovery.' required: false diff --git a/.github/workflows/test-reusable-workflows-README.md b/.github/workflows/test-reusable-workflows-README.md new file mode 100644 index 0000000..184713e --- /dev/null +++ b/.github/workflows/test-reusable-workflows-README.md @@ -0,0 +1,16 @@ +# ๐Ÿงช Reusable Workflow Tests + +This workflow tests the reusable workflows in this repository to ensure they work correctly before changes are merged. + +## When it runs + +Triggered automatically on pull requests that modify workflow files or test fixtures. + +## Test fixtures + +The `.github/workflow-test-fixtures/` directory contains minimal project stubs used as test subjects for the reusable workflows: + +- **`django-api/`** โ€” pip-based Django project fixture for `ci-django-api.yml` +- **`django-api-uv/`** โ€” uv-based Django project fixture for `ci-uv-django-api.yml` +- **`node-yarn/`** โ€” yarn-based Node.js fixture for `ci-node.yml` +- **`node-pnpm/`** โ€” pnpm-based Node.js fixture for `ci-pnpm-node.yml` diff --git a/.github/workflows/test-reusable-workflows.yml b/.github/workflows/test-reusable-workflows.yml new file mode 100644 index 0000000..76f1b5e --- /dev/null +++ b/.github/workflows/test-reusable-workflows.yml @@ -0,0 +1,117 @@ +name: Reusable Workflow Tests + +on: + pull_request: + paths: + - ".github/workflows/*.yml" + - "!.github/workflows/sync-files.yml" + - ".github/workflow-test-fixtures/**" + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + +jobs: + test-ci-node: + name: Test ci-node + uses: ./.github/workflows/ci-node.yml + with: + node-version: "24" + working-directory: ".github/workflow-test-fixtures/node-yarn" + app-directory: ".github/workflow-test-fixtures/node-yarn" + upload_artifacts: true + skip_sonar: true + commitlint-config-file: .github/workflow-test-fixtures/commitlint.config.mjs + extra-commands: "node --version" + artifact-name: build-artifacts-yarn-24 + secrets: + SONAR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test-ci-node-assert-artifact: + name: Test ci-node (assert artifact) + runs-on: ubuntu-latest + needs: test-ci-node + steps: + - name: Download build artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: build-artifacts-yarn-24 + - name: Verify expected build output + run: test -f dist/index.js + + test-ci-pnpm-node: + name: Test ci-pnpm-node + uses: ./.github/workflows/ci-pnpm-node.yml + with: + node-version: "24" + working-directory: ".github/workflow-test-fixtures/node-pnpm" + app-directory: ".github/workflow-test-fixtures/node-pnpm" + pnpm-version: "11.5.3" + upload-artifacts: true + skip-sonar: true + extra-commands: "node --version" + secrets: + name: Test ci-pnpm-node (assert artifact) + runs-on: ubuntu-latest + needs: test-ci-pnpm-node + steps: + - name: Download build artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: build-artifacts-24 + - name: Verify expected build output + run: test -f dist/index.js + + test-ci-django-api: + name: Test ci-django-api + uses: ./.github/workflows/ci-django-api.yml + with: + python-version: "3.12" + postgres-major-version: 14 + working-directory: ".github/workflow-test-fixtures/django-api" + skip-sonar: true + commitlint-config-file: .github/workflow-test-fixtures/commitlint.config.mjs + pre-commit-config-file: .github/workflow-test-fixtures/django-api/.pre-commit-config.yaml + extra-commands: "python manage.py check" + secrets: + SONAR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test-ci-uv-django-api: + name: Test ci-uv-django-api + uses: ./.github/workflows/ci-uv-django-api.yml + with: + python-version: "3.12" + postgres-major-version: 14 + working-directory: ".github/workflow-test-fixtures/django-api-uv" + skip-sonar: true + commitlint-config-file: .github/workflow-test-fixtures/commitlint.config.mjs + pre-commit-config-file: .github/workflow-test-fixtures/django-api-uv/.pre-commit-config.yaml + extra-commands: "uv run python manage.py check" + secrets: + SONAR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test-build-python-dists: + name: Test build-python-dists + uses: ./.github/workflows/build-python-dists.yml + with: + build-command: | + python - <<'PY' + from pathlib import Path + Path("dist").mkdir(exist_ok=True) + Path("dist/test.txt").write_text("ok", encoding="utf-8") + PY + artifact-path: "dist/test.txt" + artifact-name: "test-python-dist" + + test-build-python-dists-assert-artifact: + name: Test build-python-dists (assert artifact) + runs-on: ubuntu-latest + needs: test-build-python-dists + steps: + - name: Download distribution artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: test-python-dist + - name: Verify artifact file exists + run: test -f test.txt From e59465befa7b307587c0087af73533c5f8ffa723 Mon Sep 17 00:00:00 2001 From: Tuomas Haapala Date: Fri, 12 Jun 2026 11:09:49 +0300 Subject: [PATCH 5/6] feat: harden workflows with permissions and persist-credentials Refs: RATY-350 --- .github/workflows/ci-django-api.yml | 10 ++++++++++ .github/workflows/ci-node.yml | 11 +++++++++++ .github/workflows/ci-npm-publish.yml | 5 +++++ .github/workflows/ci-pnpm-node.yml | 11 +++++++++++ .github/workflows/ci-python-library.yml | 11 +++++++++++ .github/workflows/ci-uv-django-api.yml | 10 ++++++++++ .github/workflows/sync-files.yml | 6 ++++++ .github/workflows/test-reusable-workflows.yml | 4 ++++ 8 files changed, 68 insertions(+) diff --git a/.github/workflows/ci-django-api.yml b/.github/workflows/ci-django-api.yml index fc89091..16f7c77 100644 --- a/.github/workflows/ci-django-api.yml +++ b/.github/workflows/ci-django-api.yml @@ -47,6 +47,10 @@ on: required: false workflow_dispatch: +permissions: + contents: read + pull-requests: read + env: SECRET_KEY: topsecret123 DATABASE_URL: ${{ inputs.use-postgis == true && 'postgis' || 'postgres' }}://test_user:test_password@localhost/test_db @@ -60,6 +64,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 with: @@ -71,6 +77,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Setup Python ${{ inputs.python-version }} uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: @@ -126,6 +134,7 @@ jobs: uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 1 + persist-credentials: false - name: Install system packages run: | sudo apt-get update @@ -188,6 +197,7 @@ jobs: # Required by SonarQube Cloud for blame information with: fetch-depth: 0 + persist-credentials: false - name: Restore coverage.xml uses: actions/cache/restore@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: diff --git a/.github/workflows/ci-node.yml b/.github/workflows/ci-node.yml index dfd39af..d1f3ce7 100644 --- a/.github/workflows/ci-node.yml +++ b/.github/workflows/ci-node.yml @@ -62,6 +62,10 @@ on: required: false workflow_dispatch: +permissions: + contents: read + pull-requests: read + jobs: commitlint: name: Check commit messages @@ -69,6 +73,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 with: @@ -83,6 +89,8 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Use Node.js ${{ inputs.node-version }} with dependency path if: ${{ inputs.working-directory != '.' }} uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 @@ -151,6 +159,8 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Use Node.js ${{ inputs.node-version }} with dependency path if: ${{ inputs.working-directory != '.' }} uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 @@ -208,6 +218,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 + persist-credentials: false # Coverage path logic (keep in sync with tests job's cache/save step): # Resolves the effective directory (app-directory if set, else working-directory), # then prefixes 'coverage' with '/' when running in a subdirectory. diff --git a/.github/workflows/ci-npm-publish.yml b/.github/workflows/ci-npm-publish.yml index d9ee12b..a5fea96 100644 --- a/.github/workflows/ci-npm-publish.yml +++ b/.github/workflows/ci-npm-publish.yml @@ -43,6 +43,9 @@ on: type: boolean default: false +permissions: + contents: read + jobs: publish-npm-stable: runs-on: ubuntu-latest @@ -51,6 +54,8 @@ jobs: id-token: write steps: - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + with: + persist-credentials: false - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ inputs.node-version }} diff --git a/.github/workflows/ci-pnpm-node.yml b/.github/workflows/ci-pnpm-node.yml index 4665459..b113857 100644 --- a/.github/workflows/ci-pnpm-node.yml +++ b/.github/workflows/ci-pnpm-node.yml @@ -65,6 +65,10 @@ on: required: false workflow_dispatch: +permissions: + contents: read + pull-requests: read + jobs: commitlint: name: Check commit messages @@ -72,6 +76,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 with: @@ -86,6 +92,8 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 @@ -158,6 +166,8 @@ jobs: working-directory: ${{ inputs.working-directory }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@8912a9102ac27614460f54aedde9e1e7f9aec20d # v6.0.5 @@ -215,6 +225,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 with: fetch-depth: 0 + persist-credentials: false # Coverage path logic (keep in sync with tests job's cache/save step): # Resolves the effective directory (app-directory if set, else working-directory), diff --git a/.github/workflows/ci-python-library.yml b/.github/workflows/ci-python-library.yml index e3b8a76..866b085 100644 --- a/.github/workflows/ci-python-library.yml +++ b/.github/workflows/ci-python-library.yml @@ -35,6 +35,10 @@ on: description: "GITHUB_TOKEN. Required if enable-sonar is set to true." required: false +permissions: + contents: read + pull-requests: read + jobs: commitlint: name: Check commit messages @@ -43,6 +47,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 @@ -55,6 +61,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Setup Python ${{ inputs.python-version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -75,6 +83,8 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python_version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -119,6 +129,7 @@ jobs: with: # Required by SonarQube Cloud fetch-depth: 0 + persist-credentials: false - name: Download coverage reports uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/ci-uv-django-api.yml b/.github/workflows/ci-uv-django-api.yml index 38eb2d7..3c031db 100644 --- a/.github/workflows/ci-uv-django-api.yml +++ b/.github/workflows/ci-uv-django-api.yml @@ -47,6 +47,10 @@ on: required: false workflow_dispatch: +permissions: + contents: read + pull-requests: read + env: SECRET_KEY: topsecret123 DATABASE_URL: ${{ inputs.use-postgis == true && 'postgis' || 'postgres' }}://test_user:test_password@localhost/test_db @@ -60,6 +64,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - name: Run commitlint uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 with: @@ -71,6 +77,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false - name: Setup Python ${{ inputs.python-version }} uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: @@ -126,6 +134,7 @@ jobs: uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 with: fetch-depth: 1 + persist-credentials: false - name: Install system packages run: | sudo apt-get update @@ -177,6 +186,7 @@ jobs: # Required by SonarQube Cloud for blame information with: fetch-depth: 0 + persist-credentials: false - name: Restore coverage.xml uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: diff --git a/.github/workflows/sync-files.yml b/.github/workflows/sync-files.yml index 803b804..0a2f5f1 100644 --- a/.github/workflows/sync-files.yml +++ b/.github/workflows/sync-files.yml @@ -4,12 +4,18 @@ on: branches: - main workflow_dispatch: + +permissions: + contents: read + jobs: sync: runs-on: ubuntu-latest steps: - name: Checkout Repository uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false - uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2 id: app-token with: diff --git a/.github/workflows/test-reusable-workflows.yml b/.github/workflows/test-reusable-workflows.yml index 76f1b5e..4cc476a 100644 --- a/.github/workflows/test-reusable-workflows.yml +++ b/.github/workflows/test-reusable-workflows.yml @@ -51,7 +51,11 @@ jobs: upload-artifacts: true skip-sonar: true extra-commands: "node --version" + commitlint-config-file: .github/workflow-test-fixtures/commitlint.config.mjs secrets: + SONAR_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + test-ci-pnpm-node-assert-artifact: name: Test ci-pnpm-node (assert artifact) runs-on: ubuntu-latest needs: test-ci-pnpm-node From 029e68b1e18e7386772286146c835bc65f269009 Mon Sep 17 00:00:00 2001 From: Tuomas Haapala Date: Mon, 15 Jun 2026 10:11:09 +0300 Subject: [PATCH 6/6] chore: add CODEOWNERS file Refs: RATY-350 --- CODEOWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..c4670fb --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +* @City-of-Helsinki/django-react-ratkaisutoimisto +.github/workflow-test-fixtures/django-api/ @City-of-Helsinki/ratkaisutoimiston-backend +.github/workflow-test-fixtures/django-api-uv/ @City-of-Helsinki/ratkaisutoimiston-backend