Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
releases/ export-ignore
tests/ export-ignore
artifacts/ export-ignore
Formula/ export-ignore
build/ export-ignore
doc/ export-ignore

Expand Down
142 changes: 142 additions & 0 deletions .github/workflows/homebrew.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Homebrew

# Points the Homebrew formula at a newly promoted release.
#
# release.yml publishes every release as a pre-release, to be promoted by hand
# once the artifacts have been checked. This workflow listens to the "released"
# event, which GitHub fires when a release is published as a real release *or*
# when an existing pre-release is promoted to one. Pre-releases never trigger it,
# so `brew install phpmetrics` can never end up serving an rc.
#
# It does two things:
# 1. rewrites Formula/phpmetrics.rb on master, so the formula stays versioned
# and reviewable next to the code it installs;
# 2. mirrors that file to the PhpMetrics/homebrew-phpmetrics tap, which is what
# `brew tap phpmetrics/phpmetrics && brew install phpmetrics` actually reads.
# Homebrew only resolves formulae from repositories named homebrew-<name>,
# hence the separate repository.
#
# Step 2 needs a token allowed to write to *another* repository, which the
# default GITHUB_TOKEN is not. Create a fine-grained PAT (or a GitHub App token)
# with "Contents: read and write" on the tap, and store it as the
# HOMEBREW_TAP_TOKEN secret. Until that secret exists the job still updates the
# formula here and simply skips the mirroring, with a warning.
on:
release:
types: [released]
workflow_dispatch:
inputs:
version:
description: 'Version to point the formula at, format vX.Y.Z (e.g. v2.10.1). The release must already be published.'
required: true
type: string

jobs:
formula:
runs-on: ubuntu-latest
permissions:
contents: write
env:
TAP_REPOSITORY: PhpMetrics/homebrew-phpmetrics
steps:
- uses: actions/checkout@v4
with:
# The formula is committed back to master, whatever branch
# the workflow has been dispatched from.
ref: master

- name: Resolve version
id: resolve
env:
VERSION: ${{ inputs.version || github.event.release.tag_name }}
run: |
# Stable versions only. A tag carrying an alpha/beta/rc suffix
# is skipped rather than failed: promoting a release candidate
# by mistake should not turn the release red, it should just
# leave the formula where it is.
if ! echo "$VERSION" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
echo "::warning::$VERSION is not a stable version, leaving the Homebrew formula untouched"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "tag=$VERSION" >> "$GITHUB_OUTPUT"

- name: Checksum the released phar
id: phar
if: steps.resolve.outputs.skip == 'false'
env:
TAG: ${{ steps.resolve.outputs.tag }}
run: |
URL="https://github.com/PhpMetrics/PhpMetrics/releases/download/$TAG/phpmetrics.phar"
curl -fsSL -o phpmetrics.phar "$URL"
echo "url=$URL" >> "$GITHUB_OUTPUT"
echo "sha256=$(sha256sum phpmetrics.phar | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
rm -f phpmetrics.phar

- name: Update the formula
if: steps.resolve.outputs.skip == 'false'
env:
URL: ${{ steps.phar.outputs.url }}
SHA256: ${{ steps.phar.outputs.sha256 }}
run: |
# Only url and sha256: Homebrew derives the version from the
# tag in the url, and `brew audit` rejects a redundant
# `version` line.
sed -i -E \
-e "s|^ url \".*\"| url \"$URL\"|" \
-e "s|^ sha256 \".*\"| sha256 \"$SHA256\"|" \
Formula/phpmetrics.rb
grep -E '^ (url|sha256) ' Formula/phpmetrics.rb

# Advisory only: a Homebrew style rule changing upstream should not
# be able to block a release from reaching the tap.
- name: Check the formula style
if: steps.resolve.outputs.skip == 'false'
continue-on-error: true
run: brew style Formula/phpmetrics.rb

- name: Commit the formula
if: steps.resolve.outputs.skip == 'false'
env:
TAG: ${{ steps.resolve.outputs.tag }}
run: |
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
if git diff --quiet -- Formula/phpmetrics.rb; then
echo "Formula already up to date"
exit 0
fi
git add Formula/phpmetrics.rb
git commit -m "Point the Homebrew formula at $TAG"
# A release is promoted by hand, possibly long after the tag,
# so master may have moved since the checkout.
git pull --rebase origin master
git push origin HEAD:master

- name: Mirror the formula to the tap
if: steps.resolve.outputs.skip == 'false'
env:
TAG: ${{ steps.resolve.outputs.tag }}
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
if [ -z "$TAP_TOKEN" ]; then
echo "::warning::HOMEBREW_TAP_TOKEN is not set, skipping the push to $TAP_REPOSITORY. Create the tap repository and the secret to enable 'brew install phpmetrics'."
exit 0
fi
git clone --depth 1 "https://x-access-token:$TAP_TOKEN@github.com/$TAP_REPOSITORY.git" tap
mkdir -p tap/Formula
cp Formula/phpmetrics.rb tap/Formula/phpmetrics.rb
git -C tap config user.name 'github-actions[bot]'
git -C tap config user.email '41898282+github-actions[bot]@users.noreply.github.com'
# `git add` before comparing: on the very first run the file
# is untracked, and `git diff` alone would report no change.
git -C tap add Formula/phpmetrics.rb
if git -C tap diff --cached --quiet; then
echo "Tap already up to date"
exit 0
fi
git -C tap commit -m "phpmetrics $TAG"
# `push origin HEAD` rather than a bare `push`: it needs no
# upstream, so it still works on a freshly created tap.
git -C tap push origin HEAD
66 changes: 66 additions & 0 deletions Formula/phpmetrics.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Homebrew formula for PhpMetrics.
#
# This file is the source of truth, but it is NOT what `brew install` reads:
# Homebrew only looks inside "taps", i.e. repositories named homebrew-<something>.
# .github/workflows/homebrew.yml mirrors this file to PhpMetrics/homebrew-phpmetrics
# every time a release is promoted, which is what users actually install from:
#
# brew tap phpmetrics/phpmetrics
# brew install phpmetrics
#
# The url and sha256 lines below are rewritten by that same workflow.
# Do not bump them by hand; everything else can be edited freely.
#
# The formula ships the phar rather than the standalone binaries attached to each
# release. Those are ad-hoc signed (CodeDirectory identifier "micro.sfx", no
# Developer ID and no notarization), so macOS does run them once Homebrew has
# downloaded them, but shipping them would mean four url/sha256 pairs to keep in
# sync and a formula homebrew-core would not accept. The phar gives one formula
# for every architecture and matches what phpstan, php-cs-fixer and psalm do in
# homebrew-core. The cost is `depends_on "php"`, which pulls Homebrew's own PHP
# even for a user who already has one.
class Phpmetrics < Formula
desc "Static analyzer for PHP: coupling, cyclomatic complexity, maintainability index"
homepage "https://phpmetrics.github.io/website/"
# Homebrew reads the version off the tag in the url, dropping the "v" prefix.
url "https://github.com/PhpMetrics/PhpMetrics/releases/download/v2.9.0/phpmetrics.phar"
sha256 "52c1f14aa0b94695eb34cd1d0049ce7f6a7f89d7d3c3d46bfa45526d3412698a"
license "MIT"

livecheck do
url :stable
strategy :github_latest
end

depends_on "php"

def install
libexec.install "phpmetrics.phar"

# A wrapper rather than a symlink: it pins the php Homebrew depends on,
# instead of whichever php happens to come first in the user's PATH.
(bin/"phpmetrics").write <<~SHELL
#!/bin/bash
exec "#{formula_opt_bin("php")}/php" "#{libexec}/phpmetrics.phar" "$@"
SHELL
end

test do
# --version prints "PhpMetrics <version> <http://www.phpmetrics.org>"
assert_match version.to_s, shell_output("#{bin}/phpmetrics --version")

(testpath/"src/Foo.php").write <<~PHP
<?php
class Foo
{
public function bar($a)
{
return $a > 0 ? $a : -$a;
}
}
PHP

system bin/"phpmetrics", "--report-json=#{testpath}/report.json", "#{testpath}/src"
assert_path_exists testpath/"report.json"
end
end
16 changes: 13 additions & 3 deletions doc/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,34 @@ export PATH=~/.composer/vendor/bin:$PATH
## Phar

```bash
curl https://github.com/phpmetrics/PhpMetrics/releases/download/2.10.0/phpmetrics.phar
curl -fsSL -o phpmetrics.phar https://github.com/phpmetrics/PhpMetrics/releases/download/v2.10.0/phpmetrics.phar
chmod +x phpmetrics.phar && mv phpmetrics.phar /usr/local/bin/phpmetrics
```

## Apt (Debian, Ubuntu...)

```bash
curl https://github.com/phpmetrics/PhpMetrics/releases/download/2.10.0/phpmetrics.deb
curl -fsSL -o phpmetrics.deb https://github.com/phpmetrics/PhpMetrics/releases/download/v2.10.0/phpmetrics.deb
dpkg -i phpmetrics.deb
```

## Brew (OSX)
## Homebrew (macOS, Linux)

PhpMetrics is not in homebrew-core, it is published through its own tap:

```bash
brew tap phpmetrics/phpmetrics
brew trust --tap phpmetrics/phpmetrics
brew install phpmetrics
```

Recent versions of Homebrew refuse to load formulae from a tap they don't know,
hence the `brew trust` line. On older versions that command does not exist, and
`brew install` works straight after `brew tap`.

The formula installs the phar and depends on the `php` formula, so Homebrew
pulls PHP in if you don't have it already.

## PhpArch

```bash
Expand Down
Loading