fix: Update lock file in the action dir due to #118.

This commit is contained in:
Robin Tail
2026-01-20 06:47:27 +00:00
committed by pullfrog[bot]
parent ce123c9a57
commit cfd7f45db9
14 changed files with 2099 additions and 2 deletions
+32 -1
View File
@@ -1,4 +1,4 @@
import { Inputs } from "./payload.ts";
import { Inputs, JsonPayload } from "./payload.ts";
describe("Inputs schema", () => {
it("only prompt is required", () => {
@@ -43,3 +43,34 @@ describe("Inputs schema", () => {
}
);
});
describe("JsonPayload schema", () => {
it("requires ~pullfrog and version", () => {
const result = JsonPayload.assert({ "~pullfrog": true, version: "1.2.3" });
expect(result).toMatchObject({ "~pullfrog": true, version: "1.2.3" });
expect(() => JsonPayload.assert({})).toThrow();
expect(() => JsonPayload.assert({ "~pullfrog": true })).toThrow();
expect(() => JsonPayload.assert({ version: "1.2.3" })).toThrow();
});
it.each([
["prompt", "test prompt"],
["agent", "claude"],
["agent", "codex"],
["agent", "cursor"],
["agent", "gemini"],
["agent", "opencode"],
["effort", "mini"],
["effort", "auto"],
["effort", "max"],
["event", { trigger: "unknown" }],
] as const)("should accept optional %s with value %s", (prop, value) => {
const input = { "~pullfrog": true, version: "1.2.3", [prop]: value };
expect(() => JsonPayload.assert(input)).not.toThrow();
});
it.each([["agent"], ["effort"]] as const)("should reject invalid %s values", (prop) => {
const input = { "~pullfrog": true, version: "1.2.3", [prop]: "invalid" as any };
expect(() => JsonPayload.assert(input)).toThrow();
});
});
+8 -1
View File
@@ -2,7 +2,9 @@ import { isAbsolute, resolve } from "node:path";
import * as core from "@actions/core";
import { type } from "arktype";
import { AgentName, type AuthorPermission, Effort, type PayloadEvent } from "../external.ts";
import packageJson from "../package.json" with { type: "json" };
import type { RepoSettings } from "./repoSettings.ts";
import { validateCompatibility } from "./versioning.ts";
// tool permission enum types for inputs
const ToolPermissionInput = type.enumerated("disabled", "enabled");
@@ -11,8 +13,9 @@ const BashPermissionInput = type.enumerated("disabled", "restricted", "enabled")
// schema for JSON payload passed via prompt (internal dispatch invocation)
// note: permissions are intentionally NOT included here to prevent injection attacks
// permissions are derived from event.authorPermission instead
const JsonPayload = type({
export const JsonPayload = type({
"~pullfrog": "true",
"version": "string",
"agent?": AgentName.or("undefined"),
"prompt?": "string",
"repoInstructions?": "string",
@@ -94,6 +97,9 @@ export function resolvePayload(repoSettings: RepoSettings) {
// not JSON, treat as plain string prompt
}
// validate version compatibility from jsonPayload
if (jsonPayload) validateCompatibility(jsonPayload.version, packageJson.version);
// resolve event - use type guard for jsonPayload.event, fallback to unknown trigger
const rawEvent = jsonPayload?.event;
const event: PayloadEvent = isPayloadEvent(rawEvent) ? rawEvent : { trigger: "unknown" };
@@ -111,6 +117,7 @@ export function resolvePayload(repoSettings: RepoSettings) {
// note: modes are NOT in payload - they come from repoSettings in main()
return {
"~pullfrog": true as const,
version: jsonPayload?.version ?? packageJson.version,
agent: resolvedAgent,
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
// whereas inputs.prompt IS the raw JSON string when internally dispatched
+29
View File
@@ -0,0 +1,29 @@
import { describe, it, expect } from 'vitest';
import { validateCompatibility } from './versioning.ts';
describe('validateCompatibility', () => {
it('should throw if payload version is invalid', () => {
expect(() => validateCompatibility('invalid', '1.0.0')).toThrow(/not a valid semantic version/);
});
it.each([
["1.0.0", "1.0.0"], // same
["1.0.0-alpha.1", "1.0.0"], // action is newer than pre-release
["0.1.0", "0.1.1"], // action is newer during active development
["1.0.0", "1.0.1"], // action patched
["1.0.0", "1.1.0"], // action has a new feature (backward compatible)
["1.0.1", "1.0.0"], // payload is newer (patch)
["1.1.0", "1.0.0"], // payload is newer (feature is backward compatible)
])('should accept compatible payload %#', (payloadVersion, actionVersion) => {
expect(() => validateCompatibility(payloadVersion, actionVersion)).not.toThrow();
});
it.each([
["0.1.0", "0.2.0"], // action had breaking changes during active development
["0.2.0", "0.1.0"], // payload had breaking changes during active development
["2.0.0", "1.0.0"], // payload is majorly newer
["1.0.0", "2.0.0"], // action had breaking changes
])('should reject incompatible payload %#', (payloadVersion, actionVersion) => {
expect(() => validateCompatibility(payloadVersion, actionVersion)).toThrow(/is incompatible with action version/);
});
});
+42
View File
@@ -0,0 +1,42 @@
import semver from 'semver';
type CompatibilityPolicy =
/**
* Strict policy: the action must support the same features as the payload version declares
* @example Payload version 1.2.3 => ^1.2.0 range of action versions supported
* @example Payload version 0.1.55 => ^0.1.55 range of action versions supported
*/
| 'same-features'
/**
* Loose policy: the action must have no breaking changes compared to the payload version
* @example Payload version 1.2.3 => ^1.0.0 range of action versions supported
* @example Payload version 0.1.55 => ^0.1.0 range of action versions supported
*/
| 'non-breaking';
const COMPATIBILITY_POLICY: CompatibilityPolicy = "non-breaking";
/**
* @throws Error if the action can't process payload
* The compatibility is determined according to the COMPATIBILITY_POLICY above.
* @param payloadVersion the version of the payload
* @param actionVersion the version of the action (recipient)
*/
export function validateCompatibility(payloadVersion: string, actionVersion: string): void {
const payloadSemVer = semver.parse(payloadVersion);
if (!payloadSemVer) throw new Error(`Payload version ${payloadVersion} is not a valid semantic version.`);
const major = payloadSemVer.major;
const minor = payloadSemVer.minor;
const patch = payloadSemVer.patch;
const compatibilityRange = COMPATIBILITY_POLICY === 'same-features'
? `^${major}.${minor}.${major === 0 ? patch : 0}`
: `^${major}.${major === 0 ? minor : 0}.0`; // non-breaking
if (!semver.satisfies(actionVersion, compatibilityRange)) {
throw new Error(
`Payload version ${payloadVersion} is incompatible with action version ${actionVersion}. ` +
`Please update your workflow to use at least ${semver.minVersion(compatibilityRange)} version of the action.`
);
}
}