diff --git a/.github/workflows/frontend.yaml b/.github/workflows/frontend.yaml new file mode 100644 index 00000000..7541dd48 --- /dev/null +++ b/.github/workflows/frontend.yaml @@ -0,0 +1,87 @@ +--- +name: Frontend + +# yamllint disable-line rule:truthy +on: + push: + branches: [main] + paths: + - "**.js" + - "**.ts" + - "ts/**" + - ".eslintrc.cjs" + - "package.json" + - "rollup.config.js" + - "tsconfig.json" + - "vitest.config.ts" + - ".github/workflows/frontend.yaml" + pull_request: + branches: [main] + paths: + - "**.js" + - "**.ts" + - "ts/**" + - ".eslintrc.cjs" + - "package.json" + - "rollup.config.js" + - "tsconfig.json" + - "vitest.config.ts" + - ".github/workflows/frontend.yaml" + +jobs: + lint-and-build: + # pre-commit.ci skips yarn-lint and yarn-build (Node.js not available), so CI needs this job + name: Yarn Lint and Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 2 + - name: Set up Node + uses: actions/setup-node@v6 + with: + node-version: lts/iron + - name: Install dependencies + run: yarn install + - name: Lint + run: yarn lint:fix + - name: Check for changes from lint + id: change_lint + run: echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT + - name: Check if clean from lint + if: steps.change_lint.outputs.changed != 0 + uses: actions/github-script@v8 + with: + script: | + core.setFailed('Repo is dirty after lint! Run yarn lint:fix locally before pushing changes.') + - name: Build + run: yarn build + - name: Check for changes from build + id: change_build + run: echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT + - name: Check if clean from build + if: steps.change_build.outputs.changed != 0 + uses: actions/github-script@v8 + with: + script: | + core.setFailed('Repo is dirty after build! Run yarn build locally before pushing changes.') + + test: + name: Vitest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up Node + uses: actions/setup-node@v6 + with: + node-version: lts/iron + - name: Install dependencies + run: yarn install + - name: Run tests + run: yarn test --coverage --coverage.reporter=json + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: typescript + files: ./coverage/coverage-final.json diff --git a/.github/workflows/hacs.yaml b/.github/workflows/hacs.yaml deleted file mode 100644 index 69a039c3..00000000 --- a/.github/workflows/hacs.yaml +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: "HACS" -# yamllint disable-line rule:truthy -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - validate_hacs: - name: "HACS Validation" - runs-on: ubuntu-latest - steps: - - uses: "actions/checkout@v6" - - name: HACS Action - uses: "hacs/action@main" - with: - category: "integration" diff --git a/.github/workflows/hassfest.yaml b/.github/workflows/hassfest.yaml deleted file mode 100644 index 274d2856..00000000 --- a/.github/workflows/hassfest.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: "Hassfest" -# yamllint disable-line rule:truthy -on: - push: - branches: [main] - pull_request: - branches: [main] - -jobs: - validate_hassfest: - name: "Hassfest Validation" - runs-on: "ubuntu-latest" - steps: - - uses: "actions/checkout@v6" - - uses: home-assistant/actions/hassfest@master diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml new file mode 100644 index 00000000..c0ac0e23 --- /dev/null +++ b/.github/workflows/integration.yaml @@ -0,0 +1,66 @@ +--- +name: Integration + +# yamllint disable-line rule:truthy +on: + push: + branches: [main] + paths: + - "**.py" + - ".github/workflows/integration.yaml" + pull_request: + branches: [main] + paths: + - "**.py" + - "requirements*.txt" + - "pyproject.toml" + - ".github/workflows/integration.yaml" + +jobs: + test: + name: Pytest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.13" + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install libudev-dev + - name: Install dependencies + run: uv pip install --system -r requirements_dev.txt + - name: Run tests and generate coverage report + # CI-only deps installed separately to keep requirements_dev.txt lightweight + run: | + uv pip install --system pytest-cov pytest-github-actions-annotate-failures + pytest ./tests/ --cov=custom_components/lock_code_manager/ --cov-report=xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: python + files: coverage.xml + + hacs: + name: HACS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: HACS Action + uses: hacs/action@main + with: + category: integration + + hassfest: + name: Hassfest + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: home-assistant/actions/hassfest@master diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml deleted file mode 100644 index dc9a3b39..00000000 --- a/.github/workflows/pytest.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Pytest - -# yamllint disable-line rule:truthy -on: - push: - branches: [main] - paths: - - "**.py" - pull_request: - branches: [main] - paths: - - "**.py" - - "requirements*.txt" - - "pyproject.toml" - -jobs: - test: - name: Python ${{ matrix.python-version }} Test - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.13"] - - steps: - - uses: actions/checkout@v6 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt-get update - sudo apt-get install libudev-dev - python -m pip install --upgrade pip - pip install -r requirements_dev.txt - - name: Run tests and generate coverage report - run: | - pip install pytest-cov pytest-github-actions-annotate-failures - pytest ./tests/ --cov=custom_components/lock_code_manager/ --cov-report=xml - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: python - files: coverage.xml diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml deleted file mode 100644 index 19258fde..00000000 --- a/.github/workflows/release-drafter.yaml +++ /dev/null @@ -1,52 +0,0 @@ ---- -name: Release Drafter - -# yamllint disable-line rule:truthy -on: - push: - branches: - - main - pull_request: - types: - - labeled - - opened - - reopened - - synchronize - - unlabeled - # pull_request_target is needed for fork PRs - it runs in the base repo context - # with elevated permissions required for pull-requests: write - pull_request_target: - types: - - labeled - - opened - - reopened - - synchronize - - unlabeled - workflow_dispatch: - -permissions: - contents: read - -concurrency: - # Use head_ref for PRs (branch name) or ref for pushes (refs/heads/main) - # This ensures PR updates are serialized while different PRs run concurrently - group: release-drafter-${{ github.head_ref || github.ref }} - cancel-in-progress: false - -jobs: - update_release_draft: - name: Update release draft - permissions: - contents: write - pull-requests: write - runs-on: ubuntu-latest - steps: - # Give GitHub API time to index newly merged PRs before querying - - name: Wait for PR indexing - if: github.event_name == 'push' - run: sleep 10 - - uses: release-drafter/release-drafter@v6 - with: - commitish: main - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/auto-merge.yaml b/.github/workflows/repository.yaml similarity index 50% rename from .github/workflows/auto-merge.yaml rename to .github/workflows/repository.yaml index e064019f..134cb0d0 100644 --- a/.github/workflows/auto-merge.yaml +++ b/.github/workflows/repository.yaml @@ -1,27 +1,72 @@ --- -name: auto-merge +name: Repository -"on": +# yamllint disable-line rule:truthy +on: + push: + branches: [main] pull_request: - types: [opened, synchronize, reopened, labeled, unlabeled] + types: + - labeled + - opened + - reopened + - synchronize + - unlabeled + # pull_request_target is needed for fork PRs - it runs in the base repo context + # with elevated permissions required for pull-requests: write + pull_request_target: + types: + - labeled + - opened + - reopened + - synchronize + - unlabeled + workflow_dispatch: permissions: - contents: write - pull-requests: write - -# Cancel in-progress runs for the same PR (only latest should merge) -concurrency: - group: auto-merge-${{ github.event.pull_request.number }} - cancel-in-progress: true + contents: read jobs: + update-release-draft: + name: Update Release Draft + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + concurrency: + # Use head_ref for PRs (branch name) or ref for pushes (refs/heads/main) + # This ensures PR updates are serialized while different PRs run concurrently + group: release-drafter-${{ github.head_ref || github.ref }} + cancel-in-progress: false + steps: + # Give GitHub API time to index newly merged PRs before querying + - name: Wait for PR indexing + if: github.event_name == 'push' + run: sleep 10 + - uses: release-drafter/release-drafter@v6 + with: + commitish: main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + auto-merge: + name: Auto Merge runs-on: ubuntu-latest - # Run for dependabot, pre-commit-ci, or PRs with auto-merge label + # Only run on pull_request events (not push, pull_request_target, or workflow_dispatch) + # and only for dependabot, pre-commit-ci, or PRs with auto-merge label if: >- - github.event.pull_request.user.login == 'dependabot[bot]' || - github.event.pull_request.user.login == 'pre-commit-ci[bot]' || - contains(github.event.pull_request.labels.*.name, 'auto-merge') + github.event_name == 'pull_request' && ( + github.event.pull_request.user.login == 'dependabot[bot]' || + github.event.pull_request.user.login == 'pre-commit-ci[bot]' || + contains(github.event.pull_request.labels.*.name, 'auto-merge') + ) + permissions: + contents: write + pull-requests: write + concurrency: + # Cancel in-progress runs for the same PR (only latest should merge) + group: auto-merge-${{ github.event.pull_request.number }} + cancel-in-progress: true steps: # Fetch Dependabot metadata (only runs for dependabot PRs) - name: Fetch Dependabot metadata diff --git a/.github/workflows/vitest.yaml b/.github/workflows/vitest.yaml deleted file mode 100644 index f0b36bfd..00000000 --- a/.github/workflows/vitest.yaml +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: Vitest - -# yamllint disable-line rule:truthy -on: - push: - branches: [main] - paths: - - "**.ts" - - "ts/**" - - "package.json" - - "vitest.config.ts" - pull_request: - branches: [main] - paths: - - "**.ts" - - "ts/**" - - "package.json" - - "vitest.config.ts" - -jobs: - test: - name: TypeScript Test - runs-on: ubuntu-latest - strategy: - matrix: - node-version: - - "lts/iron" - - steps: - - uses: actions/checkout@v6 - - name: Set up Node ${{ matrix.node-version }} - uses: actions/setup-node@v6 - with: - node-version: ${{ matrix.node-version }} - - name: Install dependencies - run: yarn install - - name: Run tests and generate coverage report - run: yarn test --coverage --coverage.reporter=json - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 - with: - token: ${{ secrets.CODECOV_TOKEN }} - flags: typescript - files: ./coverage/coverage-final.json diff --git a/.github/workflows/yarn.yaml b/.github/workflows/yarn.yaml deleted file mode 100644 index 63617841..00000000 --- a/.github/workflows/yarn.yaml +++ /dev/null @@ -1,70 +0,0 @@ ---- -name: Yarn lint and build - -on: - push: - branches: [main] - paths: - - "**.js" - - "**.ts" - - "ts/**" - - ".eslintrc.cjs" - - "package.json" - - "rollup.config.js" - - "tsconfig.json" - pull_request: - branches: [main] - paths: - - "**.js" - - "**.ts" - - "ts/**" - - ".eslintrc.cjs" - - "package.json" - - "rollup.config.js" - - "tsconfig.json" - -jobs: - yarn: - name: Yarn lint and build - runs-on: ubuntu-latest - strategy: - matrix: - node-version: - - "lts/iron" - - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 2 - - name: Set up Node ${{ matrix.node-version }} - uses: actions/setup-node@v6 - with: - node-version: ${{ matrix.node-version }} - - name: Install - run: yarn install - - name: Lint - run: | - yarn lint:fix - - name: Check for changes from lint - id: change_lint - # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files - run: echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT - - name: Check if clean from lint - if: steps.change_lint.outputs.changed != 0 - uses: actions/github-script@v8 - with: - script: | - core.setFailed('Repo is dirty after lint! Run yarn lint:fix locally before pushing changes.') - - name: Build - run: | - yarn build - - name: Check for changes from build - id: change_build - # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files - run: echo "changed=$(git status --porcelain | wc -l)" >> $GITHUB_OUTPUT - - name: Check if clean from build - if: steps.change_build.outputs.changed != 0 - uses: actions/github-script@v8 - with: - script: | - core.setFailed('Repo is dirty after build! Run yarn build locally before pushing changes.') diff --git a/.gitignore b/.gitignore index 8ade202c..3a546303 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ coverage/ .mypy_cache .ruff_cache .uv-cache +uv.lock coverage htmlcov node_modules diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 273e348f..75b53b0a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.11 hooks: - - id: ruff + - id: ruff-check files: ^(scripts|tests|custom_components)/.+\.py$ args: [--fix, --exit-non-zero-on-fix] - id: ruff-format diff --git a/codecov.yml b/codecov.yml index e7fe6d46..6ca413b8 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,11 +7,13 @@ coverage: paths: - custom_components/ - tests/ + threshold: 1% typescript: flags: - typescript paths: - ts/ + threshold: 1% patch: python: flags: diff --git a/pyproject.toml b/pyproject.toml index b995eda4..785ee3ba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,8 @@ +[project] +name = "lock-code-manager" +version = "0.0.0" +requires-python = ">=3.13" + [tool.ruff] lint.select = ["D", "E", "F", "G", "I", "PLC", "PLE", "PLR", "PLW", "UP", "W"] lint.ignore = [ diff --git a/tests/conftest.py b/tests/conftest.py index 571544cf..d7364bb0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -82,7 +82,7 @@ class MockFlow(ConfigFlow): @pytest.fixture(name="mock_config_flow") -def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]: +def config_flow_fixture(hass: HomeAssistant) -> Generator[None]: """Mock config flow.""" mock_platform(hass, f"{TEST_DOMAIN}.config_flow") diff --git a/uv.lock b/uv.lock deleted file mode 100644 index bda02073..00000000 --- a/uv.lock +++ /dev/null @@ -1,3 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.13"