Skip to content

Commit f4d24bc

Browse files
Support workflow consumers without a local DevTools dependency (#307)
* Support workflow consumers without local DevTools * Fix workflow runtime resolution for repository checkouts * Fix workflow runtime resolution and CLI invocations * Update wiki submodule pointer for PR #307 * Validate local DevTools runtime package metadata --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
1 parent ccfecaf commit f4d24bc

20 files changed

Lines changed: 590 additions & 55 deletions

File tree

.github/actions/changelog/create-dependabot-entry/run.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
entry_message="$(php -r 'require "vendor/autoload.php"; $resolver = new \FastForward\DevTools\Changelog\DependabotChangelogEntryMessageResolver(); echo $resolver->resolve(getenv("INPUT_PULL_REQUEST_TITLE") ?: "", (int) (getenv("INPUT_PULL_REQUEST_NUMBER") ?: 0));')"
4+
entry_message="$(php -r 'require getenv("DEV_TOOLS_AUTOLOAD") ?: "vendor/autoload.php"; $resolver = new \FastForward\DevTools\Changelog\DependabotChangelogEntryMessageResolver(); echo $resolver->resolve(getenv("INPUT_PULL_REQUEST_TITLE") ?: "", (int) (getenv("INPUT_PULL_REQUEST_NUMBER") ?: 0));')"
55

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

12-
if composer dev-tools changelog:check -- --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then
12+
if dev-tools changelog:check --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then
1313
{
1414
echo "created=false"
1515
echo "status=already-present"
@@ -19,7 +19,7 @@ if composer dev-tools changelog:check -- --file="${INPUT_CHANGELOG_FILE}" --agai
1919
exit 0
2020
fi
2121

22-
composer dev-tools changelog:entry -- --type=changed --file="${INPUT_CHANGELOG_FILE}" "${entry_message}"
22+
dev-tools changelog:entry --type=changed --file="${INPUT_CHANGELOG_FILE}" "${entry_message}"
2323
git add "${INPUT_CHANGELOG_FILE}"
2424

2525
if git diff --cached --quiet -- "${INPUT_CHANGELOG_FILE}"; then
@@ -35,7 +35,7 @@ fi
3535
git commit -m "Add changelog entry for Dependabot PR #${INPUT_PULL_REQUEST_NUMBER}"
3636
git push origin "HEAD:${INPUT_HEAD_REF}"
3737

38-
if ! composer dev-tools changelog:check -- --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then
38+
if ! dev-tools changelog:check --file="${INPUT_CHANGELOG_FILE}" --against="origin/${INPUT_BASE_REF}" >/dev/null 2>&1; then
3939
{
4040
echo "created=false"
4141
echo "status=missing"

.github/actions/changelog/render-release-notes/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
set -euo pipefail
33

44
mkdir -p "$(dirname "${INPUT_OUTPUT_FILE}")"
5-
composer dev-tools changelog:show -- "${INPUT_VERSION}" --file="${INPUT_CHANGELOG_FILE}" > "${INPUT_OUTPUT_FILE}"
5+
dev-tools changelog:show "${INPUT_VERSION}" --file="${INPUT_CHANGELOG_FILE}" > "${INPUT_OUTPUT_FILE}"

.github/actions/changelog/resolve-version/run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ if [ -n "${INPUT_VERSION}" ]; then
55
version="${INPUT_VERSION}"
66
source="input"
77
else
8-
version="$(composer dev-tools changelog:next-version -- --file="${INPUT_CHANGELOG_FILE}")"
8+
version="$(dev-tools changelog:next-version --file="${INPUT_CHANGELOG_FILE}")"
99
source="inferred"
1010
fi
1111

.github/actions/github/resolve-predictable-conflicts/resolve-changelog.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use FastForward\DevTools\Changelog\Parser\ChangelogParser;
77
use FastForward\DevTools\Changelog\Renderer\MarkdownRenderer;
88

9-
$autoload = getenv('DEV_TOOLS_AUTO_RESOLVE_AUTOLOAD') ?: getcwd() . '/vendor/autoload.php';
9+
$autoload = getenv('DEV_TOOLS_AUTO_RESOLVE_AUTOLOAD') ?: getenv('DEV_TOOLS_AUTOLOAD') ?: getcwd() . '/vendor/autoload.php';
1010

1111
if (! is_file($autoload)) {
1212
fwrite(STDERR, sprintf("Composer autoload file not found: %s\n", $autoload));

.github/actions/php/setup-composer/action.yml

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Setup PHP and Composer Dependencies
2-
description: Setup PHP, warm Composer cache, mark safe directories, and install dependencies.
2+
description: Setup PHP, install Composer dependencies when available, and expose a deterministic DevTools runtime.
33

44
inputs:
55
php-version:
@@ -29,6 +29,21 @@ inputs:
2929
description: Additional newline-separated directories to mark as safe for git.
3030
required: false
3131
default: ''
32+
dev-tools-source-directory:
33+
description: Checked-out DevTools workflow source used to resolve the fallback runtime when the consumer does not install DevTools locally.
34+
required: false
35+
default: .dev-tools-actions
36+
37+
outputs:
38+
dev-tools-binary:
39+
description: Absolute path to the resolved DevTools binary.
40+
value: ${{ steps.expose-dev-tools.outputs.binary }}
41+
dev-tools-autoload:
42+
description: Absolute path to the runtime autoload file used by packaged workflow helpers.
43+
value: ${{ steps.expose-dev-tools.outputs.autoload }}
44+
dev-tools-source:
45+
description: Runtime source selected for this job, either `local` or `workflow`.
46+
value: ${{ steps.expose-dev-tools.outputs.source }}
3247

3348
runs:
3449
using: composite
@@ -40,15 +55,6 @@ runs:
4055
extensions: ${{ inputs.extensions }}
4156
coverage: ${{ inputs.coverage }}
4257

43-
- name: Cache Composer dependencies
44-
uses: actions/cache@v5
45-
with:
46-
path: ${{ inputs.cache-dir }}
47-
key: ${{ runner.os }}-composer-${{ inputs.php-version }}-${{ hashFiles('**/composer.lock') }}
48-
restore-keys: |
49-
${{ runner.os }}-composer-${{ inputs.php-version }}-
50-
${{ runner.os }}-composer-
51-
5258
- name: Mark workspace as safe for git
5359
shell: bash
5460
env:
@@ -64,11 +70,48 @@ runs:
6470
done
6571
fi
6672
67-
- name: Install dependencies
73+
- name: Detect consumer Composer manifest
74+
id: consumer-composer
6875
shell: bash
76+
run: |
77+
if [ -f composer.json ]; then
78+
echo "present=true" >> "$GITHUB_OUTPUT"
79+
else
80+
echo "present=false" >> "$GITHUB_OUTPUT"
81+
fi
82+
83+
- name: Install consumer dependencies
84+
if: steps.consumer-composer.outputs.present == 'true'
85+
uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
6986
env:
7087
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ github.token }}"} }'
7188
COMPOSER_CACHE_DIR: ${{ inputs.cache-dir }}
7289
COMPOSER_ROOT_VERSION: ${{ inputs.root-version }}
73-
INPUT_INSTALL_OPTIONS: ${{ inputs.install-options }}
74-
run: composer install ${INPUT_INSTALL_OPTIONS}
90+
with:
91+
composer-options: ${{ inputs.install-options }}
92+
93+
- name: Resolve DevTools runtime source
94+
id: resolve-dev-tools
95+
shell: bash
96+
env:
97+
INPUT_DEV_TOOLS_SOURCE_DIRECTORY: ${{ inputs.dev-tools-source-directory }}
98+
run: ${{ github.action_path }}/detect-dev-tools-runtime.sh
99+
100+
- name: Install fallback DevTools runtime
101+
if: steps.resolve-dev-tools.outputs.needs-fallback == 'true'
102+
uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
103+
env:
104+
COMPOSER_AUTH: '{"github-oauth": {"github.com": "${{ github.token }}"} }'
105+
COMPOSER_CACHE_DIR: ${{ inputs.cache-dir }}
106+
with:
107+
working-directory: ${{ inputs.dev-tools-source-directory }}
108+
composer-options: --prefer-dist --no-plugins --no-scripts
109+
require-lock-file: 'true'
110+
custom-cache-suffix: dev-tools-runtime
111+
112+
- name: Expose DevTools runtime
113+
id: expose-dev-tools
114+
shell: bash
115+
env:
116+
INPUT_DEV_TOOLS_SOURCE_DIRECTORY: ${{ inputs.dev-tools-source-directory }}
117+
run: ${{ github.action_path }}/expose-dev-tools-runtime.sh
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
source "$(dirname "$0")/dev-tools-runtime-lib.sh"
5+
6+
resolve_dev_tools_runtime
7+
8+
needs_fallback='false'
9+
10+
if runtime_requires_workflow_fallback; then
11+
needs_fallback='true'
12+
fi
13+
14+
{
15+
printf 'source=%s\n' "${DEV_TOOLS_RUNTIME_SOURCE}"
16+
printf 'needs-fallback=%s\n' "${needs_fallback}"
17+
printf 'binary=%s\n' "${DEV_TOOLS_RUNTIME_BINARY}"
18+
printf 'autoload=%s\n' "${DEV_TOOLS_RUNTIME_AUTOLOAD}"
19+
printf 'source-directory=%s\n' "${DEV_TOOLS_SOURCE_DIRECTORY}"
20+
} >> "${GITHUB_OUTPUT}"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env bash
2+
3+
resolve_dev_tools_workspace_path() {
4+
local input_path="${1:-.}"
5+
6+
if [[ "${input_path}" = /* ]]; then
7+
printf '%s\n' "${input_path}"
8+
9+
return
10+
fi
11+
12+
printf '%s/%s\n' "$(pwd)" "${input_path#./}"
13+
}
14+
15+
workspace_is_dev_tools_repository() {
16+
local workspace_root="${1:?Workspace root is required}"
17+
local composer_json="${workspace_root}/composer.json"
18+
19+
if [ ! -f "${composer_json}" ]; then
20+
return 1
21+
fi
22+
23+
php -r '
24+
$composer = json_decode((string) file_get_contents($argv[1]), true);
25+
26+
if (! is_array($composer)) {
27+
exit(1);
28+
}
29+
30+
exit(($composer["name"] ?? null) === "fast-forward/dev-tools" ? 0 : 1);
31+
' "${composer_json}"
32+
}
33+
34+
resolve_dev_tools_runtime() {
35+
local source_directory_input="${INPUT_DEV_TOOLS_SOURCE_DIRECTORY:-.dev-tools-actions}"
36+
37+
DEV_TOOLS_WORKSPACE_ROOT="$(pwd)"
38+
DEV_TOOLS_SOURCE_DIRECTORY="$(resolve_dev_tools_workspace_path "${source_directory_input}")"
39+
DEV_TOOLS_LOCAL_AUTOLOAD="${DEV_TOOLS_WORKSPACE_ROOT}/vendor/autoload.php"
40+
DEV_TOOLS_LOCAL_INSTALLED_BINARY="${DEV_TOOLS_WORKSPACE_ROOT}/vendor/bin/dev-tools"
41+
DEV_TOOLS_LOCAL_INSTALLED_PACKAGE_ROOT="${DEV_TOOLS_WORKSPACE_ROOT}/vendor/fast-forward/dev-tools"
42+
DEV_TOOLS_LOCAL_REPOSITORY_BINARY="${DEV_TOOLS_WORKSPACE_ROOT}/bin/dev-tools"
43+
44+
if [ -x "${DEV_TOOLS_LOCAL_INSTALLED_BINARY}" ] && [ -f "${DEV_TOOLS_LOCAL_AUTOLOAD}" ] && workspace_is_dev_tools_repository "${DEV_TOOLS_LOCAL_INSTALLED_PACKAGE_ROOT}"; then
45+
DEV_TOOLS_RUNTIME_SOURCE='local'
46+
DEV_TOOLS_RUNTIME_BINARY="${DEV_TOOLS_LOCAL_INSTALLED_BINARY}"
47+
DEV_TOOLS_RUNTIME_AUTOLOAD="${DEV_TOOLS_LOCAL_AUTOLOAD}"
48+
49+
return 0
50+
fi
51+
52+
if [ -x "${DEV_TOOLS_LOCAL_REPOSITORY_BINARY}" ] && [ -f "${DEV_TOOLS_LOCAL_AUTOLOAD}" ] && workspace_is_dev_tools_repository "${DEV_TOOLS_WORKSPACE_ROOT}"; then
53+
DEV_TOOLS_RUNTIME_SOURCE='local'
54+
DEV_TOOLS_RUNTIME_BINARY="${DEV_TOOLS_LOCAL_REPOSITORY_BINARY}"
55+
DEV_TOOLS_RUNTIME_AUTOLOAD="${DEV_TOOLS_LOCAL_AUTOLOAD}"
56+
57+
return 0
58+
fi
59+
60+
if [ ! -d "${DEV_TOOLS_SOURCE_DIRECTORY}" ]; then
61+
echo "The DevTools workflow source directory was not found: ${DEV_TOOLS_SOURCE_DIRECTORY}" >&2
62+
63+
return 1
64+
fi
65+
66+
if ! workspace_is_dev_tools_repository "${DEV_TOOLS_SOURCE_DIRECTORY}"; then
67+
echo "The DevTools workflow source directory does not point to the fast-forward/dev-tools package: ${DEV_TOOLS_SOURCE_DIRECTORY}" >&2
68+
echo "Checkout the full php-fast-forward/dev-tools source into ${source_directory_input} before using this action." >&2
69+
70+
return 1
71+
fi
72+
73+
DEV_TOOLS_RUNTIME_SOURCE='workflow'
74+
DEV_TOOLS_RUNTIME_BINARY="${DEV_TOOLS_SOURCE_DIRECTORY}/bin/dev-tools"
75+
DEV_TOOLS_RUNTIME_AUTOLOAD="${DEV_TOOLS_SOURCE_DIRECTORY}/vendor/autoload.php"
76+
}
77+
78+
runtime_requires_workflow_fallback() {
79+
[ "${DEV_TOOLS_RUNTIME_SOURCE}" = 'workflow' ]
80+
}
81+
82+
ensure_resolved_runtime_is_available() {
83+
if [ ! -x "${DEV_TOOLS_RUNTIME_BINARY}" ]; then
84+
echo "Resolved DevTools binary is not executable: ${DEV_TOOLS_RUNTIME_BINARY}" >&2
85+
86+
return 1
87+
fi
88+
89+
if [ ! -f "${DEV_TOOLS_RUNTIME_AUTOLOAD}" ]; then
90+
echo "Resolved DevTools autoload file was not found: ${DEV_TOOLS_RUNTIME_AUTOLOAD}" >&2
91+
92+
return 1
93+
fi
94+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
source "$(dirname "$0")/dev-tools-runtime-lib.sh"
5+
6+
resolve_dev_tools_runtime
7+
ensure_resolved_runtime_is_available
8+
9+
runtime_directory="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/dev-tools-runtime/bin"
10+
wrapper_path="${runtime_directory}/dev-tools"
11+
12+
mkdir -p "${runtime_directory}"
13+
14+
{
15+
printf '#!/usr/bin/env bash\n'
16+
printf 'set -euo pipefail\n'
17+
printf 'exec %q "$@"\n' "${DEV_TOOLS_RUNTIME_BINARY}"
18+
} > "${wrapper_path}"
19+
20+
chmod +x "${wrapper_path}"
21+
22+
{
23+
printf 'DEV_TOOLS_BINARY=%s\n' "${DEV_TOOLS_RUNTIME_BINARY}"
24+
printf 'DEV_TOOLS_AUTOLOAD=%s\n' "${DEV_TOOLS_RUNTIME_AUTOLOAD}"
25+
printf 'DEV_TOOLS_AUTO_RESOLVE_AUTOLOAD=%s\n' "${DEV_TOOLS_RUNTIME_AUTOLOAD}"
26+
printf 'DEV_TOOLS_RUNTIME_SOURCE=%s\n' "${DEV_TOOLS_RUNTIME_SOURCE}"
27+
} >> "${GITHUB_ENV}"
28+
29+
printf '%s\n' "${runtime_directory}" >> "${GITHUB_PATH}"
30+
31+
{
32+
printf 'binary=%s\n' "${DEV_TOOLS_RUNTIME_BINARY}"
33+
printf 'autoload=%s\n' "${DEV_TOOLS_RUNTIME_AUTOLOAD}"
34+
printf 'source=%s\n' "${DEV_TOOLS_RUNTIME_SOURCE}"
35+
printf 'command=%s\n' "${wrapper_path}"
36+
} >> "${GITHUB_OUTPUT}"

.github/wiki

Submodule wiki updated from d3bec40 to 9cb08e1

.github/workflows/auto-resolve-conflicts.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ jobs:
6363
repository: php-fast-forward/dev-tools
6464
ref: ${{ github.repository == 'php-fast-forward/dev-tools' && github.sha || 'main' }}
6565
path: .dev-tools-actions
66-
sparse-checkout: |
67-
.github/actions
6866

6967
- name: Setup PHP and install dependencies
7068
uses: ./.dev-tools-actions/.github/actions/php/setup-composer

0 commit comments

Comments
 (0)