Skip to content

laravel-octane-plugin 2.0.2 #1

laravel-octane-plugin 2.0.2

laravel-octane-plugin 2.0.2 #1

Workflow file for this run

name: Validate
on:
pull_request:
branches: [main]
paths:
- "plugins/**"
- "scripts/**"
- "schema/**"
- ".github/workflows/validate.yml"
# One-plugin-per-PR + semver-bump gate. Runs as pull_request_target so the
# token is writable even for fork PRs (a plain pull_request token is read-only
# on forks and would 403 the PR-edit API). pull_request_target runs in the
# BASE repo's context, so the PR content is treated as untrusted DATA only:
# we read composer.json with jq and never execute fork-supplied code with this
# token.
pull_request_target:
branches: [main]
paths:
- "plugins/**"
permissions:
contents: read
# Key by event too: the validate job (pull_request) and the meta gate
# (pull_request_target) run from different events on the same PR and must not
# cancel each other.
concurrency:
group: validate-${{ github.event_name }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
validate:
name: Validate plugins
# The validate job reads the PR's code; it must only run on the read-only
# `pull_request` event — never pull_request_target.
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
tools: composer
- name: Install tooling
run: npm ci
- name: composer validate (changed plugins)
run: |
set -euo pipefail
for dir in plugins/*/; do
if [ -f "$dir/composer.json" ]; then
echo "::group::composer validate $dir"
composer validate --no-check-publish --no-check-all "$dir/composer.json" || true
echo "::endgroup::"
fi
done
- name: Validate all plugins
run: node scripts/validate.mjs
- name: Dry-run pack (prove deterministic zip + hash)
run: node scripts/pack.mjs --dry-run
pr-meta:
name: Enforce version bump and PR title
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
# Untrusted checkout: PR head, including fork changes. Read as DATA only
# (jq over composer.json). Never executed; credentials not persisted.
- name: Checkout PR head (untrusted, data only)
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
path: pr
fetch-depth: 0
persist-credentials: false
- name: Determine changed plugin, verify composer.json, enforce bump
id: meta
working-directory: pr
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
git fetch --no-tags --depth=50 \
"https://github.com/${{ github.repository }}.git" "$BASE_SHA" 2>/dev/null || true
if git cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
base="$BASE_SHA"
else
echo "::warning::base commit $BASE_SHA unavailable; falling back to origin/${{ github.event.pull_request.base.ref }}"
base="origin/${{ github.event.pull_request.base.ref }}"
fi
if mb="$(git merge-base "$base" "$HEAD_SHA" 2>/dev/null)"; then
diff_base="$mb"
else
echo "::warning::no merge base; comparing against base tip directly."
diff_base="$base"
fi
changed="$(git diff --name-only "$diff_base" "$HEAD_SHA" -- plugins/ \
| awk -F/ 'NF>1 && $1=="plugins" {print $2}' \
| sort -u)"
count="$(printf '%s' "$changed" | grep -c . || true)"
if [ "$count" -eq 0 ]; then
echo "No plugin changed (tooling-only PR); skipping title and bump checks."
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$count" -gt 1 ]; then
echo "::error::This PR changes $count plugins:"
printf '::error:: - %s\n' $changed
echo "::error::Open one PR per plugin. The title must be a single '<name> <version>'."
exit 1
fi
slug="$changed"
manifest="plugins/$slug/composer.json"
if [ ! -f "$manifest" ]; then
echo "::error::$manifest not found — cannot read the submission's version."
exit 1
fi
if ! pkg_name="$(jq -er '.name' "$manifest" 2>/dev/null)"; then
echo "::error::$manifest is not valid JSON or has no top-level \"name\"."
exit 1
fi
if ! version="$(jq -er '.version' "$manifest" 2>/dev/null)"; then
echo "::error::$manifest has no top-level \"version\". Add semver (e.g. \"1.0.0\")."
exit 1
fi
author_gh="$(jq -er '.extra.vito.author.github // empty' "$manifest" 2>/dev/null | sed 's/^@//' || true)"
# Directory name must equal the package part of the composer name.
pkg_part="${pkg_name##*/}"
if [ "$pkg_part" != "$slug" ]; then
echo "::error::composer name package part '$pkg_part' must equal directory name '$slug' (plugins/$slug/)."
exit 1
fi
semver_re='^[0-9]+\.[0-9]+\.[0-9]+([-+].+)?$'
if ! printf '%s' "$version" | grep -Eq "$semver_re"; then
echo "::error::version '$version' in $manifest is not valid semver (expected MAJOR.MINOR.PATCH)."
exit 1
fi
base_version="$(git show "$base:plugins/$slug/composer.json" 2>/dev/null \
| jq -er '.version' 2>/dev/null || true)"
if [ -z "$base_version" ]; then
echo "New plugin '$slug' at $version — no previous version to compare."
else
echo "Existing '$slug': $base_version (main) -> $version (PR)."
core() { printf '%s' "$1" | sed -E 's/[-+].*$//'; }
IFS=. read -r ba bi bp <<<"$(core "$base_version")"
IFS=. read -r na ni np <<<"$(core "$version")"
bumped=false
if [ "$na" -gt "$ba" ]; then bumped=true
elif [ "$na" -lt "$ba" ]; then bumped=down
elif [ "$ni" -gt "$bi" ]; then bumped=true
elif [ "$ni" -lt "$bi" ]; then bumped=down
elif [ "$np" -gt "$bp" ]; then bumped=true
elif [ "$np" -lt "$bp" ]; then bumped=down
fi
if [ "$bumped" = "down" ]; then
echo "::error::version '$version' is lower than main's '$base_version'. Bump forward (no downgrades; published versions are immutable)."
exit 1
fi
if [ "$bumped" != "true" ]; then
echo "::error::version '$version' does not bump main's '$base_version'. Increase MAJOR.MINOR.PATCH (published versions are immutable)."
exit 1
fi
fi
title="$slug $version"
echo "title=$title" >> "$GITHUB_OUTPUT"
echo "slug=$slug" >> "$GITHUB_OUTPUT"
echo "author_gh=$author_gh" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "Resolved PR title: $title (author handle: ${author_gh:-<none>})"
- name: Force PR title to "<name> <version>"
if: steps.meta.outputs.skip == 'false'
uses: actions/github-script@v7
env:
DESIRED_TITLE: ${{ steps.meta.outputs.title }}
with:
script: |
const desired = process.env.DESIRED_TITLE;
const { owner, repo } = context.repo;
const pull_number = context.payload.pull_request.number;
const current = context.payload.pull_request.title;
if (current === desired) {
core.info(`PR title already "${desired}"; nothing to do.`);
return;
}
await github.rest.pulls.update({ owner, repo, pull_number, title: desired });
core.info(`Forced PR title: "${current}" -> "${desired}".`);
- name: Ping plugin author for review
if: steps.meta.outputs.skip == 'false' && steps.meta.outputs.author_gh != ''
uses: actions/github-script@v7
env:
PLUGIN_SLUG: ${{ steps.meta.outputs.slug }}
AUTHOR_GH: ${{ steps.meta.outputs.author_gh }}
with:
script: |
const { owner, repo } = context.repo;
const issue_number = context.payload.pull_request.number;
const slug = process.env.PLUGIN_SLUG;
const handle = process.env.AUTHOR_GH.replace(/^@/, "").trim();
const opener = (context.payload.pull_request.user?.login || "").toLowerCase();
const marker = "<!-- vito-plugin-author-review -->";
if (!handle || handle.toLowerCase() === opener) {
core.info(`No author ping needed (handle '${handle}', opener '${opener}').`);
return;
}
const comments = await github.paginate(
github.rest.issues.listComments,
{ owner, repo, issue_number, per_page: 100 },
);
if (comments.some((c) => c.user?.type === "Bot" && c.body?.includes(marker))) {
core.info("Author already pinged; nothing to do.");
return;
}
await github.rest.issues.createComment({
owner, repo, issue_number,
body:
`${marker}\n@${handle} — this PR changes the **${slug}** plugin, ` +
`which you're listed as the author of. Could you review it?`,
});
core.info(`Pinged author @${handle} to review '${slug}'.`);