diff --git a/.github/poutine.yml b/.github/poutine.yml new file mode 100644 index 0000000..e35805b --- /dev/null +++ b/.github/poutine.yml @@ -0,0 +1,4 @@ +skip: + # Every third-party action is pinned to a full commit SHA, which is what protects against + # a compromised upstream; whether the publisher is a "verified creator" adds nothing on top. + - rule: github_action_from_unverified_creator_used diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ffbe3a..47365c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,6 +12,9 @@ concurrency: group: build-${{ github.head_ref || github.run_id }} # will be canceled on subsequent pushes in pull requests but not branches cancel-in-progress: true +permissions: + contents: read + jobs: lint: name: "Lint" @@ -37,6 +40,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -66,10 +71,13 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Checkout build-cs" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: "phpstan/build-cs" path: "build-cs" ref: "2.x" @@ -148,6 +156,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -156,7 +166,9 @@ jobs: php-version: "${{ matrix.php-version }}" - name: "Require specific PHPUnit version" - run: "composer require --dev phpunit/phpunit:${{ matrix.phpunit-version }}" + env: + PHPUNIT_VERSION: ${{ matrix.phpunit-version }} + run: composer require --dev "phpunit/phpunit:$PHPUNIT_VERSION" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} @@ -221,6 +233,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -231,7 +245,9 @@ jobs: tools: composer:v2 - name: "Require specific PHPUnit version" - run: "composer require --dev phpunit/phpunit:${{ matrix.phpunit-version }}" + env: + PHPUNIT_VERSION: ${{ matrix.phpunit-version }} + run: composer require --dev "phpunit/phpunit:$PHPUNIT_VERSION" - name: "Install lowest dependencies" if: ${{ matrix.dependencies == 'lowest' }} @@ -266,15 +282,19 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Checkout build-infection" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: + persist-credentials: false repository: "phpstan/build-infection" path: "build-infection" ref: "1.x" - - uses: ./build-infection/.github/actions/setup-php + # The action comes from the phpstan/build-infection checkout above, which the $/ self-repository syntax cannot load + - uses: ./build-infection/.github/actions/setup-php # zizmor: ignore[self-repository] with: php-version: "${{ matrix.php-version }}" php-extensions: mbstring @@ -334,6 +354,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # v2 @@ -341,7 +363,7 @@ jobs: coverage: "none" php-version: "8.5" - - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 + - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # 4.0.0 - name: "Name Collision Detector" run: "make name-collision" diff --git a/.github/workflows/create-tag.yml b/.github/workflows/create-tag.yml index 2abdb94..66d3e29 100644 --- a/.github/workflows/create-tag.yml +++ b/.github/workflows/create-tag.yml @@ -15,6 +15,13 @@ on: - patch - minor +concurrency: + group: create-tag + cancel-in-progress: true + +permissions: + contents: read + jobs: create-tag: name: "Create tag" @@ -25,7 +32,8 @@ jobs: with: egress-policy: audit - - name: "Checkout" + # Credentials must stay persisted: rickstaa/action-create-tag pushes the tag with them + - name: "Checkout" # zizmor: ignore[artipacked] uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index b98d3a4..ac5010a 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -41,6 +41,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: "Install PHP" uses: "shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962" # 2.40.0 @@ -48,7 +50,7 @@ jobs: coverage: "none" php-version: "8.3" - - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # v4.0.0 + - uses: "ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda" # 4.0.0 - name: "Install bashunit" uses: "TypedDevs/bashunit@8d4f357f52df7596f92ece2d6c049d91d45517a2" # 0.52.0 @@ -56,4 +58,4 @@ jobs: directory: "e2e" - name: "Test" - run: ${{ matrix.script }} + run: ${{ matrix.script }} # zizmor: ignore[template-injection] the script is defined by the matrix above diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml index 02ad534..8520319 100644 --- a/.github/workflows/lint-workflows.yml +++ b/.github/workflows/lint-workflows.yml @@ -8,10 +8,15 @@ on: branches: - "2.1.x" +concurrency: + group: lint-workflows-${{ github.head_ref || github.run_id }} # will be canceled on subsequent pushes in pull requests but not branches + cancel-in-progress: true + permissions: {} jobs: actionlint: + name: actionlint runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) @@ -20,10 +25,18 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Check workflow files + env: + ACTIONLINT_VERSION: "1.7.12" + ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" run: | echo "::add-matcher::.github/actionlint-matcher.json" - bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) + curl -fsSL -o actionlint.tar.gz "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + echo "${ACTIONLINT_SHA256} actionlint.tar.gz" | sha256sum -c - + tar -xzf actionlint.tar.gz actionlint ./actionlint -color shell: bash @@ -40,11 +53,17 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Run octoscan id: octoscan uses: synacktiv/action-octoscan@6b1cf2343893dfb9e5f75652388bd2dc83f456b0 # v1.0.0 with: filter_triggers: '' + # The only custom-ref checkouts are phpstan/build-cs and phpstan/build-infection at fixed branches, + # and the only local action comes from the phpstan/build-infection checkout. + ignore: "Use of 'actions/checkout' with a custom ref|Use of local action .\\./build-infection/" - name: Upload SARIF file to GitHub uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 2a41b7b..0df897b 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -4,11 +4,16 @@ on: schedule: - cron: '7 0 * * *' +concurrency: + group: lock-closed-issues + cancel-in-progress: true + permissions: contents: read jobs: lock: + name: "Lock closed issues" permissions: issues: write # for dessant/lock-threads to lock issues runs-on: ubuntu-latest diff --git a/.github/workflows/release-toot.yml b/.github/workflows/release-toot.yml index 4e8a264..0533729 100644 --- a/.github/workflows/release-toot.yml +++ b/.github/workflows/release-toot.yml @@ -6,8 +6,15 @@ on: release: types: [published] +concurrency: + group: toot-${{ github.event.release.tag_name }} + cancel-in-progress: true + +permissions: {} + jobs: toot: + name: "Toot release" runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) diff --git a/.github/workflows/release-tweet.yml b/.github/workflows/release-tweet.yml index eb7cd95..003b399 100644 --- a/.github/workflows/release-tweet.yml +++ b/.github/workflows/release-tweet.yml @@ -6,8 +6,15 @@ on: release: types: [published] +concurrency: + group: tweet-${{ github.event.release.tag_name }} + cancel-in-progress: true + +permissions: {} + jobs: tweet: + name: "Tweet release" runs-on: ubuntu-latest steps: - name: Harden the runner (Audit all outbound calls) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8ea4ed..1da62b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,13 @@ on: tags: - '*' +concurrency: + group: release-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: deploy: name: "Deploy" @@ -20,6 +27,8 @@ jobs: - name: "Checkout" uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Generate changelog id: changelog @@ -27,7 +36,7 @@ jobs: with: myToken: ${{ secrets.PHPSTAN_BOT_TOKEN }} - - name: "Create release" + - name: "Create release" # zizmor: ignore[superfluous-actions] uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 with: token: ${{ secrets.PHPSTAN_BOT_TOKEN }} diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..0a8011d --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,11 @@ +rules: + secrets-outside-env: + config: + # Repository-level secrets used by release automation; they are not scoped to deployment environments. + allow: + - PHPSTAN_BOT_TOKEN + - MASTODON_ACCESS_TOKEN + - TWITTER_CONSUMER_API_KEY + - TWITTER_CONSUMER_API_SECRET + - TWITTER_ACCESS_TOKEN + - TWITTER_ACCESS_TOKEN_SECRET