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
53 changes: 53 additions & 0 deletions actions/validate-release-consumer/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

name: Validate release consumer
description: Validate a release-tool consumer configuration and expose normalized release metadata.

inputs:
config-path:
description: Consumer release configuration path.
required: false
default: .nextcloud-release.yml
root:
description: Consumer repository root.
required: false
default: .
ref:
description: Git ref to inspect.
required: false
default: HEAD

outputs:
version:
description: Current application version.
value: ${{ steps.validate.outputs.version }}
major:
description: Current application major version.
value: ${{ steps.validate.outputs.major }}
development:
description: Whether the current application version is a development version.
value: ${{ steps.validate.outputs.development }}
changelog-path:
description: Per-major changelog path selected by release-tool.
value: ${{ steps.validate.outputs.changelog-path }}
tool-version:
description: Exact release-tool version used for validation.
value: ${{ steps.setup.outputs.version }}

runs:
using: composite
steps:
- id: setup
name: Setup release-tool
uses: $/actions/setup-release-tool

- id: validate
name: Validate release consumer
shell: bash
env:
RELEASE_TOOL_PATH: ${{ steps.setup.outputs.path }}
RELEASE_CONFIG_PATH: ${{ inputs.config-path }}
RELEASE_ROOT: ${{ inputs.root }}
RELEASE_REF: ${{ inputs.ref }}
run: bash "${{ github.action_path }}/validate.sh"
33 changes: 33 additions & 0 deletions actions/validate-release-consumer/validate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

set -euo pipefail

: "${RELEASE_TOOL_PATH:?release-tool path is required}"
: "${RELEASE_CONFIG_PATH:?release config path is required}"
: "${RELEASE_ROOT:?release root is required}"
: "${RELEASE_REF:?release ref is required}"
: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}"

php "${RELEASE_TOOL_PATH}" config:validate --config "${RELEASE_CONFIG_PATH}" --root "${RELEASE_ROOT}"

metadata_file="${RUNNER_TEMP:-/tmp}/release-consumer-metadata-${RANDOM}-${RANDOM}.json"
trap 'rm -f "${metadata_file}"' EXIT

php "${RELEASE_TOOL_PATH}" metadata:inspect --config "${RELEASE_CONFIG_PATH}" --root "${RELEASE_ROOT}" --ref "${RELEASE_REF}" --json > "${metadata_file}"

php -r '
$metadata = json_decode(file_get_contents($argv[1]), true, 512, JSON_THROW_ON_ERROR);
foreach (["version", "major", "development", "changelog_path"] as $key) {
if (!array_key_exists($key, $metadata)) {
fwrite(STDERR, "Missing metadata field: {$key}\n");
exit(2);
}
}
printf("version=%s\n", $metadata["version"]);
printf("major=%s\n", $metadata["major"]);
printf("development=%s\n", $metadata["development"] ? "true" : "false");
printf("changelog-path=%s\n", $metadata["changelog_path"]);
' "${metadata_file}" >> "${GITHUB_OUTPUT}"
89 changes: 89 additions & 0 deletions tests/test_validate_release_consumer_action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

import os
import subprocess
import tempfile
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
ACTION = ROOT / "actions" / "validate-release-consumer" / "action.yml"
SCRIPT = ROOT / "actions" / "validate-release-consumer" / "validate.sh"


class ValidateReleaseConsumerActionTest(unittest.TestCase):
def test_action_wraps_setup_and_exposes_normalized_outputs(self) -> None:
content = ACTION.read_text(encoding="utf-8")

self.assertIn("uses: $/actions/setup-release-tool", content)
for output in (
"version:",
"major:",
"development:",
"changelog-path:",
"tool-version:",
):
self.assertIn(output, content)

def test_script_validates_config_and_metadata_once(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
fake_bin = root / "bin"
fake_bin.mkdir()
output = root / "output"
calls = root / "calls"

php = fake_bin / "php"
php.write_text(
"#!/usr/bin/env bash\n"
"set -euo pipefail\n"
"printf '%s\\n' \"$*\" >> \"${CALLS_FILE}\"\n"
"if [[ \"$*\" == *'config:validate'* ]]; then exit 0; fi\n"
"if [[ \"$*\" == *'metadata:inspect'* ]]; then\n"
" cat <<'JSON'\n"
"{\"schema\":1,\"valid\":true,\"version\":\"16.0.0-dev.2\",\"major\":16,\"development\":true,\"changelog_path\":\"docs/changelogs/changelog-16.md\"}\n"
"JSON\n"
" exit 0\n"
"fi\n"
"exec /usr/bin/php \"$@\"\n",
encoding="utf-8",
)
php.chmod(0o755)

env = os.environ | {
"PATH": str(fake_bin) + os.pathsep + os.environ["PATH"],
"CALLS_FILE": str(calls),
"RELEASE_TOOL_PATH": "/tmp/release-tool.phar",
"RELEASE_CONFIG_PATH": ".nextcloud-release.yml",
"RELEASE_ROOT": ".",
"RELEASE_REF": "HEAD",
"RUNNER_TEMP": str(root),
"GITHUB_OUTPUT": str(output),
}

result = subprocess.run(
["bash", str(SCRIPT)],
env=env,
text=True,
capture_output=True,
check=False,
)

self.assertEqual(0, result.returncode, result.stderr)
called = calls.read_text(encoding="utf-8")
self.assertIn("config:validate", called)
self.assertIn("metadata:inspect", called)

outputs = output.read_text(encoding="utf-8")
self.assertIn("version=16.0.0-dev.2", outputs)
self.assertIn("major=16", outputs)
self.assertIn("development=true", outputs)
self.assertIn(
"changelog-path=docs/changelogs/changelog-16.md",
outputs,
)


if __name__ == "__main__":
unittest.main()
Loading