fix(auto-reply): redact secrets in config show output (#88496) #66
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Plugin NPM Release | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - ".github/workflows/plugin-npm-release.yml" | |
| - "extensions/**" | |
| - "package.json" | |
| - "scripts/lib/plugin-npm-package-manifest.mjs" | |
| - "scripts/lib/plugin-npm-release.ts" | |
| - "scripts/plugin-npm-publish.sh" | |
| - "scripts/plugin-npm-release-check.ts" | |
| - "scripts/plugin-npm-release-plan.ts" | |
| - "scripts/verify-plugin-npm-published-runtime.mjs" | |
| workflow_dispatch: | |
| inputs: | |
| publish_scope: | |
| description: Publish the selected plugins or all publishable plugins from the ref | |
| required: true | |
| default: selected | |
| type: choice | |
| options: | |
| - selected | |
| - all-publishable | |
| ref: | |
| description: Commit SHA on main, a release branch, or the matching Tideclaw alpha branch to publish from | |
| required: true | |
| type: string | |
| plugins: | |
| description: Comma-separated plugin package names to publish when publish_scope=selected | |
| required: false | |
| type: string | |
| release_publish_run_id: | |
| description: Approved OpenClaw Release Publish workflow run id | |
| required: false | |
| type: string | |
| concurrency: | |
| group: plugin-npm-release-${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }} | |
| cancel-in-progress: false | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| NODE_VERSION: "24.15.0" | |
| jobs: | |
| preview_plugins_npm: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| ref_revision: ${{ steps.ref.outputs.sha }} | |
| has_candidates: ${{ steps.plan.outputs.has_candidates }} | |
| candidate_count: ${{ steps.plan.outputs.candidate_count }} | |
| matrix: ${{ steps.plan.outputs.matrix }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.sha }} | |
| fetch-depth: 0 | |
| - name: Setup Node environment | |
| uses: ./.github/actions/setup-node-env | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| install-bun: "false" | |
| - name: Resolve checked-out ref | |
| id: ref | |
| run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Validate ref is on a trusted publish branch | |
| env: | |
| WORKFLOW_REF: ${{ github.ref }} | |
| run: | | |
| set -euo pipefail | |
| git fetch --no-tags origin \ | |
| +refs/heads/main:refs/remotes/origin/main \ | |
| '+refs/heads/release/*:refs/remotes/origin/release/*' | |
| if git merge-base --is-ancestor HEAD origin/main; then | |
| exit 0 | |
| fi | |
| while IFS= read -r release_ref; do | |
| if git merge-base --is-ancestor HEAD "${release_ref}"; then | |
| exit 0 | |
| fi | |
| done < <(git for-each-ref --format='%(refname)' refs/remotes/origin/release) | |
| if [[ "${WORKFLOW_REF}" =~ ^refs/heads/tideclaw/alpha/[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{4}Z$ ]]; then | |
| alpha_branch="${WORKFLOW_REF#refs/heads/}" | |
| git fetch --no-tags origin "+refs/heads/${alpha_branch}:refs/remotes/origin/${alpha_branch}" | |
| if git merge-base --is-ancestor HEAD "refs/remotes/origin/${alpha_branch}"; then | |
| exit 0 | |
| fi | |
| fi | |
| echo "Plugin npm publishes must target a commit reachable from main, release/*, or the matching Tideclaw alpha branch." >&2 | |
| exit 1 | |
| - name: Validate publishable plugin metadata | |
| env: | |
| PUBLISH_SCOPE: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_scope || '' }} | |
| RELEASE_PLUGINS: ${{ github.event_name == 'workflow_dispatch' && inputs.plugins || '' }} | |
| BASE_REF: ${{ github.event_name != 'workflow_dispatch' && github.event.before || '' }} | |
| HEAD_REF: ${{ steps.ref.outputs.sha }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -n "${PUBLISH_SCOPE}" ]]; then | |
| release_args=(--selection-mode "${PUBLISH_SCOPE}") | |
| if [[ -n "${RELEASE_PLUGINS}" ]]; then | |
| release_args+=(--plugins "${RELEASE_PLUGINS}") | |
| fi | |
| pnpm release:plugins:npm:check -- "${release_args[@]}" | |
| elif [[ -n "${BASE_REF}" ]]; then | |
| pnpm release:plugins:npm:check -- --base-ref "${BASE_REF}" --head-ref "${HEAD_REF}" | |
| else | |
| pnpm release:plugins:npm:check | |
| fi | |
| - name: Resolve plugin release plan | |
| id: plan | |
| env: | |
| PUBLISH_SCOPE: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_scope || '' }} | |
| RELEASE_PLUGINS: ${{ github.event_name == 'workflow_dispatch' && inputs.plugins || '' }} | |
| BASE_REF: ${{ github.event_name != 'workflow_dispatch' && github.event.before || '' }} | |
| HEAD_REF: ${{ steps.ref.outputs.sha }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p .local | |
| if [[ -n "${PUBLISH_SCOPE}" ]]; then | |
| plan_args=(--selection-mode "${PUBLISH_SCOPE}") | |
| if [[ -n "${RELEASE_PLUGINS}" ]]; then | |
| plan_args+=(--plugins "${RELEASE_PLUGINS}") | |
| fi | |
| node --import tsx scripts/plugin-npm-release-plan.ts "${plan_args[@]}" > .local/plugin-npm-release-plan.json | |
| elif [[ -n "${BASE_REF}" ]]; then | |
| node --import tsx scripts/plugin-npm-release-plan.ts --base-ref "${BASE_REF}" --head-ref "${HEAD_REF}" > .local/plugin-npm-release-plan.json | |
| else | |
| node --import tsx scripts/plugin-npm-release-plan.ts > .local/plugin-npm-release-plan.json | |
| fi | |
| cat .local/plugin-npm-release-plan.json | |
| candidate_count="$(jq -r '.candidates | length' .local/plugin-npm-release-plan.json)" | |
| has_candidates="false" | |
| if [[ "${candidate_count}" != "0" ]]; then | |
| has_candidates="true" | |
| fi | |
| matrix_json="$(jq -c '.candidates' .local/plugin-npm-release-plan.json)" | |
| { | |
| echo "candidate_count=${candidate_count}" | |
| echo "has_candidates=${has_candidates}" | |
| echo "matrix=${matrix_json}" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "Plugin release candidates:" | |
| jq -r '.candidates[]? | "- \(.packageName)@\(.version) [\(.publishTag)] from \(.packageDir)"' .local/plugin-npm-release-plan.json | |
| echo "Already published / skipped:" | |
| jq -r '.skippedPublished[]? | "- \(.packageName)@\(.version)"' .local/plugin-npm-release-plan.json | |
| - name: Validate Tideclaw alpha plugin channels | |
| if: startsWith(github.ref, 'refs/heads/tideclaw/alpha/') | |
| run: | | |
| set -euo pipefail | |
| invalid="$( | |
| jq -r '.candidates[]? | select(.publishTag != "alpha" or .channel != "alpha") | "- \(.packageName)@\(.version) [\(.publishTag)]"' .local/plugin-npm-release-plan.json | |
| )" | |
| if [[ -n "${invalid}" ]]; then | |
| echo "Tideclaw alpha plugin npm publishes may only publish alpha plugin versions." >&2 | |
| printf '%s\n' "${invalid}" >&2 | |
| exit 1 | |
| fi | |
| validate_release_publish_approval: | |
| name: Validate release publish approval | |
| needs: preview_plugins_npm | |
| if: github.event_name == 'workflow_dispatch' && needs.preview_plugins_npm.outputs.has_candidates == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| contents: read | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| - name: Validate release publish approval run | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| RELEASE_PUBLISH_RUN_ID: ${{ inputs.release_publish_run_id }} | |
| EXPECTED_WORKFLOW_BRANCH: ${{ github.ref_name }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -z "${RELEASE_PUBLISH_RUN_ID// }" ]]; then | |
| if [[ "${GITHUB_ACTOR}" == "github-actions[bot]" ]]; then | |
| echo "Plugin npm publish dispatched by another workflow must include release_publish_run_id." >&2 | |
| exit 1 | |
| fi | |
| echo "Direct Plugin NPM Release dispatch; relying on this workflow's npm-release environment approval." | |
| exit 0 | |
| fi | |
| direct_recovery=false | |
| if [[ "${GITHUB_ACTOR}" != "github-actions[bot]" ]]; then | |
| direct_recovery=true | |
| echo "Direct Plugin NPM Release recovery with release_publish_run_id; relying on this workflow's npm-release environment approval." | |
| fi | |
| RUN_JSON="$(gh run view "$RELEASE_PUBLISH_RUN_ID" --repo "$GITHUB_REPOSITORY" --json workflowName,headBranch,event,status,conclusion,url)" | |
| printf '%s' "$RUN_JSON" | DIRECT_RELEASE_RECOVERY="${direct_recovery}" node scripts/validate-release-publish-approval.mjs | |
| preview_plugin_pack: | |
| needs: preview_plugins_npm | |
| if: needs.preview_plugins_npm.outputs.has_candidates == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| plugin: ${{ fromJson(needs.preview_plugins_npm.outputs.matrix) }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }} | |
| fetch-depth: 1 | |
| - name: Setup Node environment | |
| uses: ./.github/actions/setup-node-env | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| install-bun: "false" | |
| - name: Preview publish command | |
| run: bash scripts/plugin-npm-publish.sh --dry-run "${{ matrix.plugin.packageDir }}" | |
| - name: Preview npm pack contents | |
| run: bash scripts/plugin-npm-publish.sh --pack-dry-run "${{ matrix.plugin.packageDir }}" | |
| publish_plugins_npm: | |
| needs: [preview_plugins_npm, preview_plugin_pack, validate_release_publish_approval] | |
| if: github.event_name == 'workflow_dispatch' && needs.preview_plugins_npm.outputs.has_candidates == 'true' | |
| runs-on: ubuntu-latest | |
| environment: npm-release | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| plugin: ${{ fromJson(needs.preview_plugins_npm.outputs.matrix) }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| ref: ${{ needs.preview_plugins_npm.outputs.ref_revision }} | |
| fetch-depth: 1 | |
| - name: Setup Node environment | |
| uses: ./.github/actions/setup-node-env | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| install-bun: "false" | |
| - name: Check npm package version | |
| id: npm_package_version | |
| env: | |
| PACKAGE_NAME: ${{ matrix.plugin.packageName }} | |
| PACKAGE_VERSION: ${{ matrix.plugin.version }} | |
| run: | | |
| set -euo pipefail | |
| if npm view "${PACKAGE_NAME}@${PACKAGE_VERSION}" version >/dev/null 2>&1; then | |
| echo "${PACKAGE_NAME}@${PACKAGE_VERSION} is already published on npm." | |
| echo "already_published=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "already_published=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Publish | |
| if: steps.npm_package_version.outputs.already_published != 'true' | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: bash scripts/plugin-npm-publish.sh --publish "${{ matrix.plugin.packageDir }}" | |
| - name: Verify published runtime | |
| env: | |
| PACKAGE_NAME: ${{ matrix.plugin.packageName }} | |
| PACKAGE_VERSION: ${{ matrix.plugin.version }} | |
| run: node scripts/verify-plugin-npm-published-runtime.mjs "${PACKAGE_NAME}@${PACKAGE_VERSION}" |