Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9b057b8
ci(skill): publish OpenMAIC skill to ClawHub
wyuc Aug 5, 2026
24e50e2
ci(skill): support manual ClawHub versions
wyuc Aug 5, 2026
47f4002
Merge branch 'main' into codex/clawhub-skill-publish-workflow
wyuc Aug 5, 2026
899f9ee
ci(skill): validate manual ClawHub versions
wyuc Aug 5, 2026
8d3819e
ci(skill): normalize manual ClawHub versions
wyuc Aug 5, 2026
5e74db1
fix(skill): preserve build metadata in version checks
wyuc Aug 5, 2026
3d0d303
fix(skill): reject manual build metadata versions
wyuc Aug 5, 2026
6e4bcfd
refactor(skill): share ClawHub version validation
wyuc Aug 5, 2026
8dbfc07
fix(skill): validate shared version checker inputs
wyuc Aug 5, 2026
44ae708
test(skill): cover ClawHub version validation
wyuc Aug 5, 2026
8727609
test(skill): harden ClawHub version fixtures
wyuc Aug 5, 2026
c809837
test(skill): cover ClawHub checker edge cases
wyuc Aug 5, 2026
ba79017
test(skill): cover ClawHub metadata contracts
wyuc Aug 5, 2026
88bf3b4
ci(skill): harden ClawHub publish workflow
wyuc Aug 5, 2026
f9749fb
test(skill): cover ClawHub publish shell
wyuc Aug 5, 2026
d323995
test(skill): strengthen publish shell regressions
wyuc Aug 5, 2026
f7e9e83
ci(skill): verify publish path on Bash 3.2
wyuc Aug 5, 2026
0fd3036
ci(skill): harden ClawHub release guards
wyuc Aug 5, 2026
4b8a17a
ci(skill): unify publish divergence handling
wyuc Aug 5, 2026
fdf4399
test(skill): bind publish divergence reasons
wyuc Aug 5, 2026
8e75dfd
test(skill): bind stale tree inputs
wyuc Aug 5, 2026
04d8972
test(skill): enforce stale guard ordering
wyuc Aug 5, 2026
aa7aa89
test(skill): lock publish guard sequence
wyuc Aug 5, 2026
b115c81
test(skill): enforce publish guard counts
wyuc Aug 5, 2026
e4fd88e
test(skill): enforce publish job uniqueness
wyuc Aug 5, 2026
b72b76d
Merge remote-tracking branch 'origin/main' into codex/clawhub-skill-p…
wyuc Aug 6, 2026
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
109 changes: 109 additions & 0 deletions .github/scripts/check-clawhub-version.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';

class UserFacingError extends Error {}

function fail(message) {
throw new UserFacingError(message);
}

function loadSemver(packageJson) {
try {
return createRequire(packageJson)('semver');
} catch {
fail('Unable to load the pinned SemVer dependency.');
}
}

function main() {
const packageJson = process.env.SEMVER_PACKAGE_JSON;
const preflightFile = process.env.PREFLIGHT_FILE;
const desired = process.env.PUBLISH_VERSION;
if (!packageJson || !preflightFile || desired === undefined) {
fail('ClawHub version check environment is incomplete.');
}

const semver = loadSemver(packageJson);

let preflight;
try {
preflight = JSON.parse(readFileSync(preflightFile, 'utf8'));
} catch {
fail('Unable to read ClawHub version preflight metadata.');
}

function canonicalVersion(value) {
if (typeof value !== 'string') return null;
const parsed = semver.parse(value);
if (!parsed) return null;
const build = parsed.build.length > 0 ? `+${parsed.build.join('.')}` : '';
return {
full: `${parsed.version}${build}`,
hasBuild: parsed.build.length > 0,
hasPrerelease: parsed.prerelease.length > 0,
};
}

const desiredVersion = canonicalVersion(desired);
if (!desiredVersion) {
fail('Requested version is not valid semver.');
}
if (desiredVersion.hasPrerelease) {
fail('Requested version must be a stable SemVer release.');
}
if (desiredVersion.hasBuild) {
fail('Requested version must not include build metadata.');
}
const canonical = desiredVersion.full;

const isPreflightObject =
preflight !== null && typeof preflight === 'object' && !Array.isArray(preflight);
if (
!isPreflightObject ||
typeof preflight.status !== 'string' ||
typeof preflight.version !== 'string' ||
typeof preflight.fingerprint !== 'string' ||
preflight.fingerprint.length === 0 ||
!Object.hasOwn(preflight, 'latestVersion')
) {
fail('ClawHub returned incomplete version metadata.');
}

const preflightVersion = canonicalVersion(preflight.version);
if (!preflightVersion) {
fail('ClawHub returned an invalid preflight version.');
}
if (preflight.status === 'unchanged' && preflightVersion.full === canonical) {
return `noop\t${canonical}`;
}
if (
preflight.status === 'unchanged' &&
semver.eq(preflightVersion.full, canonical) &&
preflightVersion.full !== canonical
) {
fail(
'ClawHub has unchanged content at the same SemVer precedence with different build metadata.',
);
}

if (preflight.latestVersion !== null) {
const latest = canonicalVersion(preflight.latestVersion);
if (!latest) {
fail('ClawHub returned an invalid latest version.');
}
if (!semver.gt(canonical, latest.full)) {
fail(`Requested version must be greater than ${latest.full}.`);
}
}

return `continue\t${canonical}`;
}

try {
process.stdout.write(`${main()}\n`);
} catch (error) {
const message =
error instanceof UserFacingError ? error.message : 'Unable to check ClawHub version.';
process.stderr.write(`::error::${message}\n`);
process.exitCode = 1;
}
58 changes: 58 additions & 0 deletions .github/scripts/publish-openmaic-skill.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash

set -euo pipefail

fail() {
echo "::error::$1" >&2
exit 1
}

if [[ "$#" -eq 0 ]]; then
dry_run=false
elif [[ "$#" -eq 1 && "$1" == "--dry-run" ]]; then
dry_run=true
else
fail "Usage: publish-openmaic-skill.sh [--dry-run]"
fi

[[ -n "${SOURCE_REPO:-}" ]] || fail "SOURCE_REPO is required."
[[ "${PUBLISH_VERSION+x}" == x ]] || fail "PUBLISH_VERSION is required."
[[ -n "${CLAWHUB:-}" ]] || fail "CLAWHUB is required."

source_commit="$(git rev-parse HEAD)"
publish_args=(
skills/openmaic
--slug openmaic
--name OpenMAIC
--owner wyuc
--source-repo "$SOURCE_REPO"
--source-commit "$source_commit"
--source-path skills/openmaic
)

if [[ -n "$PUBLISH_VERSION" ]]; then
[[ -n "${RUNNER_TEMP:-}" ]] || fail "RUNNER_TEMP is required for version preflight."
preflight_file="$RUNNER_TEMP/clawhub-version-preflight.json"
"$CLAWHUB" skill publish "${publish_args[@]}" --dry-run --json | tee "$preflight_file"
if ! decision="$(PREFLIGHT_FILE="$preflight_file" node .github/scripts/check-clawhub-version.mjs)"; then
exit 1
fi
IFS=$'\t' read -r action canonical_version extra <<< "$decision"
if [[ -n "${extra:-}" || -z "$action" || -z "$canonical_version" ]]; then
fail "Invalid version preflight decision."
fi
case "$action" in
noop)
echo "::notice::The requested version already has identical content."
exit 0
;;
continue) publish_args+=(--version "$canonical_version") ;;
*) fail "Unknown version preflight action." ;;
esac
fi

if [[ "$dry_run" == true ]]; then
"$CLAWHUB" skill publish "${publish_args[@]}" --dry-run --json
else
"$CLAWHUB" skill publish "${publish_args[@]}" --json
fi
Loading
Loading