From b7a7c89edd264df3cf3d1ffd71eff3eee0d8acf1 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 17:03:38 +0900 Subject: [PATCH 01/11] feat: extract setup, generate-check, and tag-guard composite actions Move the managed-step logic that the SDK 'setup github' templates currently inline into reusable composite actions, so behavior fixes ship via a pin bump instead of workflow regeneration: - setup: Node/Bun + dependency install for pnpm/npm/yarn/bun, selected by a 'package-manager' input, with an 'install-command' override. - generate-check: run tailor-sdk generate and fail on uncommitted output. - tag-guard: report whether the tag commit is reachable from a branch. Adds a setup smoke test (pnpm). Full PM x target coverage and generate-check/tag-guard E2E are handled by the SDK-side test matrix. The SDK templates are updated to reference these in a follow-up, pinned to this action's release. --- .github/workflows/test-setup.yaml | 40 +++++++++++++++++++ generate-check/action.yaml | 29 ++++++++++++++ setup/action.yaml | 65 +++++++++++++++++++++++++++++++ tag-guard/action.yaml | 33 ++++++++++++++++ tests/deploy/package.json | 3 ++ 5 files changed, 170 insertions(+) create mode 100644 .github/workflows/test-setup.yaml create mode 100644 generate-check/action.yaml create mode 100644 setup/action.yaml create mode 100644 tag-guard/action.yaml diff --git a/.github/workflows/test-setup.yaml b/.github/workflows/test-setup.yaml new file mode 100644 index 0000000..11b7196 --- /dev/null +++ b/.github/workflows/test-setup.yaml @@ -0,0 +1,40 @@ +name: Test Setup Action + +on: + pull_request: + paths: + - "setup/**" + - "tests/deploy/**" + - ".github/workflows/test-setup.yaml" + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: test-setup-${{ github.ref }} + cancel-in-progress: true + +permissions: {} + +jobs: + test: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - name: Run setup action + uses: ./setup + with: + package-manager: pnpm + node-version-file: tests/deploy/package.json + working-directory: tests/deploy + + - name: Verify tailor-sdk is runnable + working-directory: tests/deploy + run: npx tailor-sdk --version diff --git a/generate-check/action.yaml b/generate-check/action.yaml new file mode 100644 index 0000000..a901c69 --- /dev/null +++ b/generate-check/action.yaml @@ -0,0 +1,29 @@ +name: Check Tailor Platform generated files +description: >- + Run tailor-sdk generate and fail if it produces changes, catching generated + files (seed data, enum constants, etc.) that were not regenerated and + committed. Requires Node.js and dependencies to be installed by the caller. + +inputs: + working-directory: + description: Working directory for the project (for monorepo setups) + required: false + default: "." + +runs: + using: composite + steps: + - name: Generate + shell: bash + working-directory: ${{ inputs.working-directory }} + run: npx tailor-sdk generate + + - name: Check generated files are committed + shell: bash + run: | + git add -A + if ! git diff --cached --quiet; then + git --no-pager diff --cached --stat + echo "::error::Generated files are out of date. Run 'tailor-sdk generate' locally and commit the result." + exit 1 + fi diff --git a/setup/action.yaml b/setup/action.yaml new file mode 100644 index 0000000..56af7b1 --- /dev/null +++ b/setup/action.yaml @@ -0,0 +1,65 @@ +name: Set up Tailor Platform toolchain +description: >- + Set up Node.js (or Bun) and install project dependencies for the configured + package manager, so subsequent steps can run tailor-sdk. The caller is + responsible for checking out the repository first. + +inputs: + package-manager: + description: Package manager to use (pnpm, npm, yarn, or bun) + required: true + node-version-file: + description: File to read the Node.js version from + required: false + default: package.json + install-command: + description: >- + Override the dependency-install command (e.g. a filtered monorepo + install). When empty, a frozen-lockfile install for the selected + package manager is used. + required: false + default: "" + working-directory: + description: Working directory to install dependencies in (for monorepo setups) + required: false + default: "." + +runs: + using: composite + steps: + - name: Set up pnpm + if: inputs.package-manager == 'pnpm' + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + + - name: Set up Node.js + if: inputs.package-manager != 'bun' + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: ${{ inputs.node-version-file }} + cache: ${{ inputs.package-manager }} + + - name: Set up Bun + if: inputs.package-manager == 'bun' + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + + - name: Install dependencies + shell: bash + working-directory: ${{ inputs.working-directory }} + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} + INSTALL_COMMAND: ${{ inputs.install-command }} + run: | + if [ -n "$INSTALL_COMMAND" ]; then + eval "$INSTALL_COMMAND" + exit 0 + fi + case "$PACKAGE_MANAGER" in + pnpm) pnpm install --frozen-lockfile ;; + npm) npm ci ;; + yarn) yarn install --frozen-lockfile ;; + bun) bun install --frozen-lockfile ;; + *) + echo "::error::Unsupported package-manager '$PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)" + exit 1 + ;; + esac diff --git a/tag-guard/action.yaml b/tag-guard/action.yaml new file mode 100644 index 0000000..aa48bf4 --- /dev/null +++ b/tag-guard/action.yaml @@ -0,0 +1,33 @@ +name: Guard tag reachability from a branch +description: >- + Determine whether the pushed tag's commit is reachable from a target branch, + so a tag-triggered deploy can be limited to tags cut from that branch. The + caller must check out the repository with full history (fetch-depth: 0). + +inputs: + target-branch: + description: Branch the tag must be reachable from (e.g. main) + required: true + +outputs: + on-branch: + description: "'true' when the tag commit is reachable from the target branch, otherwise 'false'" + value: ${{ steps.guard.outputs.on-branch }} + +runs: + using: composite + steps: + - name: Check tag reachability + id: guard + shell: bash + env: + TARGET_BRANCH: ${{ inputs.target-branch }} + run: | + git fetch origin "$TARGET_BRANCH" + if git merge-base --is-ancestor "$GITHUB_SHA" "origin/$TARGET_BRANCH"; then + echo "on-branch=true" >> "$GITHUB_OUTPUT" + else + # A tag outside the target branch is not an error — just skip. + echo "on-branch=false" >> "$GITHUB_OUTPUT" + echo "::notice::Tag $GITHUB_REF_NAME is not reachable from $TARGET_BRANCH; skipping deploy." + fi diff --git a/tests/deploy/package.json b/tests/deploy/package.json index 1b32b2a..aef5f26 100644 --- a/tests/deploy/package.json +++ b/tests/deploy/package.json @@ -2,6 +2,9 @@ "name": "deploy-action-test", "private": true, "type": "module", + "engines": { + "node": ">=22" + }, "dependencies": { "@tailor-platform/sdk": "latest" }, From 425d11fa531f61e17f5be04ace14b2e6984631bc Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 17:05:46 +0900 Subject: [PATCH 02/11] test: add packageManager and engines to the setup test fixture pnpm/action-setup and setup-node read the pnpm version and Node version from package.json; the deploy fixture lacked both, so the setup smoke test could not resolve them. --- tests/deploy/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/deploy/package.json b/tests/deploy/package.json index aef5f26..f91eded 100644 --- a/tests/deploy/package.json +++ b/tests/deploy/package.json @@ -2,6 +2,7 @@ "name": "deploy-action-test", "private": true, "type": "module", + "packageManager": "pnpm@10.33.4", "engines": { "node": ">=22" }, From 5a25c0c7aeea1f1c1449658650b0564d29e4087c Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 17:07:54 +0900 Subject: [PATCH 03/11] test: pin pnpm via packageManager at repo root for the setup smoke test pnpm/action-setup reads the pnpm version from the repository root package.json (not the working-directory), so the root needs a packageManager field for the setup action to resolve pnpm. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 84bac3e..0a94e17 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "private": true, + "packageManager": "pnpm@10.33.4", "scripts": { "lint": "actionlint", "test": "actrun workflow run .github/workflows/test-deploy.yaml --dry-run", From d1d4d8b21432db7e3cfee43375cfb6b6daed39f4 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 21:18:22 +0900 Subject: [PATCH 04/11] fix: address review feedback on the extracted actions - setup: run install-command via if/else (drop the implicit exit 0) and key setup-node caching to the working-directory lockfiles. - generate-check: detect drift with git status --porcelain instead of git add -A, so the index is not mutated. - tag-guard: normalize a fully-qualified refs/heads/ input to a short branch name. --- generate-check/action.yaml | 6 +++--- setup/action.yaml | 27 ++++++++++++++++----------- tag-guard/action.yaml | 8 +++++--- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/generate-check/action.yaml b/generate-check/action.yaml index a901c69..dd6f3c4 100644 --- a/generate-check/action.yaml +++ b/generate-check/action.yaml @@ -21,9 +21,9 @@ runs: - name: Check generated files are committed shell: bash run: | - git add -A - if ! git diff --cached --quiet; then - git --no-pager diff --cached --stat + CHANGES=$(git status --porcelain) + if [ -n "$CHANGES" ]; then + echo "$CHANGES" echo "::error::Generated files are out of date. Run 'tailor-sdk generate' locally and commit the result." exit 1 fi diff --git a/setup/action.yaml b/setup/action.yaml index 56af7b1..04822c8 100644 --- a/setup/action.yaml +++ b/setup/action.yaml @@ -37,6 +37,11 @@ runs: with: node-version-file: ${{ inputs.node-version-file }} cache: ${{ inputs.package-manager }} + cache-dependency-path: | + ${{ inputs.working-directory }}/pnpm-lock.yaml + ${{ inputs.working-directory }}/package-lock.json + ${{ inputs.working-directory }}/npm-shrinkwrap.json + ${{ inputs.working-directory }}/yarn.lock - name: Set up Bun if: inputs.package-manager == 'bun' @@ -51,15 +56,15 @@ runs: run: | if [ -n "$INSTALL_COMMAND" ]; then eval "$INSTALL_COMMAND" - exit 0 + else + case "$PACKAGE_MANAGER" in + pnpm) pnpm install --frozen-lockfile ;; + npm) npm ci ;; + yarn) yarn install --frozen-lockfile ;; + bun) bun install --frozen-lockfile ;; + *) + echo "::error::Unsupported package-manager '$PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)" + exit 1 + ;; + esac fi - case "$PACKAGE_MANAGER" in - pnpm) pnpm install --frozen-lockfile ;; - npm) npm ci ;; - yarn) yarn install --frozen-lockfile ;; - bun) bun install --frozen-lockfile ;; - *) - echo "::error::Unsupported package-manager '$PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)" - exit 1 - ;; - esac diff --git a/tag-guard/action.yaml b/tag-guard/action.yaml index aa48bf4..af82e8c 100644 --- a/tag-guard/action.yaml +++ b/tag-guard/action.yaml @@ -23,11 +23,13 @@ runs: env: TARGET_BRANCH: ${{ inputs.target-branch }} run: | - git fetch origin "$TARGET_BRANCH" - if git merge-base --is-ancestor "$GITHUB_SHA" "origin/$TARGET_BRANCH"; then + # Accept both short names (main) and fully-qualified refs (refs/heads/main). + BRANCH="${TARGET_BRANCH#refs/heads/}" + git fetch origin "$BRANCH" + if git merge-base --is-ancestor "$GITHUB_SHA" "origin/$BRANCH"; then echo "on-branch=true" >> "$GITHUB_OUTPUT" else # A tag outside the target branch is not an error — just skip. echo "on-branch=false" >> "$GITHUB_OUTPUT" - echo "::notice::Tag $GITHUB_REF_NAME is not reachable from $TARGET_BRANCH; skipping deploy." + echo "::notice::Tag $GITHUB_REF_NAME is not reachable from $BRANCH; skipping deploy." fi From 5e971b8d1efc33d68db6096b90687bea0b519646 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 21:23:42 +0900 Subject: [PATCH 05/11] test: drop setup smoke scaffolding from the extraction PR The repo's test workflows pin pnpm via an explicit version input, which conflicts with a root packageManager field (pnpm/action-setup errors on multiple specified versions). Rather than entangle the extraction PR with the repo's pnpm-version strategy, the new actions are exercised by the SDK-side E2E matrix when the templates are wired to them. --- .github/workflows/test-setup.yaml | 40 ------------------------------- package.json | 1 - tests/deploy/package.json | 9 ------- 3 files changed, 50 deletions(-) delete mode 100644 .github/workflows/test-setup.yaml diff --git a/.github/workflows/test-setup.yaml b/.github/workflows/test-setup.yaml deleted file mode 100644 index 11b7196..0000000 --- a/.github/workflows/test-setup.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Test Setup Action - -on: - pull_request: - paths: - - "setup/**" - - "tests/deploy/**" - - ".github/workflows/test-setup.yaml" - push: - branches: - - main - workflow_dispatch: - -concurrency: - group: test-setup-${{ github.ref }} - cancel-in-progress: true - -permissions: {} - -jobs: - test: - runs-on: ubuntu-latest - timeout-minutes: 10 - permissions: - contents: read - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - persist-credentials: false - - - name: Run setup action - uses: ./setup - with: - package-manager: pnpm - node-version-file: tests/deploy/package.json - working-directory: tests/deploy - - - name: Verify tailor-sdk is runnable - working-directory: tests/deploy - run: npx tailor-sdk --version diff --git a/package.json b/package.json index 0a94e17..84bac3e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "private": true, - "packageManager": "pnpm@10.33.4", "scripts": { "lint": "actionlint", "test": "actrun workflow run .github/workflows/test-deploy.yaml --dry-run", diff --git a/tests/deploy/package.json b/tests/deploy/package.json index f91eded..0f247c8 100644 --- a/tests/deploy/package.json +++ b/tests/deploy/package.json @@ -2,16 +2,7 @@ "name": "deploy-action-test", "private": true, "type": "module", - "packageManager": "pnpm@10.33.4", - "engines": { - "node": ">=22" - }, "dependencies": { "@tailor-platform/sdk": "latest" - }, - "pnpm": { - "overrides": { - "valibot": ">=1.2.0" - } } } From 7e29ca15d0741039c232b2c5a877e1bd46476e73 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 21:47:27 +0900 Subject: [PATCH 06/11] Revert setup cache-dependency-path change Pointing setup-node's cache-dependency-path at the working-directory lockfiles breaks the common monorepo layout where a single lockfile lives at the repo root (the subdir path resolves to nothing and setup-node errors). The default behavior (auto-detect from root) is correct for both single-package and root-lockfile monorepo setups. --- setup/action.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/setup/action.yaml b/setup/action.yaml index 04822c8..79edd6a 100644 --- a/setup/action.yaml +++ b/setup/action.yaml @@ -37,11 +37,6 @@ runs: with: node-version-file: ${{ inputs.node-version-file }} cache: ${{ inputs.package-manager }} - cache-dependency-path: | - ${{ inputs.working-directory }}/pnpm-lock.yaml - ${{ inputs.working-directory }}/package-lock.json - ${{ inputs.working-directory }}/npm-shrinkwrap.json - ${{ inputs.working-directory }}/yarn.lock - name: Set up Bun if: inputs.package-manager == 'bun' From a80a5b37282010cad886105972cb2d066f9adb74 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 22:03:39 +0900 Subject: [PATCH 07/11] fix(tag-guard): peel GITHUB_SHA to its commit before ancestry check For annotated tags GITHUB_SHA can reference the tag object rather than the commit, and git merge-base --is-ancestor expects commits. Peel with rev-parse ^{commit} (a no-op for a commit SHA) so the reachability check is reliable regardless of tag type. --- tag-guard/action.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tag-guard/action.yaml b/tag-guard/action.yaml index af82e8c..8f8fbef 100644 --- a/tag-guard/action.yaml +++ b/tag-guard/action.yaml @@ -26,7 +26,10 @@ runs: # Accept both short names (main) and fully-qualified refs (refs/heads/main). BRANCH="${TARGET_BRANCH#refs/heads/}" git fetch origin "$BRANCH" - if git merge-base --is-ancestor "$GITHUB_SHA" "origin/$BRANCH"; then + # GITHUB_SHA may be a tag object for annotated tags; peel to its commit + # so --is-ancestor (which expects commits) is reliable. + COMMIT="$(git rev-parse "${GITHUB_SHA}^{commit}")" + if git merge-base --is-ancestor "$COMMIT" "origin/$BRANCH"; then echo "on-branch=true" >> "$GITHUB_OUTPUT" else # A tag outside the target branch is not an error — just skip. From b37a41f0f491bac6619deb5ba8ba8d4a00cdff1e Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 22:12:28 +0900 Subject: [PATCH 08/11] fix(generate-check): run tailor-sdk with the project's package manager The extraction hard-coded 'npx tailor-sdk generate', which regressed the SDK template's per-PM exec: for Bun it relied on an ambient Node/npx being present, and bare npx can silently fall back to fetching a non-pinned tailor-sdk. Dispatch on a new package-manager input so each runtime uses its own runner (pnpm exec / yarn / bunx / npx) against the locally-installed, pinned version. --- generate-check/action.yaml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/generate-check/action.yaml b/generate-check/action.yaml index dd6f3c4..3293d03 100644 --- a/generate-check/action.yaml +++ b/generate-check/action.yaml @@ -5,6 +5,9 @@ description: >- committed. Requires Node.js and dependencies to be installed by the caller. inputs: + package-manager: + description: Package manager used to run tailor-sdk (pnpm, npm, yarn, or bun) + required: true working-directory: description: Working directory for the project (for monorepo setups) required: false @@ -16,7 +19,22 @@ runs: - name: Generate shell: bash working-directory: ${{ inputs.working-directory }} - run: npx tailor-sdk generate + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} + run: | + # Run the locally-installed tailor-sdk with each package manager's own + # runner, so the pinned version is used and Bun does not depend on an + # ambient Node/npx being present on the runner. + case "$PACKAGE_MANAGER" in + pnpm) pnpm exec tailor-sdk generate ;; + npm) npx tailor-sdk generate ;; + yarn) yarn tailor-sdk generate ;; + bun) bunx tailor-sdk generate ;; + *) + echo "::error::Unsupported package-manager '$PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)" + exit 1 + ;; + esac - name: Check generated files are committed shell: bash From 6f2aeffedd0c9bca8fda69192fa0f0b83106c4ad Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 22:31:40 +0900 Subject: [PATCH 09/11] feat: run tailor-sdk via the project package manager in deploy/plan Add an optional package-manager input to the deploy and plan actions and resolve a per-PM runner (pnpm exec / yarn / bunx / npx) once, so all tailor-sdk invocations use it. This stops Bun from depending on an ambient Node and ensures the locally-installed, pinned tailor-sdk is used. Defaults to npx when the input is empty, so existing v1.2 callers are unaffected. --- deploy/action.yaml | 30 +++++++++++++++++++++++++++--- plan/action.yaml | 28 ++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/deploy/action.yaml b/deploy/action.yaml index 7d5306d..1754ace 100644 --- a/deploy/action.yaml +++ b/deploy/action.yaml @@ -13,6 +13,13 @@ inputs: description: Working directory for the project (for monorepo setups) required: false default: "." + package-manager: + description: >- + Package manager used to run tailor-sdk (pnpm, npm, yarn, or bun). + Defaults to npx when empty, so Bun uses its own runtime instead of + relying on an ambient Node, and the locally-installed version is used. + required: false + default: "" platform-client-id: description: OAuth2 client ID for Tailor Platform machine user required: true @@ -37,13 +44,30 @@ runs: CLIENT_ID: ${{ inputs.platform-client-id }} CLIENT_SECRET: ${{ inputs.platform-client-secret }} + - name: Resolve tailor-sdk runner + shell: bash + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} + run: | + case "$PACKAGE_MANAGER" in + pnpm) RUN="pnpm exec" ;; + yarn) RUN="yarn" ;; + bun) RUN="bunx" ;; + npm|"") RUN="npx" ;; + *) + echo "::error::Unsupported package-manager '$PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)" + exit 1 + ;; + esac + echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" + - name: Login to Tailor Platform shell: bash working-directory: ${{ inputs.working-directory }} env: TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID: ${{ inputs.platform-client-id }} TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET: ${{ inputs.platform-client-secret }} - run: npx tailor-sdk login --machineuser + run: $TAILOR_SDK_RUN tailor-sdk login --machineuser - name: Validate workspace-id shell: bash @@ -60,11 +84,11 @@ runs: working-directory: ${{ inputs.working-directory }} env: TAILOR_PLATFORM_WORKSPACE_ID: ${{ inputs.workspace-id }} - run: npx tailor-sdk generate + run: $TAILOR_SDK_RUN tailor-sdk generate - name: Deploy shell: bash working-directory: ${{ inputs.working-directory }} env: TAILOR_PLATFORM_WORKSPACE_ID: ${{ inputs.workspace-id }} - run: npx tailor-sdk apply --yes + run: $TAILOR_SDK_RUN tailor-sdk apply --yes diff --git a/plan/action.yaml b/plan/action.yaml index f6e58ae..04d95fb 100644 --- a/plan/action.yaml +++ b/plan/action.yaml @@ -21,6 +21,13 @@ inputs: description: Working directory for the project (for monorepo setups) required: false default: "." + package-manager: + description: >- + Package manager used to run tailor-sdk (pnpm, npm, yarn, or bun). + Defaults to npx when empty, so Bun uses its own runtime instead of + relying on an ambient Node, and the locally-installed version is used. + required: false + default: "" platform-client-id: description: OAuth2 client ID for Tailor Platform machine user required: true @@ -53,13 +60,30 @@ runs: CLIENT_ID: ${{ inputs.platform-client-id }} CLIENT_SECRET: ${{ inputs.platform-client-secret }} + - name: Resolve tailor-sdk runner + shell: bash + env: + PACKAGE_MANAGER: ${{ inputs.package-manager }} + run: | + case "$PACKAGE_MANAGER" in + pnpm) RUN="pnpm exec" ;; + yarn) RUN="yarn" ;; + bun) RUN="bunx" ;; + npm|"") RUN="npx" ;; + *) + echo "::error::Unsupported package-manager '$PACKAGE_MANAGER' (expected pnpm, npm, yarn, or bun)" + exit 1 + ;; + esac + echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" + - name: Login to Tailor Platform shell: bash working-directory: ${{ inputs.working-directory }} env: TAILOR_PLATFORM_MACHINE_USER_CLIENT_ID: ${{ inputs.platform-client-id }} TAILOR_PLATFORM_MACHINE_USER_CLIENT_SECRET: ${{ inputs.platform-client-secret }} - run: npx tailor-sdk login --machineuser + run: $TAILOR_SDK_RUN tailor-sdk login --machineuser - name: Merge base branch if: github.event_name == 'pull_request' @@ -79,7 +103,7 @@ runs: TAILOR_PLATFORM_WORKSPACE_ID: ${{ inputs.workspace-id }} run: | set +e - OUTPUT=$(npx tailor-sdk apply --dry-run --yes 2>&1) + OUTPUT=$($TAILOR_SDK_RUN tailor-sdk apply --dry-run --yes 2>&1) EXIT_CODE=$? set -e From 4adceb273270d0b0553ee21c7d57e793ae401197 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 22:35:22 +0900 Subject: [PATCH 10/11] ci: suppress zizmor github-env false positive in deploy/plan runner The resolved runner written to GITHUB_ENV is one of four fixed literals selected by a case statement (never user input), so the env-file write cannot inject code. Annotate the line with a zizmor ignore + rationale. --- deploy/action.yaml | 4 +++- plan/action.yaml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/deploy/action.yaml b/deploy/action.yaml index 1754ace..b172604 100644 --- a/deploy/action.yaml +++ b/deploy/action.yaml @@ -59,7 +59,9 @@ runs: exit 1 ;; esac - echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" + # RUN is one of a fixed set of literals chosen by the case above (never + # user input), so writing it to the env file cannot inject code. + echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" # zizmor: ignore[github-env] - name: Login to Tailor Platform shell: bash diff --git a/plan/action.yaml b/plan/action.yaml index 04d95fb..5f839c2 100644 --- a/plan/action.yaml +++ b/plan/action.yaml @@ -75,7 +75,9 @@ runs: exit 1 ;; esac - echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" + # RUN is one of a fixed set of literals chosen by the case above (never + # user input), so writing it to the env file cannot inject code. + echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" # zizmor: ignore[github-env] - name: Login to Tailor Platform shell: bash From 92e52761d90ebf66e8d5db83342a4af10bfc19ea Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 15 Jun 2026 22:37:54 +0900 Subject: [PATCH 11/11] ci: place zizmor github-env ignore on the run line zizmor reports the env-file finding at the run: step line, so the inline ignore must sit there rather than on the echo line. --- deploy/action.yaml | 8 ++++---- plan/action.yaml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/deploy/action.yaml b/deploy/action.yaml index b172604..036dcd6 100644 --- a/deploy/action.yaml +++ b/deploy/action.yaml @@ -48,7 +48,9 @@ runs: shell: bash env: PACKAGE_MANAGER: ${{ inputs.package-manager }} - run: | + # RUN is one of a fixed set of literals chosen by the case below (never + # user input), so writing it to GITHUB_ENV cannot inject code. + run: | # zizmor: ignore[github-env] case "$PACKAGE_MANAGER" in pnpm) RUN="pnpm exec" ;; yarn) RUN="yarn" ;; @@ -59,9 +61,7 @@ runs: exit 1 ;; esac - # RUN is one of a fixed set of literals chosen by the case above (never - # user input), so writing it to the env file cannot inject code. - echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" # zizmor: ignore[github-env] + echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" - name: Login to Tailor Platform shell: bash diff --git a/plan/action.yaml b/plan/action.yaml index 5f839c2..4d00e4c 100644 --- a/plan/action.yaml +++ b/plan/action.yaml @@ -64,7 +64,9 @@ runs: shell: bash env: PACKAGE_MANAGER: ${{ inputs.package-manager }} - run: | + # RUN is one of a fixed set of literals chosen by the case below (never + # user input), so writing it to GITHUB_ENV cannot inject code. + run: | # zizmor: ignore[github-env] case "$PACKAGE_MANAGER" in pnpm) RUN="pnpm exec" ;; yarn) RUN="yarn" ;; @@ -75,9 +77,7 @@ runs: exit 1 ;; esac - # RUN is one of a fixed set of literals chosen by the case above (never - # user input), so writing it to the env file cannot inject code. - echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" # zizmor: ignore[github-env] + echo "TAILOR_SDK_RUN=$RUN" >> "$GITHUB_ENV" - name: Login to Tailor Platform shell: bash