Skip to content
Open
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
44 changes: 44 additions & 0 deletions .github/actions/changelog/create-dependabot-entry/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Create Dependabot Changelog Entry
description: Create and push a minimal changelog entry for a same-repository Dependabot pull request when the branch still lacks one.

inputs:
changelog-file:
description: Managed changelog path.
required: true
base-ref:
description: Base branch reference used for changelog comparison.
required: true
head-ref:
description: Pull request head branch ref.
required: true
pull-request-number:
description: Pull request number.
required: true
pull-request-title:
description: Pull request title used as the fallback changelog message source.
required: true

outputs:
created:
description: Whether a changelog entry was created and pushed.
value: ${{ steps.create.outputs.created }}
status:
description: Whether the branch already had an entry, auto-created one, or still remains missing.
value: ${{ steps.create.outputs.status }}
message:
description: Generated changelog entry message, or empty when no entry was needed.
value: ${{ steps.create.outputs.message }}

runs:
using: composite
steps:
- id: create
name: Create entry when missing
shell: bash
env:
INPUT_CHANGELOG_FILE: ${{ inputs.changelog-file }}
INPUT_BASE_REF: ${{ inputs.base-ref }}
INPUT_HEAD_REF: ${{ inputs.head-ref }}
INPUT_PULL_REQUEST_NUMBER: ${{ inputs.pull-request-number }}
INPUT_PULL_REQUEST_TITLE: ${{ inputs.pull-request-title }}
run: ${{ github.action_path }}/run.sh
75 changes: 75 additions & 0 deletions .github/actions/changelog/create-dependabot-entry/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
set -euo pipefail

entry_message="$(php <<'PHP'
<?php

$title = preg_replace('/\s+/', ' ', trim(getenv('INPUT_PULL_REQUEST_TITLE') ?: '')) ?: trim(getenv('INPUT_PULL_REQUEST_TITLE') ?: '');
$message = rtrim($title, " \t\n\r\0\x0B.");

if (1 !== preg_match('/\(#\d+\)$/', $message)) {
$message = sprintf('%s (#%d)', $message, (int) (getenv('INPUT_PULL_REQUEST_NUMBER') ?: 0));
}

echo $message;
PHP
)"
dev_tools_bin="${DEV_TOOLS_BIN:-dev-tools}"

git fetch --no-tags --depth=1 origin "+refs/heads/${INPUT_BASE_REF}:refs/remotes/origin/${INPUT_BASE_REF}"
git fetch --no-tags --depth=1 origin "+refs/heads/${INPUT_HEAD_REF}:refs/remotes/origin/${INPUT_HEAD_REF}"
git switch -C "${INPUT_HEAD_REF}" "refs/remotes/origin/${INPUT_HEAD_REF}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

if "${dev_tools_bin}" changelog:check --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then
{
echo "created=false"
echo "status=already-present"
printf 'message=%s\n' "${entry_message}"
} >> "$GITHUB_OUTPUT"

exit 0
fi

"${dev_tools_bin}" changelog:entry --type=changed --file="${INPUT_CHANGELOG_FILE}" "${entry_message}"
git add "${INPUT_CHANGELOG_FILE}"

if git diff --cached --quiet -- "${INPUT_CHANGELOG_FILE}"; then
{
echo "created=false"
echo "status=missing"
printf 'message=%s\n' "${entry_message}"
} >> "$GITHUB_OUTPUT"

exit 1
fi

git commit -m "Add changelog entry for Dependabot PR #${INPUT_PULL_REQUEST_NUMBER}"
git push origin "HEAD:${INPUT_HEAD_REF}"

if ! "${dev_tools_bin}" changelog:check --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then
{
echo "created=false"
echo "status=missing"
printf 'message=%s\n' "${entry_message}"
} >> "$GITHUB_OUTPUT"

exit 1
fi

if ! grep -F --quiet -- "- ${entry_message}" "${INPUT_CHANGELOG_FILE}"; then
{
echo "created=false"
echo "status=missing"
printf 'message=%s\n' "${entry_message}"
} >> "$GITHUB_OUTPUT"

exit 1
fi

{
echo "created=true"
echo "status=auto-created"
printf 'message=%s\n' "${entry_message}"
} >> "$GITHUB_OUTPUT"
35 changes: 35 additions & 0 deletions .github/actions/changelog/publish-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Publish GitHub Release
description: Create or update a GitHub release from a rendered release-notes file.

inputs:
version:
description: Release version without leading v.
required: true
target:
description: Commit SHA or ref the release should target.
required: true
notes-file:
description: Rendered release-notes file path.
required: false
default: .dev-tools/release-notes.md

runs:
using: composite
steps:
- id: publish
name: Publish release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_TARGET: ${{ inputs.target }}
INPUT_NOTES_FILE: ${{ inputs.notes-file }}
run: ${{ github.action_path }}/run.sh

outputs:
operation:
description: Whether the release was created or updated.
value: ${{ steps.publish.outputs.operation }}
url:
description: URL of the published GitHub release.
value: ${{ steps.publish.outputs.url }}
24 changes: 24 additions & 0 deletions .github/actions/changelog/publish-release/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail

release_tag="v${INPUT_VERSION}"

if gh release view "${release_tag}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
operation="updated"
gh release edit "${release_tag}" \
--repo "${GITHUB_REPOSITORY}" \
--title "${release_tag}" \
--notes-file "${INPUT_NOTES_FILE}"
else
operation="created"
gh release create "${release_tag}" \
--repo "${GITHUB_REPOSITORY}" \
--target "${INPUT_TARGET}" \
--title "${release_tag}" \
--notes-file "${INPUT_NOTES_FILE}"
fi

release_url="$(gh release view "${release_tag}" --repo "${GITHUB_REPOSITORY}" --json url --jq '.url')"

echo "operation=${operation}" >> "${GITHUB_OUTPUT}"
echo "url=${release_url}" >> "${GITHUB_OUTPUT}"
25 changes: 25 additions & 0 deletions .github/actions/changelog/render-release-notes/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Render Release Notes
description: Render a changelog section into a release-notes file.

inputs:
changelog-file:
description: Managed changelog path.
required: true
version:
description: Release version to render.
required: true
output-file:
description: Output file path for rendered release notes.
required: false
default: .dev-tools/release-notes.md

runs:
using: composite
steps:
- name: Render release notes
shell: bash
env:
INPUT_CHANGELOG_FILE: ${{ inputs.changelog-file }}
INPUT_VERSION: ${{ inputs.version }}
INPUT_OUTPUT_FILE: ${{ inputs.output-file }}
run: ${{ github.action_path }}/run.sh
7 changes: 7 additions & 0 deletions .github/actions/changelog/render-release-notes/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

dev_tools_bin="${DEV_TOOLS_BIN:-dev-tools}"

mkdir -p "$(dirname "${INPUT_OUTPUT_FILE}")"
"${dev_tools_bin}" changelog:show "${INPUT_VERSION}" --file="${INPUT_CHANGELOG_FILE}" > "${INPUT_OUTPUT_FILE}"
26 changes: 26 additions & 0 deletions .github/actions/changelog/resolve-merged-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Resolve Merged Release Version
description: Derive the released version from a merged release branch name.

inputs:
head-ref:
description: Pull request head ref.
required: true
release-branch-prefix:
description: Release branch prefix.
required: true

outputs:
value:
description: Resolved release version.
value: ${{ steps.resolve.outputs.value }}

runs:
using: composite
steps:
- id: resolve
name: Resolve merged version
shell: bash
env:
INPUT_HEAD_REF: ${{ inputs.head-ref }}
INPUT_RELEASE_BRANCH_PREFIX: ${{ inputs.release-branch-prefix }}
run: ${{ github.action_path }}/run.sh
11 changes: 11 additions & 0 deletions .github/actions/changelog/resolve-merged-version/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail

version="${INPUT_HEAD_REF#${INPUT_RELEASE_BRANCH_PREFIX}}"

if [ -z "${version}" ] || [ "${version}" = "${INPUT_HEAD_REF}" ]; then
echo "Failed to derive the release version from ${INPUT_HEAD_REF}." >&2
exit 1
fi

echo "value=${version}" >> "${GITHUB_OUTPUT}"
30 changes: 30 additions & 0 deletions .github/actions/changelog/resolve-version/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Resolve Changelog Release Version
description: Resolve the release version from workflow input or infer it from the changelog.

inputs:
changelog-file:
description: Managed changelog path.
required: true
version:
description: Optional explicit version.
required: false
default: ''

outputs:
value:
description: Resolved release version.
value: ${{ steps.resolve.outputs.value }}
source:
description: Resolution source, either input or inferred.
value: ${{ steps.resolve.outputs.source }}

runs:
using: composite
steps:
- id: resolve
name: Resolve version
shell: bash
env:
INPUT_CHANGELOG_FILE: ${{ inputs.changelog-file }}
INPUT_VERSION: ${{ inputs.version }}
run: ${{ github.action_path }}/run.sh
15 changes: 15 additions & 0 deletions .github/actions/changelog/resolve-version/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail

dev_tools_bin="${DEV_TOOLS_BIN:-dev-tools}"

if [ -n "${INPUT_VERSION}" ]; then
version="${INPUT_VERSION}"
source="input"
else
version="$("${dev_tools_bin}" changelog:next-version --file="${INPUT_CHANGELOG_FILE}")"
source="inferred"
fi

echo "value=${version}" >> "${GITHUB_OUTPUT}"
echo "source=${source}" >> "${GITHUB_OUTPUT}"
91 changes: 91 additions & 0 deletions .github/actions/dev-tools/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: Setup Fast Forward DevTools
description: Resolve a local dev-tools binary or install fast-forward/dev-tools globally.

inputs:
version:
description: Composer version constraint or branch for fast-forward/dev-tools.
required: false
default: ^1.0
repository:
description: Composer VCS repository used when installing branch refs.
required: false
default: https://github.com/php-fast-forward/dev-tools
prefer-local:
description: Use a project-local vendor/bin/dev-tools binary when available.
required: false
default: 'true'
composer-home:
description: COMPOSER_HOME used for global installs.
required: false
default: ''
cache-dir:
description: Composer cache directory used for global installs.
required: false
default: /tmp/composer-cache

outputs:
command:
description: Absolute dev-tools command path.
value: ${{ steps.resolve.outputs.command }}
source:
description: Where the command was resolved from.
value: ${{ steps.resolve.outputs.source }}

runs:
using: composite
steps:
- name: Resolve or install dev-tools
id: resolve
shell: bash
env:
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ github.token }}"} }'
INPUT_CACHE_DIR: ${{ inputs.cache-dir }}
INPUT_COMPOSER_HOME: ${{ inputs.composer-home }}
INPUT_PREFER_LOCAL: ${{ inputs.prefer-local }}
INPUT_REPOSITORY: ${{ inputs.repository }}
INPUT_VERSION: ${{ inputs.version }}
run: |
set -euo pipefail

command_path=''
command_source=''

if [ "${INPUT_PREFER_LOCAL}" = "true" ] && [ -x "${GITHUB_WORKSPACE}/vendor/bin/dev-tools" ]; then
command_path="${GITHUB_WORKSPACE}/vendor/bin/dev-tools"
command_source='project'
elif command -v dev-tools >/dev/null 2>&1; then
command_path="$(command -v dev-tools)"
command_source='path'
fi

if [ -z "${command_path}" ]; then
composer_home="${INPUT_COMPOSER_HOME:-${RUNNER_TEMP}/fast-forward-composer-home}"
mkdir -p "${composer_home}" "${INPUT_CACHE_DIR}"

export COMPOSER_HOME="${composer_home}"
export COMPOSER_CACHE_DIR="${INPUT_CACHE_DIR}"

composer global config --no-plugins allow-plugins.fast-forward/dev-tools true
composer global config --no-plugins allow-plugins.ergebnis/composer-normalize true
composer global config --no-plugins allow-plugins.phpdocumentor/shim true
composer global config --no-plugins allow-plugins.phpro/grumphp-shim true
composer global config --no-plugins allow-plugins.pyrech/composer-changelogs true
composer global config --no-plugins repositories.fast-forward-dev-tools vcs "${INPUT_REPOSITORY}"
composer global require --no-interaction --prefer-dist --no-progress --no-scripts "fast-forward/dev-tools:${INPUT_VERSION}"

command_path="${composer_home}/vendor/bin/dev-tools"
command_source='composer-global'

echo "${composer_home}/vendor/bin" >> "${GITHUB_PATH}"
echo "COMPOSER_HOME=${composer_home}" >> "${GITHUB_ENV}"
fi

if [ ! -x "${command_path}" ]; then
echo "::error::Resolved dev-tools command is not executable: ${command_path}"
exit 1
fi

echo "DEV_TOOLS_BIN=${command_path}" >> "${GITHUB_ENV}"
echo "command=${command_path}" >> "${GITHUB_OUTPUT}"
echo "source=${command_source}" >> "${GITHUB_OUTPUT}"
"${command_path}" --version
Loading
Loading