diff --git a/.github/workflows/benchmark-pr.yml b/.github/workflows/benchmark-pr.yml index 68f4a334c..b6546a115 100644 --- a/.github/workflows/benchmark-pr.yml +++ b/.github/workflows/benchmark-pr.yml @@ -101,14 +101,29 @@ jobs: with: ref: main + - name: Resolve baseline cache key + id: baseline_key + run: echo "key=phpbench-baseline-$(git rev-parse HEAD)-${{ hashFiles('Monorepo/Benchmark/**', 'phpbench.json') }}" >> "$GITHUB_OUTPUT" + + # Baseline results for a given main commit are identical across PRs, so they are benchmarked + # once and reused from cache to keep the workflow duration low + - name: Restore baseline benchmark results + id: baseline_cache + uses: actions/cache@v4 + with: + path: .phpbench + key: ${{ steps.baseline_key.outputs.key }} + - name: Install dependencies + if: steps.baseline_cache.outputs.cache-hit != 'true' run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --ignore-platform-reqs - name: Benchmark main as baseline id: baseline + if: steps.baseline_cache.outputs.cache-hit != 'true' continue-on-error: true run: | - vendor/bin/phpbench run --retry-threshold=5 --tag=main --report=github-report --profile=opcache_enabled + vendor/bin/phpbench run --tag=main --report=github-report --profile=opcache_enabled - uses: actions/checkout@v3 with: @@ -121,13 +136,17 @@ jobs: - name: Install dependencies run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --ignore-platform-reqs + - name: Check baseline availability + id: baseline_check + run: echo "available=$([ -d .phpbench/storage ] && echo true || echo false)" >> "$GITHUB_OUTPUT" + - name: Benchmark PR shell: bash env: - PHPBENCH_REF_OPTION: "${{ steps.baseline.outcome == 'success' && '--ref=main' || '' }}" + PHPBENCH_REF_OPTION: "${{ steps.baseline_check.outputs.available == 'true' && '--ref=main' || '' }}" run: | echo "Baseline: $PHPBENCH_REF_OPTION" - vendor/bin/phpbench run --retry-threshold=5 --report=github-report --profile=opcache_enabled $PHPBENCH_REF_OPTION | bin/phpbench-to-md.sh > benchmark.md + vendor/bin/phpbench run --report=github-report --profile=opcache_enabled $PHPBENCH_REF_OPTION | bin/phpbench-to-md.sh > benchmark.md cat benchmark.md echo '## Benchmark' >> "$GITHUB_STEP_SUMMARY" cat benchmark.md >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/contribution-check.yml b/.github/workflows/contribution-check.yml index 4f4bf3696..3736e2782 100644 --- a/.github/workflows/contribution-check.yml +++ b/.github/workflows/contribution-check.yml @@ -17,7 +17,7 @@ jobs: uses: actions/github-script@v3 with: script: | - const prBody = context.payload.pull_request.body; - if (!prBody.includes("[X] I have read and agree to the contribution terms")) { - core.setFailed("Contributor has not agreed to the contribution terms. Please mark the checkout with X to agree to the terms. `[X] I have read and agree to the contribution terms outlined in [CONTRIBUTING](https://github.com/ecotoneframework/ecotone-dev/blob/main/CONTRIBUTING.md)`"); + const prBody = context.payload.pull_request.body || ""; + if (!/\[\s*[xX]\s*\] I have read and agree to the contribution terms/.test(prBody)) { + core.setFailed("Contributor has not agreed to the contribution terms. Please mark the checkbox with x to agree to the terms. `[x] I have read and agree to the contribution terms outlined in [CONTRIBUTING](https://github.com/ecotoneframework/ecotone-dev/blob/main/CONTRIBUTING.md)`"); } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41f09f852..430ef6843 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,10 +50,10 @@ jobs: - name: Checkout branch related to tag run: | - raw=$(git branch -r --contains ${{ github.ref }}) + raw=$(git branch -r --contains ${{ github.ref }} | head -n1) branch=${raw##*/} echo "checking out branch $branch for tag ${GITHUB_REF#refs/tags/}" - git checkout $branch + git checkout "$branch" - name: Install PHP-CS-Fixer run: | @@ -160,11 +160,13 @@ jobs: fetch-depth: '0' - name: Checkout branch related to tag + id: release_branch run: | - raw=$(git branch -r --contains ${{ github.ref }}) + raw=$(git branch -r --contains ${{ github.ref }} | head -n1) branch=${raw##*/} echo "checking out branch $branch for tag ${GITHUB_REF#refs/tags/}" - git checkout $branch + git checkout "$branch" + echo "name=$branch" >> "$GITHUB_OUTPUT" - name: Replace key.pem with ECOTONE_ENTERPRISE_PUBLIC_KEY if: ${{ matrix.package.name == 'ecotone' }} @@ -175,11 +177,15 @@ jobs: run: php bin/strip-monorepo-repositories.php "${{ matrix.package.directory }}" - - uses: "danharrin/monorepo-split-github-action@v2.4.0" + uses: "danharrin/monorepo-split-github-action@v2.4.5" if: "startsWith(github.ref, 'refs/tags/')" with: tag: ${GITHUB_REF#refs/tags/} + # ↓ split commits land on the same branch the tag was created from, + # so stable releases from main and pre-releases from other branches do not overwrite each other + branch: '${{ steps.release_branch.outputs.name }}' + # ↓ split "packages/easy-coding-standard" directory package_directory: '${{ matrix.package.directory }}' diff --git a/Monorepo/Benchmark/DbConnectBenchmark.php b/Monorepo/Benchmark/DbConnectBenchmark.php index 0146ec874..060dcc05a 100644 --- a/Monorepo/Benchmark/DbConnectBenchmark.php +++ b/Monorepo/Benchmark/DbConnectBenchmark.php @@ -10,7 +10,7 @@ class DbConnectBenchmark { public function bench_db_connect(): void { - $connectionFactory = new DbalConnectionFactory('pgsql://ecotone:secret@localhost:5432/ecotone'); + $connectionFactory = new DbalConnectionFactory(getenv('DATABASE_DSN') ?: 'pgsql://ecotone:secret@localhost:5432/ecotone'); $connection = $connectionFactory->createContext()->getDbalConnection(); $connection->executeQuery('SELECT 1'); diff --git a/Monorepo/Benchmark/HighThroughputPublishingBenchmark.php b/Monorepo/Benchmark/HighThroughputPublishingBenchmark.php index 2ad81cea6..7c7fe46b0 100644 --- a/Monorepo/Benchmark/HighThroughputPublishingBenchmark.php +++ b/Monorepo/Benchmark/HighThroughputPublishingBenchmark.php @@ -41,7 +41,7 @@ * DBAL and Redis confirm deliveries synchronously as part of the send call itself (database statement result * and command reply respectively), so the asynchronous scenarios are not supported for them and are not benchmarked. */ -#[Warmup(0), Revs(1), Iterations(10)] +#[Warmup(0), Revs(1), Iterations(2)] class HighThroughputPublishingBenchmark { private const AMOUNT_OF_PUBLISHED_MESSAGES = 1000; diff --git a/Monorepo/Benchmark/OutboxRelayBenchmark.php b/Monorepo/Benchmark/OutboxRelayBenchmark.php index 61b314b89..8197632c6 100644 --- a/Monorepo/Benchmark/OutboxRelayBenchmark.php +++ b/Monorepo/Benchmark/OutboxRelayBenchmark.php @@ -37,10 +37,10 @@ * The in-memory target subjects isolate the producing side of the relay; the provider subjects show the full * path into real brokers receiving whole batches at once. */ -#[Warmup(0), Revs(1), Iterations(5)] +#[Warmup(0), Revs(1), Iterations(2)] class OutboxRelayBenchmark { - private const AMOUNT_OF_RELAYED_MESSAGES = 10_000; + private const AMOUNT_OF_RELAYED_MESSAGES = 1_000; private const MESSAGE_PAYLOAD = 'benchmark order payload for outbox relay comparison'; @@ -126,25 +126,25 @@ public function bench_dbal_outbox_drain_batched_into_high_throughput_dbal_target $this->drainWholeOutbox(); } - #[BeforeMethods('setUpRelayBatchedIntoAmqpTarget'), Iterations(3)] + #[BeforeMethods('setUpRelayBatchedIntoAmqpTarget')] public function bench_dbal_outbox_drain_batched_into_rabbitmq_target(): void { $this->drainWholeOutbox(); } - #[BeforeMethods('setUpRelayBatchedIntoKafkaTarget'), Iterations(3)] + #[BeforeMethods('setUpRelayBatchedIntoKafkaTarget')] public function bench_dbal_outbox_drain_batched_into_kafka_target(): void { $this->drainWholeOutbox(); } - #[BeforeMethods('setUpRelayBatchedIntoRedisTarget'), Iterations(3)] + #[BeforeMethods('setUpRelayBatchedIntoRedisTarget')] public function bench_dbal_outbox_drain_batched_into_redis_target(): void { $this->drainWholeOutbox(); } - #[BeforeMethods('setUpRelayBatchedIntoSqsTarget'), Iterations(3)] + #[BeforeMethods('setUpRelayBatchedIntoSqsTarget')] public function bench_dbal_outbox_drain_batched_into_sqs_target(): void { $this->drainWholeOutbox(); diff --git a/bin/update-required-packages.php b/bin/update-required-packages.php index d3d757f9e..dedd7472a 100644 --- a/bin/update-required-packages.php +++ b/bin/update-required-packages.php @@ -7,6 +7,10 @@ if (!$version) { throw new \InvalidArgumentException("Pass version to update branch alias"); } +if (!preg_match('/^(\d+)\.(\d+)\./', $version, $versionParts)) { + throw new \InvalidArgumentException("Version must be in format MAJOR.MINOR.PATCH with optional stability suffix, got: " . $version); +} +$branchAlias = $versionParts[1] . '.' . $versionParts[2] . '.x-dev'; $packageNames = array_map(function ($package) { return $package['package']; }, $packages); @@ -14,16 +18,16 @@ foreach ($packages as $package) { $composerFile = $package['directory'] . DIRECTORY_SEPARATOR . 'composer.json'; $composer = json_decode(file_get_contents($composerFile), true); - $composer['extra']['branch-alias']['dev-main'] = $version . '-dev'; + $composer['extra']['branch-alias']['dev-main'] = $branchAlias; $releaseTime = (new \DateTimeImmutable('now', new DateTimeZone('UTC'))); $composer['extra']['release-time'] = $releaseTime->format('Y-m-d H:i:s'); - foreach ($composer['require'] as $requiredPackage => $requiredVersion) { + foreach ($composer['require'] ?? [] as $requiredPackage => $requiredVersion) { if (in_array($requiredPackage, $packageNames)) { $composer['require'][$requiredPackage] = "~" . $version; } } - foreach ($composer['require-dev'] as $requiredPackage => $requiredVersion) { + foreach ($composer['require-dev'] ?? [] as $requiredPackage => $requiredVersion) { if (in_array($requiredPackage, $packageNames)) { $composer['require-dev'][$requiredPackage] = '~' . $version; }