From 4c0b034cd5f748d89cafc5243eb145dc25679961 Mon Sep 17 00:00:00 2001 From: dormouse-bot <287024035+dormouse-bot@users.noreply.github.com> Date: Wed, 16 Sep 2026 11:41:14 +0000 Subject: [PATCH] =?UTF-8?q?chore:=20update=20tend=20workflows=20(0.2.7=20?= =?UTF-8?q?=E2=86=92=200.2.8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/tend-ci-fix.yaml | 75 +---- .github/workflows/tend-mention-relay.yaml | 69 +++++ .github/workflows/tend-mention.yaml | 326 ++-------------------- .github/workflows/tend-nightly.yaml | 75 +---- .github/workflows/tend-notifications.yaml | 88 +----- .github/workflows/tend-review-runs.yaml | 75 +---- .github/workflows/tend-review.yaml | 79 +----- .github/workflows/tend-triage.yaml | 80 +----- .github/workflows/tend-weekly.yaml | 75 +---- 9 files changed, 129 insertions(+), 813 deletions(-) create mode 100644 .github/workflows/tend-mention-relay.yaml diff --git a/.github/workflows/tend-ci-fix.yaml b/.github/workflows/tend-ci-fix.yaml index 78d160067..b85f63b73 100644 --- a/.github/workflows/tend-ci-fix.yaml +++ b/.github/workflows/tend-ci-fix.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -15,7 +15,7 @@ on: jobs: fix-ci: - if: github.repository_owner == 'diffplug' && contains(fromJSON('["failure", "cancelled"]'), github.event.workflow_run.conclusion) + if: github.repository_owner == 'diffplug' && vars.TEND_ENABLED != 'false' && contains(fromJSON('["failure", "cancelled"]'), github.event.workflow_run.conclusion) concurrency: # A red branch fails every push that follows, each on its own commit, so # one session per branch — not per commit — is what collapses the burst. @@ -34,83 +34,14 @@ jobs: pull-requests: write actions: read steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: ref: main fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-mention-relay.yaml b/.github/workflows/tend-mention-relay.yaml new file mode 100644 index 000000000..4941dc5f8 --- /dev/null +++ b/.github/workflows/tend-mention-relay.yaml @@ -0,0 +1,69 @@ +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init +# +# Do not edit this file directly — it will be overwritten on regeneration. +# To customize behavior, edit the relevant skill (for example, +# `running-tend`) in this repo's .claude/skills/ directory, or open an issue at +# https://github.com/max-sixty/tend/issues for changes that need to +# happen upstream in the tend-ci-runner plugin. + +name: tend-mention-relay +# Review events for tend-mention. Their runs carry `refs/pull/N/merge`, which +# the operational secrets' environment does not admit, so this workflow holds +# no secret: it re-posts each event as a `repository_dispatch`, which starts +# tend-mention on the default branch. It is a workflow of its own because +# GitHub lists every job of a review event's run among the PR's checks, skipped +# jobs included, so the run holds only the job that can execute. Same-repo PRs +# only — the notifications poll covers fork PRs. +on: + pull_request_review: + types: [submitted] + # Not `created`: GitHub also fires `pull_request_review` for every new inline + # comment (probed across the comments endpoint, the replies endpoint, the + # "Add single comment" button, and reviews submitted with inline comments), + # so subscribing would relay each one twice. An edit fires no review event, + # so `edited` stays, to catch "@bot" added to an existing comment. + pull_request_review_comment: + types: [edited] + +jobs: + # Move the event onto a ref the environment admits, and nothing else. The + # merge ref itself can never be admitted — a same-repo `pull_request` run + # executes the PR head's own workflow files on that same ref, so admitting + # it would hand a pushed workflow the secrets the environment exists to + # deny. All judgement lives in tend-mention's `verify`, which re-reads the + # review or comment from the API, so a skipped event costs one extra short + # run rather than a second copy of the heuristics here. + relay: + # Fork PRs are excluded to hold long-standing behaviour: the notifications + # poll covers them, and it applies author-association tiers that this path + # does not. What the filter buys is that no fork head is checked out into a + # secret-bearing run; it is not an authorship gate, since a mention from + # any user with read access already wakes `handle` on a same-repo PR. + # Widening this is a separate decision, and moot while a fork run's token + # is refused the dispatch POST (403, probed). + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-24.04 + # No secrets here. `contents: write` is what the dispatch POST needs, and + # is the whole of what this token can do — probed both ways on a live + # repo: an otherwise identical same-repo run declaring `contents: read` + # is refused with 403, so a narrower token would leave every review + # mention unanswered rather than merely unprivileged. It grants nobody a + # new capability: only someone who can already push a branch can open the + # same-repo PR whose workflow file this is, and they could declare any + # permissions they liked in it. The merge restriction is what bounds that, + # here as everywhere. + permissions: + contents: write + steps: + # Identifiers only: `verify` re-reads the review or comment from the + # API, so the words the bot weighs and acts on are the ones GitHub + # holds, and a forged dispatch faces the same scrutiny as a relayed one. + - name: Re-enter on an admitted ref + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh api "repos/$GITHUB_REPOSITORY/dispatches" \ + -f event_type=tend-mention-review \ + -f "client_payload[kind]=${{ github.event_name }}" \ + -f "client_payload[pr]=${{ github.event.pull_request.number }}" \ + -f "client_payload[id]=${{ github.event.review.id || github.event.comment.id }}" diff --git a/.github/workflows/tend-mention.yaml b/.github/workflows/tend-mention.yaml index 0a7cdaaa3..c8772697b 100644 --- a/.github/workflows/tend-mention.yaml +++ b/.github/workflows/tend-mention.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -12,151 +12,15 @@ on: types: [edited] issue_comment: types: [created, edited] - # The review events reach only the `relay` job below: their runs carry - # `refs/pull/N/merge`, which the operational secrets' environment does not - # admit, so the secret-bearing jobs see them re-entered as a - # `repository_dispatch` instead. Same-repo PRs only — the notifications poll - # covers fork PRs. - pull_request_review: - types: [submitted] - # `created` is intentionally absent. Modern GitHub fires *both* - # pull_request_review and pull_request_review_comment for every newly-created - # inline comment (the standalone POST /pulls/PR/comments endpoint, the - # /replies endpoint, the "Add single comment" UI button, and reviews - # submitted with inline comments — all empirically verified). Subscribing to - # `created` would produce a duplicate dispatch whose handle runs collide on - # the tend-mention-handle-PR concurrency group, with the loser cancelled and - # posted as a CANCELLED check_run on the PR head SHA — which renders the - # PR's statusCheckRollup as FAILURE even though the bot did its job from the - # sibling run. Edits have no sibling event (review submissions don't fire on - # edits), so we still need to listen for `edited` to catch edit-to-summon - # ("@bot" added to an existing comment after the fact). - pull_request_review_comment: - types: [edited] - # The relay's re-entry point. GitHub creates this run even though a - # `GITHUB_TOKEN` triggered it — `workflow_dispatch` and `repository_dispatch` - # are the two events exempt from the rule that token-triggered events start - # no workflow — and it carries the default branch, which the environment - # admits. + # Review events arrive re-posted by tend-mention-relay. GitHub creates this + # run even though a `GITHUB_TOKEN` triggered it — `workflow_dispatch` and + # `repository_dispatch` are the two events exempt from the rule that + # token-triggered events start no workflow — and it carries the default + # branch, which the environment admits. repository_dispatch: types: [tend-mention-review] jobs: - # The secretless half of the review path: move the event onto a ref the - # environment admits, and nothing else. The merge ref itself can never be - # admitted — a same-repo `pull_request` run executes the PR head's own - # workflow files on that same ref, so admitting it would hand a pushed - # workflow the secrets the environment exists to deny. All judgement lives - # in `verify`, which re-reads the review or comment from the API, so a - # skipped event costs one extra short run rather than a second copy of the - # heuristics here. - relay: - # Fork PRs are excluded to hold long-standing behaviour: the notifications - # poll covers them, and it applies author-association tiers that this path - # does not. What the filter buys is that no fork head is checked out into a - # secret-bearing run; it is not an authorship gate, since a mention from - # any user with read access already wakes `handle` on a same-repo PR. - # Widening this is a separate decision, and moot while a fork run's token - # is refused the dispatch POST (403, probed). A dispatched run fails the - # event check, so the relay cannot re-enter itself. - if: | - (github.event_name == 'pull_request_review' || - github.event_name == 'pull_request_review_comment') && - github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-24.04 - # No secrets here. `contents: write` is what the dispatch POST needs, and - # is the whole of what this token can do — probed both ways on a live - # repo: an otherwise identical same-repo run declaring `contents: read` - # is refused with 403, so a narrower token would leave every review - # mention unanswered rather than merely unprivileged. It grants nobody a - # new capability: only someone who can already push a branch can open the - # same-repo PR whose workflow file this is, and they could declare any - # permissions they liked in it. The merge restriction is what bounds that, - # here as everywhere. - permissions: - contents: write - steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - # Identifiers only: `verify` re-reads the review or comment from the - # API, so the words the bot weighs and acts on are the ones GitHub - # holds, and a forged dispatch faces the same scrutiny as a relayed one. - - name: Re-enter on an admitted ref - if: steps.tend_enabled.outputs.enabled == 'true' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh api "repos/$GITHUB_REPOSITORY/dispatches" \ - -f event_type=tend-mention-review \ - -f "client_payload[kind]=${{ github.event_name }}" \ - -f "client_payload[pr]=${{ github.event.pull_request.number }}" \ - -f "client_payload[id]=${{ github.event.review.id || github.event.comment.id }}" - verify: # Skip comments on the issues tend files about its own health: the action # auto-comments on those when a run fails or is refused, and without this @@ -167,11 +31,12 @@ jobs: # enters as `repository_dispatch` and is judged in the check step below, # against the record the API holds. if: | - github.event_name == 'repository_dispatch' || - (github.event_name == 'issues' && - contains(github.event.issue.body, '@dormouse-bot')) || - (github.event_name == 'issue_comment' && - contains(github.event.issue.labels.*.name, 'tend-outage') == false && contains(github.event.issue.labels.*.name, 'tend-rate-limit') == false) + vars.TEND_ENABLED != 'false' && ( + github.event_name == 'repository_dispatch' || + (github.event_name == 'issues' && + contains(github.event.issue.body, '@dormouse-bot')) || + (github.event_name == 'issue_comment' && + contains(github.event.issue.labels.*.name, 'tend-outage') == false && contains(github.event.issue.labels.*.name, 'tend-rate-limit') == false)) runs-on: ubuntu-24.04 environment: name: tend @@ -184,83 +49,18 @@ jobs: url: ${{ steps.check.outputs.url }} ts: ${{ steps.check.outputs.ts }} steps: - - name: Check whether tend is enabled - id: tend_enabled + - uses: astral-sh/setup-uv@v10.1.0 + id: tend_uv env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - - uses: astral-sh/setup-uv@v10.0.1 - if: steps.tend_enabled.outputs.enabled == 'true' + UV_NO_MODIFY_PATH: "1" with: - version: "0.12.10" + version: "0.12.13" + enable-cache: false ignore-empty-workdir: true - name: Verify bot engagement id: check - if: steps.tend_enabled.outputs.enabled == 'true' run: | - uv run --script - <<'TEND_PY' + "$TEND_UV" run --script - <<'TEND_PY' # /// script # requires-python = ">=3.12" # dependencies = [] @@ -443,6 +243,7 @@ jobs: TEND_PY env: GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} + TEND_UV: ${{ steps.tend_uv.outputs.uv-path }} BOT_NAME: dormouse-bot EVENT_NAME: ${{ github.event_name }} COMMENT_BODY: ${{ github.event.comment.body }} @@ -472,73 +273,6 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY # Both halves of the reaction belong to this job, so the eyes can only # go on once the job that takes them off has started. Put them in # `verify` and `handle` respectively and the routine burst case strands @@ -548,13 +282,13 @@ jobs: # # The dispatch arm covers a relayed inline comment, whose id the check # step verified belongs to this PR; a review *submission* has no single - # comment to react to, so it gets no eyes — same as when the events - # arrived directly. The job's own `if` already carries `should_run`. + # comment to react to, so it gets no eyes. The job's own `if` already + # carries `should_run`. - name: React with eyes if: | - (steps.tend_enabled.outputs.enabled == 'true') && (((github.event.comment && contains(github.event.comment.body, '@dormouse-bot')) + ((github.event.comment && contains(github.event.comment.body, '@dormouse-bot')) || (github.event.client_payload.kind == 'pull_request_review_comment' - && needs.verify.outputs.reason == 'mention'))) + && needs.verify.outputs.reason == 'mention')) run: | gh api "repos/$REPO/$TARGET/reactions" -f content=eyes --silent \ || echo "::warning::could not add the eyes reaction" @@ -562,11 +296,10 @@ jobs: REPO: ${{ github.repository }} TARGET: ${{ github.event_name == 'issue_comment' && format('issues/comments/{0}', github.event.comment.id) - || format('pulls/comments/{0}', github.event.comment.id || github.event.client_payload.id) }} + || format('pulls/comments/{0}', github.event.client_payload.id) }} GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: fetch-depth: 0 fetch-tags: true @@ -574,7 +307,6 @@ jobs: - name: Compute queue delay id: delay - if: steps.tend_enabled.outputs.enabled == 'true' run: | if [ -z "$EVENT_TS" ]; then echo "seconds=" >> "$GITHUB_OUTPUT" @@ -587,8 +319,7 @@ jobs: # the API record — the dispatch payload never carries one to spoof. EVENT_TS: ${{ github.event.comment.updated_at || needs.verify.outputs.ts || github.event.issue.updated_at }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} @@ -622,10 +353,9 @@ jobs: - name: Remove the eyes reaction if: | always() - && (steps.tend_enabled.outputs.enabled == 'true') - && (((github.event.comment && contains(github.event.comment.body, '@dormouse-bot')) + && ((github.event.comment && contains(github.event.comment.body, '@dormouse-bot')) || (github.event.client_payload.kind == 'pull_request_review_comment' - && needs.verify.outputs.reason == 'mention'))) + && needs.verify.outputs.reason == 'mention')) run: | REACTION_ID=$(gh api --paginate \ "repos/$REPO/$TARGET/reactions?content=eyes&per_page=100" \ @@ -638,6 +368,6 @@ jobs: REPO: ${{ github.repository }} TARGET: ${{ github.event_name == 'issue_comment' && format('issues/comments/{0}', github.event.comment.id) - || format('pulls/comments/{0}', github.event.comment.id || github.event.client_payload.id) }} + || format('pulls/comments/{0}', github.event.client_payload.id) }} BOT_NAME: dormouse-bot GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} diff --git a/.github/workflows/tend-nightly.yaml b/.github/workflows/tend-nightly.yaml index aa97bbe9a..246662b74 100644 --- a/.github/workflows/tend-nightly.yaml +++ b/.github/workflows/tend-nightly.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -14,7 +14,7 @@ on: jobs: nightly: - if: github.repository_owner == 'diffplug' + if: github.repository_owner == 'diffplug' && vars.TEND_ENABLED != 'false' runs-on: ubuntu-24.04 environment: name: tend @@ -25,83 +25,14 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: ref: main fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-notifications.yaml b/.github/workflows/tend-notifications.yaml index 33de30ecb..a9a6415be 100644 --- a/.github/workflows/tend-notifications.yaml +++ b/.github/workflows/tend-notifications.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -17,7 +17,7 @@ jobs: concurrency: group: tend-notifications cancel-in-progress: false - if: github.repository_owner == 'diffplug' + if: github.repository_owner == 'diffplug' && vars.TEND_ENABLED != 'false' runs-on: ubuntu-24.04 environment: name: tend @@ -28,85 +28,21 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled + - uses: astral-sh/setup-uv@v10.1.0 + id: tend_uv env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - - uses: astral-sh/setup-uv@v10.0.1 - if: steps.tend_enabled.outputs.enabled == 'true' + UV_NO_MODIFY_PATH: "1" with: - version: "0.12.10" + version: "0.12.13" + enable-cache: false ignore-empty-workdir: true - name: Check for unread notifications and conflicted PRs id: check - if: steps.tend_enabled.outputs.enabled == 'true' env: GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} + TEND_UV: ${{ steps.tend_uv.outputs.uv-path }} run: | - uv run --script - <<'TEND_PY' + "$TEND_UV" run --script - <<'TEND_PY' # /// script # requires-python = ">=3.12" # dependencies = [] @@ -289,14 +225,14 @@ jobs: TEND_PY - uses: actions/checkout@v7 - if: (steps.tend_enabled.outputs.enabled == 'true') && (steps.check.outputs.count != '0' || steps.check.outputs.conflict_count != '0' || github.event_name == 'workflow_dispatch') + if: steps.check.outputs.count != '0' || steps.check.outputs.conflict_count != '0' || github.event_name == 'workflow_dispatch' with: ref: main fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: (steps.tend_enabled.outputs.enabled == 'true') && (steps.check.outputs.count != '0' || steps.check.outputs.conflict_count != '0' || github.event_name == 'workflow_dispatch') + - uses: max-sixty/tend/claude@0.2.8 + if: steps.check.outputs.count != '0' || steps.check.outputs.conflict_count != '0' || github.event_name == 'workflow_dispatch' with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-review-runs.yaml b/.github/workflows/tend-review-runs.yaml index 48c052770..ea32341de 100644 --- a/.github/workflows/tend-review-runs.yaml +++ b/.github/workflows/tend-review-runs.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -14,7 +14,7 @@ on: jobs: review-runs: - if: github.repository_owner == 'diffplug' + if: github.repository_owner == 'diffplug' && vars.TEND_ENABLED != 'false' runs-on: ubuntu-24.04 environment: name: tend @@ -25,83 +25,14 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: ref: main fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} diff --git a/.github/workflows/tend-review.yaml b/.github/workflows/tend-review.yaml index e9630568b..83f555ae8 100644 --- a/.github/workflows/tend-review.yaml +++ b/.github/workflows/tend-review.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -13,6 +13,7 @@ on: jobs: review: + if: vars.TEND_ENABLED != 'false' concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number }} # Preserve pending PR events within GitHub's queue limit: a push must not @@ -31,75 +32,7 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - name: React with eyes - if: steps.tend_enabled.outputs.enabled == 'true' run: | gh api "repos/$REPO/$TARGET/reactions" -f content=eyes --silent \ || echo "::warning::could not add the eyes reaction" @@ -112,14 +45,12 @@ jobs: # actions and their POST chains. The harness creates and selects the PR # topology in an independent disposable clone. - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} @@ -132,9 +63,7 @@ jobs: /tend-ci-runner:review ${{ github.event.pull_request.number }} - name: Remove the eyes reaction - if: | - always() - && (steps.tend_enabled.outputs.enabled == 'true') + if: always() run: | REACTION_ID=$(gh api --paginate \ "repos/$REPO/$TARGET/reactions?content=eyes&per_page=100" \ diff --git a/.github/workflows/tend-triage.yaml b/.github/workflows/tend-triage.yaml index 2e381d3e5..5962ea900 100644 --- a/.github/workflows/tend-triage.yaml +++ b/.github/workflows/tend-triage.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -17,7 +17,7 @@ concurrency: jobs: triage: - if: github.repository_owner == 'diffplug' && contains(github.event.issue.labels.*.name, 'tend-outage') == false && contains(github.event.issue.labels.*.name, 'tend-rate-limit') == false + if: github.repository_owner == 'diffplug' && vars.TEND_ENABLED != 'false' && contains(github.event.issue.labels.*.name, 'tend-outage') == false && contains(github.event.issue.labels.*.name, 'tend-rate-limit') == false runs-on: ubuntu-24.04 environment: name: tend @@ -28,75 +28,7 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - name: React with eyes - if: steps.tend_enabled.outputs.enabled == 'true' run: | gh api "repos/$REPO/$TARGET/reactions" -f content=eyes --silent \ || echo "::warning::could not add the eyes reaction" @@ -106,15 +38,13 @@ jobs: GITHUB_TOKEN: ${{ secrets.TEND_BOT_TOKEN }} - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: ref: main fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} @@ -127,9 +57,7 @@ jobs: /tend-ci-runner:triage ${{ github.event.issue.number }} - name: Remove the eyes reaction - if: | - always() - && (steps.tend_enabled.outputs.enabled == 'true') + if: always() run: | REACTION_ID=$(gh api --paginate \ "repos/$REPO/$TARGET/reactions?content=eyes&per_page=100" \ diff --git a/.github/workflows/tend-weekly.yaml b/.github/workflows/tend-weekly.yaml index bda054569..cd4f022df 100644 --- a/.github/workflows/tend-weekly.yaml +++ b/.github/workflows/tend-weekly.yaml @@ -1,4 +1,4 @@ -# Generated by tend 0.2.7. Regenerate with: uvx tend@latest init +# Generated by tend 0.2.8. Regenerate with: uvx tend@latest init # # Do not edit this file directly — it will be overwritten on regeneration. # To customize behavior, edit the relevant skill (for example, @@ -14,7 +14,7 @@ on: jobs: weekly: - if: github.repository_owner == 'diffplug' + if: github.repository_owner == 'diffplug' && vars.TEND_ENABLED != 'false' runs-on: ubuntu-24.04 environment: name: tend @@ -25,83 +25,14 @@ jobs: actions: read issues: write steps: - - name: Check whether tend is enabled - id: tend_enabled - env: - GH_TOKEN: ${{ github.token }} - run: | - gh api \ - -H "Accept: application/vnd.github.raw+json" \ - "repos/$GITHUB_REPOSITORY/contents/.config/tend.yaml" \ - > "$RUNNER_TEMP/tend.yaml" - ruby - "$RUNNER_TEMP/tend.yaml" <<'RUBY' >> "$GITHUB_OUTPUT" - # Inspect the parsed YAML node so the extra YAML 1.1 boolean words (yes/no/on/off) - # do not diverge from the YAML 1.2 parser used by `tend init`. - require "psych" - - path = ARGV.fetch(0) - documents = Psych.parse_stream(File.read(path, mode: "r:bom|utf-8")).children - unless documents.length == 1 - abort "tend config must contain exactly one YAML document" - end - - mapping = documents.first.root - unless mapping.is_a?(Psych::Nodes::Mapping) - abort "tend config must contain a YAML mapping" - end - - def has_yaml_merge_key?(node) - case node - when Psych::Nodes::Mapping - node.children.each_slice(2).any? do |key, value| - (key.is_a?(Psych::Nodes::Scalar) && key.plain && key.value == "<<") || - has_yaml_merge_key?(key) || has_yaml_merge_key?(value) - end - when Psych::Nodes::Sequence - node.children.any? { |value| has_yaml_merge_key?(value) } - else - false - end - end - - if has_yaml_merge_key?(mapping) - abort "tend config: YAML merge keys (<<) are not supported" - end - - matches = mapping.children.each_slice(2).select do |key, _value| - key.is_a?(Psych::Nodes::Scalar) && key.value == "enabled" - end - abort "tend config: enabled must appear at most once" if matches.length > 1 - - value = matches.dig(0, 1) - enabled = true - if value - bool_tag = value.respond_to?(:tag) && value.tag == "tag:yaml.org,2002:bool" - literal = value.value.downcase if value.is_a?(Psych::Nodes::Scalar) - unless value.is_a?(Psych::Nodes::Scalar) && - (value.plain || bool_tag) && - ["true", "false"].include?(literal) - abort "tend config: enabled must be true or false" - end - enabled = literal == "true" - end - - puts "enabled=#{enabled}" - - unless enabled - warn "::notice title=Tend disabled::The tend config sets enabled: false; skipping this job" - end - RUBY - uses: actions/checkout@v7 - if: steps.tend_enabled.outputs.enabled == 'true' with: ref: main fetch-depth: 0 fetch-tags: true token: ${{ secrets.TEND_BOT_TOKEN }} - - uses: max-sixty/tend/claude@0.2.7 - if: steps.tend_enabled.outputs.enabled == 'true' + - uses: max-sixty/tend/claude@0.2.8 with: github_token: ${{ secrets.TEND_BOT_TOKEN }} claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}