diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f329996 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,29 @@ +# https://EditorConfig.org + +root = true + +[*] +indent_style = space +# Reduce tab size on GitHub +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +block_comment_start = /* +block_comment = * +block_comment_end = */ + +[{*.yml,*.yaml}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab + +# Generated file +[infection.txt] +indent_size = unset +trim_trailing_whitespace = unset diff --git a/.gitattributes b/.gitattributes index cea09fc..de25e8f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,14 +3,14 @@ /.github export-ignore /doc export-ignore /tests export-ignore +/.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore /CODE_OF_CONDUCT.md export-ignore +/deptrac.yaml export-ignore /ecs.php export-ignore /infection.json.dist export-ignore /Makefile export-ignore /phpstan.neon export-ignore /phpunit.xml.dist export-ignore -/README.md export-ignore /rector.php export-ignore -/SECURITY.md export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2b7c6aa..7cc67ce 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -11,9 +11,14 @@ Few rules to ease code reviews and merges: - You MUST write (or update) unit tests when bugs are fixed or features are added. - You SHOULD write documentation. -To contribute use [Pull Requests](https://help.github.com/articles/using-pull-requests), please, write commit messages that make sense, and rebase your branch before submitting your PR. +We use [Git-Flow](http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/) to automate our git branching +workflow. -May be asked to squash your commits too. This is used to "clean" your Pull Request before merging it, avoiding commits such as fix tests, fix 2, fix 3, etc. +To contribute use [Pull Requests](https://help.github.com/articles/using-pull-requests), please, write commit messages +that make sense, and rebase your branch before submitting your PR. + +May be asked to squash your commits too. This is used to "clean" your Pull Request before merging it, avoiding commits +such as fix tests, fix 2, fix 3, etc. Run test suite ------------ @@ -21,7 +26,3 @@ Run test suite * install composer: `curl -s http://getcomposer.org/installer | php` * install dependencies: `php composer.phar install` * run tests: `vendor/bin/phpunit` -* check and fix coding standards: - * `vendor/bin/phpstan analyse` - * `vendor/bin/rector process` - * `vendor/bin/ecs check --fix` diff --git a/.github/ISSUE_TEMPLATE/1_Bug_report.yaml b/.github/ISSUE_TEMPLATE/1_Bug_report.yaml index 841cfdc..d3f26cf 100644 --- a/.github/ISSUE_TEMPLATE/1_Bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/1_Bug_report.yaml @@ -1,43 +1,42 @@ name: 🐛 Bug Report description: ⚠️ NEVER report security issues, email security AT spomky-labs.com instead labels: Bug - body: - - type: input - id: affected-versions - attributes: - label: Version(s) affected - placeholder: x.y.z - validations: - required: true - - type: textarea - id: description - attributes: - label: Description - description: A clear and concise description of the problem - validations: - required: true - - type: textarea - id: how-to-reproduce - attributes: - label: How to reproduce - description: | - ⚠️ This is the most important part of the report ⚠️ - Without a way to easily reproduce your issue, there is little chance we will be able to help you and work on a fix. - Please, take the time to show us some code and/or config that is needed for others to reproduce the problem easily. - Most of the time, creating a "bug reproducer" is the best way to help us and increases the chances someone - will have a look at it. - validations: - required: true - - type: textarea - id: possible-solution - attributes: - label: Possible Solution - description: | - Optional: only if you have suggestions on a fix/reason for the bug - Don't hesitate to create a pull request with your solution, it helps get faster feedback. - - type: textarea - id: additional-context - attributes: - label: Additional Context - description: "Optional: any other context about the problem: log messages, screenshots, etc." + - type: input + id: affected-versions + attributes: + label: Version(s) affected + placeholder: x.y.z + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the problem + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to reproduce + description: | + ⚠️ This is the most important part of the report ⚠️ + Without a way to easily reproduce your issue, there is little chance we will be able to help you and work on a fix. + Please, take the time to show us some code and/or config that is needed for others to reproduce the problem easily. + Most of the time, creating a "bug reproducer" is the best way to help us and increases the chances someone + will have a look at it. + validations: + required: true + - type: textarea + id: possible-solution + attributes: + label: Possible Solution + description: | + Optional: only if you have suggestions on a fix/reason for the bug + Don't hesitate to create a pull request with your solution, it helps get faster feedback. + - type: textarea + id: additional-context + attributes: + label: Additional Context + description: "Optional: any other context about the problem: log messages, screenshots, etc." diff --git a/.github/ISSUE_TEMPLATE/2_Feature_request.yaml b/.github/ISSUE_TEMPLATE/2_Feature_request.yaml index bd300eb..52b3de7 100644 --- a/.github/ISSUE_TEMPLATE/2_Feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/2_Feature_request.yaml @@ -1,17 +1,17 @@ name: 🚀 Feature Request description: RFC and ideas for new features and improvements body: - - type: textarea - id: description - attributes: - label: Description - description: A clear and concise description of the new feature - validations: - required: true - - type: textarea - id: example - attributes: - label: Example - description: | - A simple example of the new feature in action (include PHP code, YAML config, etc.) - If the new feature changes an existing feature, include a simple before/after comparison. + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the new feature + validations: + required: true + - type: textarea + id: example + attributes: + label: Example + description: | + A simple example of the new feature in action (include PHP code, YAML config, etc.) + If the new feature changes an existing feature, include a simple before/after comparison. diff --git a/.github/ISSUE_TEMPLATE/3_Documentation.yaml b/.github/ISSUE_TEMPLATE/3_Documentation.yaml index 7a3dc17..1c5ac89 100644 --- a/.github/ISSUE_TEMPLATE/3_Documentation.yaml +++ b/.github/ISSUE_TEMPLATE/3_Documentation.yaml @@ -1,10 +1,10 @@ name: 📖 Documentation Issue description: To report typo or obsolete section in the documentation body: - - type: textarea - id: description - attributes: - label: Description - description: A clear and concise description of the error you found in the documentation - validations: - required: true + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the error you found in the documentation + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1181241..6d11ce2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: - - name: Support Question - url: https://spomky-labs.com/contact/ - about: We use GitHub issues only to discuss about bugs and new features. For this kind of questions about using the library, please use Stackoverflow (or similar) or send a quote request at https://spomky-labs.com/contact/ + - name: Support Question + url: https://spomky-labs.com/contact/ + about:| + We use GitHub issues only to discuss about bugs and new features. + For this kind of questions about using the framework or third-party bundles, + please email us contact AT spomky-labs.com for quoting diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1beb0a3..029817b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,19 +1,17 @@ -| Q | A -| ------------- | --- -| Branch? | -| Bug fix? | yes/no -| New feature? | yes/no -| Deprecations? | yes/no -| Tickets | Fix #... -| License | MIT +Target branch: +Resolves issue # + + +- [ ] It is a Bug fix +- [ ] It is a New feature +- [ ] Breaks BC +- [ ] Includes Deprecations + \ No newline at end of file +Please consider the following requirement: +* Modification of existing tests should be avoided unless deemed necessary. +* You MUST never open a PR related to a security issue. Contact Spomky in private at https://gitter.im/Spomky/ +--> diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 75436e4..ae2faa4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,19 @@ version: 2 updates: -- package-ecosystem: composer - directory: "/" - schedule: - interval: daily - time: "11:00" - open-pull-requests-limit: 10 + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + day: "friday" + versioning-strategy: "widen" + open-pull-requests-limit: 20 + allow: + - dependency-type: all + labels: [ "Dependencies" ] + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 20 + labels: [ "Dependencies" ] diff --git a/.github/stale.yml b/.github/stale.yml index 98284be..19367a6 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,8 +1,8 @@ -daysUntilStale: 60 +daysUntilStale: 30 daysUntilClose: 7 staleLabel: wontfix markComment: > - Ce problème a été automatiquement marqué comme périmé car il n'a pas eu - d’activité récente. Il sera fermé dans 7 jours si aucune autre activité ne se produit. Merci - pour votre contribution. + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. closeComment: false diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml deleted file mode 100644 index 584b524..0000000 --- a/.github/workflows/coding-standards.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Coding Standards - -on: [push] - -jobs: - tests: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: [ubuntu-latest] - php-versions: ['8.1'] - name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring - coverage: xdebug - - - name: Install the application - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: Coding Standards Checks - run: make ci-cs diff --git a/.github/workflows/gitsplit.yml b/.github/workflows/gitsplit.yml new file mode 100644 index 0000000..0677186 --- /dev/null +++ b/.github/workflows/gitsplit.yml @@ -0,0 +1,18 @@ +name: gitsplit +on: + push: + tags: + - '*' + release: + types: [ published ] + +jobs: + gitsplit: + runs-on: ubuntu-latest + steps: + - name: checkout + run: git clone https://github.com/web-auth/webauthn-framework /home/runner/work/web-auth/webauthn-framework && cd /home/runner/work/web-auth/webauthn-framework + - name: Split repositories + run: docker run --rm -t -e GH_TOKEN -v /cache/gitsplit:/cache/gitsplit -v /home/runner/work/web-auth/webauthn-framework:/srv jderusse/gitsplit gitsplit + env: + GH_TOKEN: ${{ secrets.GITSPLIT_TOKEN }} diff --git a/.github/workflows/integrate.yml b/.github/workflows/integrate.yml new file mode 100644 index 0000000..5578d06 --- /dev/null +++ b/.github/workflows/integrate.yml @@ -0,0 +1,230 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow + +name: "Integrate" + +on: + push: + branches: + - "*.x" + pull_request: null + +jobs: + byte_level: + name: "0️⃣ Byte-level" + runs-on: "ubuntu-latest" + steps: + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Check file permissions" + run: | + test "$(find . -type f -not -path './.git/*' -executable)" == "" + + - name: "Find non-printable ASCII characters" + run: | + ! LC_ALL=C.UTF-8 find . -type f -name "*.php" -print0 | xargs -0 -- grep -PHn "[^ -~]" + + syntax_errors: + name: "1️⃣ Syntax errors" + runs-on: "ubuntu-latest" + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + coverage: "none" + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Install dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + + - name: "Check source code for syntax errors" + run: "composer exec -- parallel-lint src/ tests/" + + unit_tests: + name: "2️⃣ Unit and functional tests" + needs: + - "byte_level" + - "syntax_errors" + strategy: + matrix: + operating-system: + - "ubuntu-latest" + php-version: + - "8.1" + dependencies: + - "lowest" + - "highest" + runs-on: ${{ matrix.operating-system }} + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + extensions: "mbstring" + coverage: "xdebug" + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Install dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "${{ matrix.dependencies }}" + composer-options: "--optimize-autoloader" + + - name: "Execute tests (PHP)" + run: "make ci-cc" + + # - name: Send coverage to Coveralls + # if: "matrix.php-version == '8.1' && matrix.dependencies == 'highest'" + # env: + # COVERALLS_REPO_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + # run: | + # wget "https://github.com/php-coveralls/php-coveralls/releases/download/v2.5.2/php-coveralls.phar" + # php ./php-coveralls.phar -v + + static_analysis: + name: "3️⃣ Static Analysis" + needs: + - "byte_level" + - "syntax_errors" + runs-on: "ubuntu-latest" + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + extensions: "mbstring" + coverage: "none" + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Validate Composer configuration" + run: "composer validate --strict" + + - name: "Install dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + composer-options: "--optimize-autoloader" + + - name: "Check PSR-4 mapping" + run: "composer dump-autoload --optimize --strict-psr" + + - name: "Execute static analysis" + run: "make st" + + coding_standards: + name: "4️⃣ Coding Standards" + needs: + - "byte_level" + - "syntax_errors" + runs-on: "ubuntu-latest" + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + extensions: "mbstring" + coverage: "none" + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Check adherence to EditorConfig" + uses: "greut/eclint-action@v0" + + - name: "Install dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + composer-options: "--optimize-autoloader" + + - name: "Check coding style" + run: "make ci-cs" + + - name: "Deptrac" + run: | + vendor/bin/deptrac analyse --fail-on-uncovered --no-cache + + mutation_testing: + name: "5️⃣ Mutation Testing" + needs: + - "byte_level" + - "syntax_errors" + runs-on: "ubuntu-latest" + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + extensions: "mbstring" + coverage: "xdebug" + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Fetch Git base reference" + run: "git fetch --depth=1 origin ${GITHUB_BASE_REF}" + + - name: "Install dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + composer-options: "--optimize-autoloader" + + - name: "Execute Infection" + run: "make ci-mu" + + rector_checkstyle: + name: "6️⃣ Rector Checkstyle" + needs: + - "byte_level" + - "syntax_errors" + runs-on: "ubuntu-latest" + steps: + - name: "Set up PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.1" + extensions: "mbstring" + coverage: "xdebug" + + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Fetch Git base reference" + run: "git fetch --depth=1 origin ${GITHUB_BASE_REF}" + + - name: "Install dependencies" + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + composer-options: "--optimize-autoloader" + + - name: "Execute Rector" + run: "make rector" + + exported_files: + name: "7️⃣ Exported files" + needs: + - "byte_level" + - "syntax_errors" + runs-on: "ubuntu-20.04" + steps: + - name: "Checkout code" + uses: "actions/checkout@v3" + + - name: "Check exported files" + run: | + EXPECTED="LICENSE,README.md,SECURITY.md,composer.json" + CURRENT="$(git archive HEAD | tar --list --exclude="src" --exclude="src/*" | paste -s -d ",")" + echo "CURRENT =${CURRENT}" + echo "EXPECTED=${EXPECTED}" + test "${CURRENT}" == "${EXPECTED}" diff --git a/.github/workflows/merge-me.yml b/.github/workflows/merge-me.yml new file mode 100644 index 0000000..77e8f7d --- /dev/null +++ b/.github/workflows/merge-me.yml @@ -0,0 +1,28 @@ +name: Merge me! + +on: + check_suite: + types: + - completed + +jobs: + merge-me: + name: Merge me! + runs-on: ubuntu-latest + steps: + - name: Merge me! + uses: ridedott/merge-me-action@v2.10.19 + with: + # Depending on branch protection rules, a manually populated + # `GITHUB_TOKEN_WORKAROUND` environment variable with permissions to + # push to a protected branch must be used. This variable can have an + # arbitrary name, as an example, this repository uses + # `GITHUB_TOKEN_DOTTBOTT`. + # + # When using a custom token, it is recommended to leave the following + # comment for other developers to be aware of the reasoning behind it: + # + # This must be used as GitHub Actions token does not support + # pushing to protected branches. + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + MERGE_METHOD: MERGE diff --git a/.github/workflows/mutation-tests.yml b/.github/workflows/mutation-tests.yml deleted file mode 100644 index 2277a98..0000000 --- a/.github/workflows/mutation-tests.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Mutation Testing - -on: [push] - -jobs: - tests: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: [ubuntu-latest] - php-versions: ['8.1'] - name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring - coverage: xdebug - - - name: Install the application - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: Fetch Git base reference - run: git fetch --depth=1 origin $GITHUB_BASE_REF - - - name: Infection - run: make ci-mu diff --git a/.github/workflows/rector_checkstyle.yaml b/.github/workflows/rector_checkstyle.yaml deleted file mode 100644 index 53f8d36..0000000 --- a/.github/workflows/rector_checkstyle.yaml +++ /dev/null @@ -1,30 +0,0 @@ -name: Rector Checkstyle - -on: [push] - -jobs: - tests: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: [ ubuntu-latest ] - php-versions: ['8.1'] - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring - coverage: none - - - name: Install the application - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: Rector - run: make ci-rector diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml new file mode 100644 index 0000000..babdc09 --- /dev/null +++ b/.github/workflows/release-on-milestone-closed.yml @@ -0,0 +1,72 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: "Automatic Releases" + +on: + milestone: + types: + - "closed" + +jobs: + release: + name: "GIT tag, release & create merge-up PR" + runs-on: ubuntu-latest + + steps: + - name: "Checkout" + uses: "actions/checkout@v3" + + - name: "Release" + uses: "laminas/automatic-releases@1.17.0" + with: + command-name: "laminas:automatic-releases:release" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create Merge-Up Pull Request" + uses: "laminas/automatic-releases@1.17.0" + with: + command-name: "laminas:automatic-releases:create-merge-up-pull-request" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create and/or Switch to new Release Branch" + uses: "laminas/automatic-releases@1.17.0" + with: + command-name: "laminas:automatic-releases:switch-default-branch-to-next-minor" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.ORGANIZATION_ADMIN_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Bump Changelog Version On Originating Release Branch" + uses: "laminas/automatic-releases@1.17.0" + with: + command-name: "laminas:automatic-releases:bump-changelog" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} + + - name: "Create new milestones" + uses: "laminas/automatic-releases@1.17.0" + with: + command-name: "laminas:automatic-releases:create-milestones" + env: + "SHELL_VERBOSITY": "3" + "GITHUB_TOKEN": ${{ secrets.GITHUB_TOKEN }} + "SIGNING_SECRET_KEY": ${{ secrets.SIGNING_SECRET_KEY }} + "GIT_AUTHOR_NAME": ${{ secrets.GIT_AUTHOR_NAME }} + "GIT_AUTHOR_EMAIL": ${{ secrets.GIT_AUTHOR_EMAIL }} diff --git a/.github/workflows/static-analyze.yml b/.github/workflows/static-analyze.yml deleted file mode 100644 index 91154b0..0000000 --- a/.github/workflows/static-analyze.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Static Analyze - -on: [push] - -jobs: - tests: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: [ubuntu-latest] - php-versions: ['8.1'] - name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring - coverage: xdebug - tools: cs2pr - - - name: Install the application - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: Static Analyze Checks - run: make ci-st diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index 71f18a4..0000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Unit and Functional Tests - -on: [push] - -jobs: - tests: - runs-on: ${{ matrix.operating-system }} - strategy: - matrix: - operating-system: [ ubuntu-latest ] - php-versions: ['8.1'] - name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} - - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - ref: ${{ github.head_ref }} - - - name: Setup PHP, with composer and extensions - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php-versions }} - extensions: mbstring - coverage: xdebug - - - name: Install the application - run: | - composer install --no-progress --prefer-dist --optimize-autoloader - - - name: Run tests - run: make all diff --git a/.github/workflows/tweet.yml b/.github/workflows/tweet.yml new file mode 100644 index 0000000..5f9acda --- /dev/null +++ b/.github/workflows/tweet.yml @@ -0,0 +1,23 @@ +name: tweet +on: + push: + tags: + - '*' + release: + types: [ published ] + +jobs: + tweet: + runs-on: ubuntu-latest + steps: + - name: Tweet + uses: snow-actions/tweet@v1.3.0 + with: + status: | + We are proud to announce that ${{ github.repository }} · ${{ github.event.release.name }} + ${{ github.event.release.html_url }} is now released 🚀. #php #totp #hotp + env: + CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }} + CONSUMER_API_SECRET_KEY: ${{ secrets.TWITTER_CONSUMER_API_SECRET }} + ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} + ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} diff --git a/.gitignore b/.gitignore index 9c4be60..757b917 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ /vendor/ .phpunit.result.cache /phpunit.xml -/infection.log \ No newline at end of file +/infection.log diff --git a/Makefile b/Makefile index 529b80e..159530a 100644 --- a/Makefile +++ b/Makefile @@ -1,63 +1,69 @@ ######################## -# CI/CD # +# Everyday # ######################## -ci-cs: vendor ## Check all files using defined rules (CI/CD) - vendor/bin/ecs check +.PHONY: mu +mu: vendor ## Mutation tests + vendor/bin/infection -s --threads=$$(nproc) --min-msi=30 --min-covered-msi=50 -ci-st: vendor ## Run static analyse (CI/CD) - vendor/bin/phpstan analyse --error-format=checkstyle | cs2pr +.PHONY: tests +tests: vendor ## Run all tests + vendor/bin/phpunit --color + yarn test -ci-rector: vendor ## Check all files using Rector (CI/CD) - vendor/bin/rector process --ansi --dry-run +.PHONY: cc +cc: vendor ## Show test coverage rates (HTML) + vendor/bin/phpunit --coverage-html ./build -ci-mu: vendor ## Mutation tests (CI/CD) - vendor/bin/infection --logger-github -s --threads=$$(nproc) --min-msi=70 --min-covered-msi=50 --test-framework-options="--exclude-group=Performance" +.PHONY: cs +cs: vendor ## Fix all files using defined ECS rules + vendor/bin/ecs check --fix -######################## -# Everyday # -######################## +.PHONY: tu +tu: vendor ## Run only unit tests + vendor/bin/phpunit --color --group Unit -all: vendor ## Run all tests - vendor/bin/phpunit --color +.PHONY: ti +ti: vendor ## Run only integration tests + vendor/bin/phpunit --color --group Integration -tu: vendor ## Run only unit tests - vendor/bin/phpunit --color tests +.PHONY: tf +tf: vendor ## Run only functional tests + vendor/bin/phpunit --color --group Functional +.PHONY: st st: vendor ## Run static analyse - vendor/bin/phpstan analyse + XDEBUG_MODE=off vendor/bin/phpstan analyse ######################## -# Every PR # +# CI/CD # ######################## -cs: vendor ## Fix all files using defined rules - vendor/bin/ecs check --fix +.PHONY: ci-mu +ci-mu: vendor ## Mutation tests (for CI/CD only) + vendor/bin/infection --logger-github -s --threads=$$(nproc) --min-msi=30 --min-covered-msi=50 -rector: vendor ## Check all files using Rector - vendor/bin/rector process +.PHONY: ci-cc +ci-cc: vendor ## Show test coverage rates (for CI/CD only) + vendor/bin/phpunit --coverage-text +.PHONY: ci-cs +ci-cs: vendor ## Check all files using defined ECS rules (for CI/CD only) + XDEBUG_MODE=off vendor/bin/ecs check ######################## # Others # ######################## -mu: vendor ## Mutation tests - vendor/bin/infection -s --threads=$$(nproc) --min-msi=70 --min-covered-msi=50 --test-framework-options="--exclude-group=Performance" - -cc: vendor ## Show test coverage rates (HTML) - vendor/bin/phpunit --coverage-html ./build +.PHONY: rector +rector: vendor ## Check all files using Rector + XDEBUG_MODE=off vendor/bin/rector process --ansi --dry-run --xdebug -vendor: composer.json composer.lock +vendor: composer.json composer validate composer install - -######################## -# Default # -######################## - .DEFAULT_GOAL := help help: @grep -E '(^[a-zA-Z_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/' diff --git a/composer.json b/composer.json index bff24fa..30db972 100644 --- a/composer.json +++ b/composer.json @@ -23,13 +23,15 @@ "require-dev": { "ekino/phpstan-banned-code": "^1.0", "infection/infection": "^0.26", + "php-parallel-lint/php-parallel-lint": "^1.3", "phpstan/phpstan": "^1.0", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^9.5.26", + "qossmic/deptrac-shim": "^1.0", "rector/rector": "^0.14", - "symfony/phpunit-bridge": "^6.0", + "symfony/phpunit-bridge": "^6.1", "symplify/easy-coding-standard": "^11.0" }, "autoload": { diff --git a/deptrac.yaml b/deptrac.yaml new file mode 100644 index 0000000..1986e15 --- /dev/null +++ b/deptrac.yaml @@ -0,0 +1,14 @@ +parameters: + paths: + - './src' + layers: + - name: 'OTP' + collectors: + - type: 'directory' + regex: 'src/.*' + - name: 'Vendors' + collectors: + - { type: className, regex: '^ParagonIE\\' } + ruleset: + OTP: + - 'Vendors' diff --git a/infection.json.dist b/infection.json.dist index f02cbf8..00a3453 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -9,8 +9,11 @@ }, "mutators": { "@default": true, - "global-ignoreSourceCodeByRegex": [ - "\\$this->logger.*" - ] + "MBString": { + "settings": { + "mb_substr": false, + "mb_strlen": false + } + } } -} \ No newline at end of file +} diff --git a/phpstan.neon b/phpstan.neon index e55b635..2ce3a2c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -4,10 +4,25 @@ parameters: - src - tests ignoreErrors: - - '#Variable property access on \$this\(OTPHP\\OTP\)\.#' - - '#^Method OTPHP\\OTP::generateSecret\(\) should return non-empty-string but returns string\.$#' + - + message: '#Variable property access on \$this\(OTPHP\\OTP\)\.#' + path: src/ParameterTrait.php + count: 1 + - + message: '#^Method OTPHP\\OTP::generateSecret\(\) should return non-empty-string but returns string\.$#' + path: src/OTP.php + count: 1 + - + message: '#^Cannot cast mixed to int\.$#' + path: src/HOTP.php + count: 1 + - + message: '#^Parameter \#\d .* of class OTPHP\\Url constructor expects .*\, .* given\.$#' + path: src/Url.php + count: 2 includes: + - vendor/phpstan/phpstan/conf/bleedingEdge.neon - vendor/phpstan/phpstan-strict-rules/rules.neon - vendor/phpstan/phpstan-phpunit/extension.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d4bd675..e52c02c 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,24 +1,28 @@ - - - - ./src - - - - - ./tests - - - - - - - OTPHP - OTPHP\TOTP - - - - - + + + + ./src + + + + + ./tests + + + + + + + OTPHP\TOTP + + + + diff --git a/src/HOTP.php b/src/HOTP.php index 8dc18d3..aa5a227 100644 --- a/src/HOTP.php +++ b/src/HOTP.php @@ -12,6 +12,8 @@ */ final class HOTP extends OTP implements HOTPInterface { + private const DEFAULT_WINDOW = 0; + public static function create( null|string $secret = null, int $counter = self::DEFAULT_COUNTER, @@ -86,10 +88,11 @@ public function setCounter(int $counter): void protected function getParameterMap(): array { return [...parent::getParameterMap(), ...[ - 'counter' => static function ($value): int { - (int) $value >= 0 || throw new InvalidArgumentException('Counter must be at least 0.'); + 'counter' => static function (mixed $value): int { + $value = (int) $value; + $value >= 0 || throw new InvalidArgumentException('Counter must be at least 0.'); - return (int) $value; + return $value; }, ]]; } @@ -101,7 +104,7 @@ private function updateCounter(int $counter): void private function getWindow(null|int $window): int { - return abs($window ?? 0); + return abs($window ?? self::DEFAULT_WINDOW); } private function verifyOtpWithWindow(string $otp, int $counter, null|int $window): bool diff --git a/src/OTP.php b/src/OTP.php index ba640a3..2cba067 100644 --- a/src/OTP.php +++ b/src/OTP.php @@ -17,6 +17,8 @@ abstract class OTP implements OTPInterface { use ParameterTrait; + private const DEFAULT_SECRET_SIZE = 64; + /** * @param non-empty-string $secret */ @@ -42,7 +44,7 @@ public function at(int $input): string */ final protected static function generateSecret(): string { - return Base32::encodeUpper(random_bytes(64)); + return Base32::encodeUpper(random_bytes(self::DEFAULT_SECRET_SIZE)); } /** diff --git a/src/ParameterTrait.php b/src/ParameterTrait.php index 80d3888..b050923 100644 --- a/src/ParameterTrait.php +++ b/src/ParameterTrait.php @@ -149,9 +149,7 @@ protected function getParameterMap(): array return $value; }, - 'secret' => static function ($value): string { - return mb_strtoupper(trim($value, '=')); - }, + 'secret' => static fn ($value): string => mb_strtoupper(trim((string) $value, '=')), 'algorithm' => static function ($value): string { $value = mb_strtolower($value); in_array($value, hash_algos(), true) || throw new InvalidArgumentException(sprintf( diff --git a/src/Url.php b/src/Url.php index 387d54f..56ad979 100644 --- a/src/Url.php +++ b/src/Url.php @@ -15,13 +15,13 @@ final class Url { /** * @param non-empty-string $secret + * @param array $query */ public function __construct( private readonly string $scheme, private readonly string $host, private readonly string $path, private readonly string $secret, - /** @var array $query */ private readonly array $query ) { } diff --git a/tests/TOTPTest.php b/tests/TOTPTest.php index 03691da..53d7284 100644 --- a/tests/TOTPTest.php +++ b/tests/TOTPTest.php @@ -164,9 +164,12 @@ public function wrongSizeOtp(): void */ public function generateOtpNow(): void { + ClockMock::register(TOTP::class); + $time = time(); + ClockMock::withClockMock($time); $otp = $this->createTOTP(6, 'sha1', 30); - static::assertSame($otp->now(), $otp->at(time())); + static::assertSame($otp->now(), $otp->at($time)); } /** @@ -174,7 +177,9 @@ public function generateOtpNow(): void */ public function verifyOtpNow(): void { + ClockMock::register(TOTP::class); $time = time(); + ClockMock::withClockMock($time); $otp = $this->createTOTP(6, 'sha1', 30); $totp = $otp->at($time); @@ -308,8 +313,12 @@ public function verifyOtpInWindow(int $timestamp, string $input, int $leeway, bo * @test * @dataProvider dataLeewayWithEpoch */ - public function verifyOtpWithEpochInWindow(int $timestamp, string $input, int $leeway, bool $expectedResult): void - { + public function verifyOtpWithEpochInWindow( + int $timestamp, + string $input, + int $leeway, + bool $expectedResult + ): void { ClockMock::register(TOTP::class); ClockMock::withClockMock($timestamp); $otp = $this->createTOTP(6, 'sha1', 30, 'JDDK4U6G3BJLEZ7Y', 'alice@foo.bar', 'My Project', 100);