name: Publish OpenMAIC skill # Required repository setup: # - Create a GitHub Environment named `clawhub-release`. # - Restrict its deployment branches to `main` with a custom policy. # - Store `CLAWHUB_TOKEN` as an Environment secret. # - Do not also store `CLAWHUB_TOKEN` as a repository secret. # This workflow is path-filtered. Do not configure it as a required check for every PR; # require it only through rules that apply to the paths below. # PR previews execute the checked-out head scripts, so they intentionally receive no secrets, # have read-only contents permission, and disable persisted checkout credentials. # Deleting skills/openmaic does not unpublish or deprecate an existing ClawHub release; # that registry lifecycle action must be performed manually in ClawHub. on: pull_request: branches: [main] paths: - "skills/openmaic/**" - ".github/scripts/check-clawhub-version.mjs" - ".github/scripts/publish-openmaic-skill.sh" - ".github/workflows/publish-openmaic-skill.yml" push: branches: [main] paths: - "skills/openmaic/**" - ".github/scripts/check-clawhub-version.mjs" - ".github/scripts/publish-openmaic-skill.sh" - ".github/workflows/publish-openmaic-skill.yml" workflow_dispatch: inputs: dry_run: description: "Preview the current main branch without publishing" type: boolean default: true version: description: "Optional stable SemVer version (no prerelease/build); empty means automatic patch" type: string default: "" jobs: reject-invalid-dispatch: name: Reject publish outside main if: >- github.event_name == 'workflow_dispatch' && !inputs.dry_run && github.ref != 'refs/heads/main' runs-on: ubuntu-latest permissions: contents: read steps: - name: Reject publish outside main run: | echo "::error::Publishing is only allowed from the main branch." exit 1 bash-3-compatibility: name: Verify macOS Bash 3.2 publish compatibility if: github.event_name == 'pull_request' runs-on: macos-15 permissions: contents: read concurrency: group: clawhub-bash3-${{ github.event.pull_request.number }} cancel-in-progress: true steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: persist-credentials: false - name: Run publish paths with macOS Bash 3.2 run: | set -euo pipefail bash_version="$(/bin/bash -c 'printf "%s.%s" "${BASH_VERSINFO[0]}" "${BASH_VERSINFO[1]}"')" if [[ "$bash_version" != "3.2" ]]; then echo "::error::Expected macOS system Bash 3.2, found $bash_version." exit 1 fi CLAWHUB=/usr/bin/true \ PUBLISH_VERSION='' \ SOURCE_REPO='THU-MAIC/OpenMAIC' \ /bin/bash .github/scripts/publish-openmaic-skill.sh compat_dir="$RUNNER_TEMP/clawhub-bash3" mkdir -p "$compat_dir" cat > "$compat_dir/clawhub" <<'BASH' #!/bin/bash printf '%s\t' "$@" >> "$CLAWHUB_CALLS" printf '\n' >> "$CLAWHUB_CALLS" if [[ " $* " != *" --version 0.4.0 "* ]]; then printf '%s\n' '{"status":"would-publish","version":"0.4.0","latestVersion":"0.3.1","fingerprint":"bash3-fixture"}' fi BASH cat > "$compat_dir/node" <<'BASH' #!/bin/bash printf 'continue\t0.4.0\n' BASH chmod 700 "$compat_dir/clawhub" "$compat_dir/node" : > "$compat_dir/calls" CLAWHUB="$compat_dir/clawhub" \ CLAWHUB_CALLS="$compat_dir/calls" \ PATH="$compat_dir:$PATH" \ PUBLISH_VERSION='0.4.0' \ RUNNER_TEMP="$compat_dir" \ SOURCE_REPO='THU-MAIC/OpenMAIC' \ /bin/bash .github/scripts/publish-openmaic-skill.sh if [[ "$(wc -l < "$compat_dir/calls" | tr -d ' ')" != "2" ]] || ! grep -q -- $'--version\t0.4.0\t' "$compat_dir/calls"; then echo "::error::Bash 3.2 manual-version path did not reach canonical publish." exit 1 fi preview: name: Preview ClawHub publish if: >- github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.dry_run) runs-on: ubuntu-latest permissions: contents: read concurrency: group: clawhub-preview-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }} cancel-in-progress: true steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }} ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || 'main' }} persist-credentials: false - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 22 - name: Check publish script syntax run: | node --check .github/scripts/check-clawhub-version.mjs bash -n .github/scripts/publish-openmaic-skill.sh - name: Install ClawHub CLI working-directory: ${{ runner.temp }} env: NPM_CONFIG_REGISTRY: https://registry.npmjs.org NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/empty-npmrc run: | set -euo pipefail : > "$NPM_CONFIG_USERCONFIG" npm install --global --ignore-scripts clawhub@0.23.3 semver@7.8.5 global_root="$(npm root --global)" if [[ -z "$global_root" ]]; then echo "::error::npm returned an empty global package root." exit 1 fi echo "SEMVER_PACKAGE_JSON=$global_root/semver/package.json" >> "$GITHUB_ENV" - name: Configure ClawHub registry run: | python3 - <<'PY' import json import os from pathlib import Path path = Path(os.environ["RUNNER_TEMP"]) / "clawhub-preview-config.json" path.write_text( json.dumps({"registry": "https://clawhub.ai"}, indent=2) + "\n", encoding="utf-8", ) path.chmod(0o600) PY echo "CLAWHUB_CONFIG_PATH=$RUNNER_TEMP/clawhub-preview-config.json" >> "$GITHUB_ENV" - name: Preview OpenMAIC skill publish env: CLAWHUB: clawhub PUBLISH_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }} SOURCE_REPO: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }} run: | set -euo pipefail source_commit="$(git rev-parse HEAD)" if ! git cat-file -e "HEAD^{tree}:skills/openmaic" 2>/dev/null; then echo "::notice::Skipping $source_commit because skills/openmaic was deleted." exit 0 fi bash .github/scripts/publish-openmaic-skill.sh --dry-run publish: name: Publish to ClawHub if: >- github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' && !inputs.dry_run) runs-on: ubuntu-latest environment: clawhub-release permissions: contents: read concurrency: group: publish-openmaic-skill cancel-in-progress: false steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: ref: ${{ github.sha }} fetch-depth: 0 persist-credentials: false - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 22 - name: Check publish script syntax run: | node --check .github/scripts/check-clawhub-version.mjs bash -n .github/scripts/publish-openmaic-skill.sh - name: Install ClawHub CLI working-directory: ${{ runner.temp }} env: NPM_CONFIG_REGISTRY: https://registry.npmjs.org NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/empty-npmrc run: | set -euo pipefail : > "$NPM_CONFIG_USERCONFIG" npm install --global --ignore-scripts clawhub@0.23.3 semver@7.8.5 global_root="$(npm root --global)" if [[ -z "$global_root" ]]; then echo "::error::npm returned an empty global package root." exit 1 fi echo "SEMVER_PACKAGE_JSON=$global_root/semver/package.json" >> "$GITHUB_ENV" - name: Verify ClawHub token is configured env: CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }} run: | if [[ -z "$CLAWHUB_TOKEN" ]]; then echo "::error::CLAWHUB_TOKEN is not configured in the clawhub-release environment." exit 1 fi - name: Write ClawHub config env: CLAWHUB_TOKEN: ${{ secrets.CLAWHUB_TOKEN }} run: | set -euo pipefail python3 - <<'PY' import json import os import sys from pathlib import Path path = Path(os.environ["RUNNER_TEMP"]) / "clawhub-config.json" flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL try: fd = os.open(path, flags, 0o600) except FileExistsError: print("::error::ClawHub config already exists in RUNNER_TEMP.", file=sys.stderr) sys.exit(1) with os.fdopen(fd, "w", encoding="utf-8") as config: json.dump( {"registry": "https://clawhub.ai", "token": os.environ["CLAWHUB_TOKEN"]}, config, indent=2, ) config.write("\n") PY echo "CLAWHUB_CONFIG_PATH=$RUNNER_TEMP/clawhub-config.json" >> "$GITHUB_ENV" - name: Verify ClawHub authentication run: clawhub whoami - name: Publish OpenMAIC skill env: CLAWHUB: clawhub EVENT_NAME: ${{ github.event_name }} PUBLISH_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.version || '' }} SOURCE_REPO: ${{ github.repository }} run: | set -euo pipefail source_commit="$(git rev-parse HEAD)" handle_divergence() { reason="$1" case "$EVENT_NAME" in workflow_dispatch) echo "::error::Refusing manual publish because $reason." exit 1 ;; push) echo "::notice::Skipping $source_commit because $reason." exit 0 ;; *) echo "::error::Unexpected publish event: $EVENT_NAME." exit 1 ;; esac } if ! git rev-parse --verify --quiet "refs/remotes/origin/main^{commit}" >/dev/null; then echo "::error::Unable to resolve the checked-out origin/main commit." exit 1 fi if ! git cat-file -e "HEAD^{tree}:skills/openmaic" 2>/dev/null; then handle_divergence "skills/openmaic was deleted" fi if ! git cat-file -e "origin/main^{tree}:skills/openmaic" 2>/dev/null; then handle_divergence "skills/openmaic was removed from main" fi source_tree="$(git rev-parse HEAD:skills/openmaic)" main_tree="$(git rev-parse origin/main:skills/openmaic)" if [[ "$source_tree" != "$main_tree" ]]; then handle_divergence "skills/openmaic changed on main" fi bash .github/scripts/publish-openmaic-skill.sh