Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/fleet-workflow-guards.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Fleet Workflow Guards
#
# repo-maintenance のワークフロー検査を、直近いじったリポジトリ全体に対して実行する。
# 検査は bash と awk だけで動き、対象リポジトリのワークフロー YAML を読むだけなので、
# 各リポジトリへスクリプトを配布せずここから一括で走らせられる。検査を直せば次回の
# 実行から全リポジトリへ反映される。
#
# 走査は読み取りのみ。対象リポジトリへ Issue や PR は作らない。
name: Fleet Workflow Guards

on:
schedule:
- cron: '0 22 * * 0' # 毎週月曜 07:00 JST
workflow_dispatch:
inputs:
days:
description: '対象とする最終 push からの日数'
required: false
default: '90'
repos:
description: '走査するリポジトリ名(空白区切り)。空なら自動検出'
required: false
default: ''

permissions: {}

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

jobs:
scan:
name: Scan repositories
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Checkout config
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1

# 他リポジトリを読むため PAT が要る。GITHUB_TOKEN は自リポジトリしか見えない。
- name: Run workflow guards across repositories
env:
GH_TOKEN: ${{ secrets.CLAUDE_PAT }}
FLEET_DAYS: ${{ inputs.days || '90' }}
FLEET_REPOS: ${{ inputs.repos || '' }}
run: |
args=(--days "$FLEET_DAYS")
if [ -n "$FLEET_REPOS" ]; then
args+=(--repos "$FLEET_REPOS")
fi
script/fleet-workflow-guards.sh "${args[@]}"
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,24 @@ Development infrastructure template repository providing DevContainer images, CI

| Directory | Purpose |
| -------------------- | ----------------------------------------------------------- |
| `.claude-plugin/` | Claude Code LSP plugin configuration |
| `.claude/agents/` | Claude Code specialized agents |
| `.claude/commands/` | Claude Code slash commands |
| `.claude/hooks/` | Pre/post hook scripts for quality enforcement |
| `.claude/plugins/` | Claude Code plugin configuration |
| `.claude/rules/` | Claude Code rules for development standards |
| `.claude/skills/` | Claude Code skill definitions |
| `.claude-plugin/` | Claude Code LSP plugin configuration |
| `.codex/` | Codex AI agent configuration |
| `.context/` | Shared intermediate artifacts (complexity reports etc.) |
| `.cursor/` | Cursor editor settings |
| `.devcontainer/` | DevContainer configuration and Dockerfile |
| `.gemini/` | Gemini AI agent configuration |
| `.github/workflows/` | GitHub Actions CI/CD workflows (15 workflows) |
| `.github/workflows/` | GitHub Actions CI/CD workflows (16 workflows) |
| `.husky/` | Git hooks (pre-commit, commit-msg) |
| `.takt/` | TAKT workflow automation for scheduled agent tasks |
| `.vscode/` | VS Code workspace settings |
| `.zsh/` | Zsh configuration (aliases, completions, functions, prompt) |
| `automation/` | Weekly/screenshot ingest adapters and threshold rules |
| `automation/` | Weekly and screenshot ingest adapters and threshold rules |
| `brew/` | Homebrew package management (Linux only) |
| `credentials/` | Credential templates and filtering documentation |
| `docs/` | Documentation and ADRs |
Expand Down Expand Up @@ -141,6 +141,7 @@ Development infrastructure template repository providing DevContainer images, CI
| `coverage-report.yml` | Coverage Report |
| `dependabot-auto-merge.yml` | Dependabot Auto-merge |
| `docker-image.yml` | Build and Release DevContainer Image |
| `fleet-workflow-guards.yml` | Fleet Workflow Guards |
| `label-sync.yml` | Label Sync |
| `manual-release.yml` | Manual Release |
| `quality-gate-fallback.yml` | CI Fallback |
Expand Down
23 changes: 23 additions & 0 deletions script/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ This directory contains utility scripts for managing configuration, credentials,
| `version.sh` | Semantic versioning | Makefile |
| `update-agents-md.sh` | AGENTS.md 自動生成セクション更新 | repo-maintenance |
| `repo-maintenance.sh` | Repository maintenance executable workflow | `/repo-maintenance` |
| `fleet-workflow-guards.sh` | Workflow guards across every recently pushed repository | fleet-workflow-guards |
| `validate-takt-auth.sh` | Fail fast on rejected Anthropic credentials | scheduled-maintenance |
| `trust-claude-workspace.sh` | Trust a workspace in `~/.claude.json` for headless runs | scheduled-maintenance |
| `setup-ci.sh` | CI/CD workflow setup | `/setup-ci` |
Expand Down Expand Up @@ -195,6 +196,28 @@ The workflow guards (`--check-claude-action-credentials`, `--check-self-cancelli
`.github/workflows/templates/`, and `templates/workflows/`. `ci.yml` runs all four in its
Workflow Lint job so regressions fail the pull request.

### fleet-workflow-guards.sh

Runs the repo-maintenance workflow guards across several repositories at once.
The guards only read workflow YAML, so they run unchanged against any checkout;
keeping them here means a fix to a guard reaches every repository on the next
run without distributing this script downstream.

The scan is read-only. It never opens issues or pull requests in the scanned
repositories.

**Usage**:

```bash
./script/fleet-workflow-guards.sh # 直近90日に push されたリポジトリ
./script/fleet-workflow-guards.sh --days 30
./script/fleet-workflow-guards.sh --repos "config ohana"
```

Requires `gh` authenticated with a token that can read the target repositories.
`.github/workflows/fleet-workflow-guards.yml` runs it weekly with `CLAUDE_PAT`
and writes the per-repository result to the job summary.

### setup-ci.sh

Detects project type and package manager, then writes CI/CD workflow defaults.
Expand Down
185 changes: 185 additions & 0 deletions script/fleet-workflow-guards.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#!/usr/bin/env bash
# Run the repo-maintenance workflow guards across several repositories.
#
# The guards only read workflow YAML, so they run unchanged against any
# repository checkout. Keeping them here means a fix to a guard reaches every
# repository at once, without distributing this script downstream.
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=script/lib/output.sh
source "$SCRIPT_DIR/lib/output.sh"

OWNER="${FLEET_OWNER:-keito4}"
DAYS="90"
REPOS=""
WORK_DIR="${FLEET_WORK_DIR:-${CONTEXT_DIR:-.context}/fleet}"

GUARDS=(
--check-claude-action-credentials
--check-self-cancelling-workflows
--check-gh-repo-context
--check-artifact-retention
)

usage() {
cat <<'EOF'
Usage: script/fleet-workflow-guards.sh [--owner OWNER] [--days N] [--repos "name ..."]

Scans each repository with the repo-maintenance workflow guards and prints a
markdown summary. Exits non-zero when any repository reports a violation.

--owner OWNER GitHub owner to scan (default: keito4)
--days N Only scan repositories pushed within N days (default: 90)
--repos "..." Scan exactly these repository names, skipping discovery
EOF
}

while [[ $# -gt 0 ]]; do
case "$1" in
--owner)
OWNER="${2:?--owner requires a value}"
shift 2
;;
--days)
DAYS="${2:?--days requires a value}"
shift 2
;;
--repos)
REPOS="${2:?--repos requires a value}"
shift 2
;;
-h | --help)
usage
exit 0
;;
*)
output::fatal "Unknown argument: $1"
;;
esac
done

validate_inputs() {
local repo

if [[ ! "$DAYS" =~ ^[1-9][0-9]*$ ]]; then
# 検証しないと date が失敗して cutoff が空になり、全リポジトリへ黙って広がる。
output::fatal "invalid --days: $DAYS (expected a positive integer)"
fi

for repo in $REPOS; do
# rm -rf する先を作るので、作業ディレクトリの外を指す名前は受け付けない。
if [[ ! "$repo" =~ ^[A-Za-z0-9._-]+$ || "$repo" == .* ]]; then
output::fatal "invalid repository name: $repo"
fi
done
}

discover_repos() {
local cutoff
# 直近いじったリポジトリだけを対象にする。アーカイブ済みは除外する。
cutoff="$(date -u -v-"${DAYS}"d +%Y-%m-%d 2>/dev/null || date -u -d "${DAYS} days ago" +%Y-%m-%d)"
gh repo list "$OWNER" \
--limit 200 \
--no-archived \
--json name,pushedAt \
--jq "[.[] | select(.pushedAt >= \"$cutoff\")] | .[].name"
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# 1 リポジトリ分の違反行を stdout へ出す。違反が無ければ何も出さない。
# チェックアウトへ入れないときは 2 を返す。ここを握り潰すと、走査できていないのに
# 「違反なし」と報告してしまう。
scan_repo() {
local dest="$1" guard output guard_status

[[ -d "$dest" ]] || return 2

for guard in "${GUARDS[@]}"; do
# 終了ステータスを正とする。検査によっては output::warning ではなく素の
# "file: message" を出すため、⚠ 行だけを拾うと違反が消える。依存不足などの
# 違反以外の失敗も、黙って clean にせずここで拾う。
# 1 つ落ちても残りを続け、まとめて直せるようにする。
guard_status=0
output="$(cd "$dest" || exit 1; "$SCRIPT_DIR/repo-maintenance.sh" "$guard" 2>&1)" || guard_status=$?
[[ "$guard_status" -eq 0 ]] && continue

if [[ -n "$output" ]]; then
printf '%s\n' "$output"
else
printf '%s: failed with no output\n' "$guard"
fi
done
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

main() {
local repos repo dest violations=0 summary=""

validate_inputs

if [[ -n "$REPOS" ]]; then
read -r -a repos <<<"$REPOS"
else
mapfile -t repos < <(discover_repos)
fi

if [[ "${#repos[@]}" -eq 0 ]]; then
output::warning "No repositories to scan"
return 0
fi

mkdir -p "$WORK_DIR"
summary+="## Workflow guard scan"$'\n\n'
summary+="| Repository | Result |"$'\n'
summary+="| --- | --- |"$'\n'

for repo in "${repos[@]}"; do
[[ -n "$repo" ]] || continue
dest="$WORK_DIR/$repo"
rm -rf "$dest"
Comment on lines +137 to +138

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject repository names that escape the work directory

When --repos receives an invalid name containing traversal components, such as ../.., dest resolves outside WORK_DIR and this unconditional rm -rf can delete the repository checkout or other parent data before gh rejects the name. Because the script is documented for direct local use and the workflow exposes repos as free-form input, validate repository names before using them as paths or derive a path-safe destination.

Useful? React with 👍 / 👎.

Comment on lines +135 to +138

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching fleet-workflow-guards.sh:\n'
fd -a 'fleet-workflow-guards\.sh$' . || true

file="$(fd 'fleet-workflow-guards\.sh$' . | head -n 1 || true)"
if [[ -n "$file" ]]; then
  printf '\nOutline:\n'
  ast-grep outline "$file" || true
  printf '\nRelevant lines:\n'
  sed -n '1,170p' "$file" | cat -n
fi

printf '\nSearch for script/fleet-workflow-guards.sh references and repos input/argument usage:\n'
rg -n --hidden --glob '!*.lock' --glob '!node_modules/**' 'fleet-workflow-guards\.sh|\\-\\-repos|repos=|workflow_dispatch|inputs\.repos|output::fatal|rm -rf' .

Repository: keito4/config

Length of output: 17642


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import re
for bad in ["foo/bar", "foo/..", "../etc", "/etc", "foo\\0bar"]:
    dest = "/tmp/work/" + bad
    print(f"{bad!r}: dest={dest!r}, starts_with={dest.startswith('/tmp/work/')}, contains_dotdot={bad.endswith('/..') or '/..' in bad}")
PY

Repository: keito4/config

Length of output: 529


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Workflow relevant lines:\n'
sed -n '1,80p' .github/workflows/fleet-workflow-guards.yml | cat -n

printf '\nTest cases touching fleet-workflow-guards validation/repo names:\n'
sed -n '1,260p' test/fleet-workflow-guards.test.js | cat -n

Repository: keito4/config

Length of output: 11410


Path Traversal (CWE-22): Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

Reject invalid repository names before constructing dest.

--repos comes from FLEET_REPOS, so values like ../evil, /etc/passwd, or embedded nulls are passed unchecked to rm -rf "$WORK_DIR/$repo" before the clone check. Gate repo before assigning dest, e.g. match an allowed repository-name pattern and reject names containing /, .., or other path-dangerous characters.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@script/fleet-workflow-guards.sh` around lines 107 - 110, Add path-safety
validation for the repo variable in the loop immediately after the existing
non-empty check and before assigning dest. Reject any repo value containing path
traversal characters such as `/`, `..`, embedded nulls, or other dangerous
characters by adding a validation check that continues to the next iteration if
the name fails. This ensures only safe repository names reach the dest
assignment and the rm -rf command.


if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
output::warning "$repo: clone failed; skipped"
summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
continue
fi
Comment on lines +140 to +144

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

A clone failure is reported as no violations and the script exits 0.

The continue at line 115 skips the violations increment. The summary row shows ⚠️ clone failed, but violations stays 0. If every repository fails to clone, line 143 prints "No workflow guard violations across N repositories" and line 154 returns success.

This contradicts the stated behavior that a repository which cannot be scanned must be aggregated as a violation. The missing-checkout path at line 121 already increments violations; make the clone-failure path consistent.

The current test suite does not cover this path. does not report clean when the checkout is missing exercises the scan_status -eq 2 branch, because the gh stub exits 0 for unknown repositories. Add a test where gh repo clone exits non-zero.

🐛 Proposed fix
     if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
+      violations=$((violations + 1))
       output::warning "$repo: clone failed; skipped"
       summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
       continue
     fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
output::warning "$repo: clone failed; skipped"
summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
continue
fi
if ! gh repo clone "$OWNER/$repo" "$dest" -- --depth 1 --no-tags >/dev/null 2>&1; then
violations=$((violations + 1))
output::warning "$repo: clone failed; skipped"
summary+="| \`$repo\` | ⚠️ clone failed |"$'\n'
continue
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@script/fleet-workflow-guards.sh` around lines 112 - 116, Increment the
violations counter in the clone-failure branch before continuing, matching the
existing missing-checkout handling so failed scans produce a nonzero exit status
and are not reported as clean. Add test coverage for a gh repo clone invocation
that exits nonzero, asserting the failure is summarized as a violation and the
script returns failure.


local findings scan_status=0
findings="$(scan_repo "$dest")" || scan_status=$?

if [[ "$scan_status" -eq 2 ]]; then
violations=$((violations + 1))
output::warning "$repo: checkout missing; not scanned"
summary+="| \`$repo\` | ⚠️ not scanned |"$'\n'
continue
fi

if [[ -n "$findings" ]]; then
violations=$((violations + 1))
output::warning "$repo"
printf '%s\n' "$findings"
summary+="| \`$repo\` | ❌ $(printf '%s' "$findings" | grep -c . ) violation(s) |"$'\n'
while IFS= read -r line; do
[[ -n "$line" ]] || continue
summary+="| | $(printf '%s' "$line" | sed 's/\x1b\[[0-9;]*m//g; s/^[[:space:]]*⚠[[:space:]]*//') |"$'\n'
done <<<"$findings"
else
output::success "$repo"
summary+="| \`$repo\` | ✅ clean |"$'\n'
fi
done

if [[ "$violations" -eq 0 ]]; then
summary+=$'\n'"No workflow guard violations across ${#repos[@]} repositories."$'\n'
output::success "No workflow guard violations across ${#repos[@]} repositories"
else
summary+=$'\n'"$violations of ${#repos[@]} repositories reported violations."$'\n'
fi

if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
printf '%s' "$summary" >>"$GITHUB_STEP_SUMMARY"
fi

[[ "$violations" -eq 0 ]]
}

main
35 changes: 26 additions & 9 deletions script/lib/repo_maintenance_checks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -183,22 +183,39 @@ check_gh_repo_context() {
[[ -n "$workflow" ]] || continue

# ジョブ単位で判定する。あるジョブの checkout や GH_REPO は別ジョブの gh を設定しない。
# --repo は同じコマンド行にある場合だけ有効とみなす。
# 行継続は 1 つの論理行に結合してから見る。--repo が継続行にある書き方は普通で、
# 同一行しか見ないと正常動作しているワークフローを誤検知する。
if ! awk '
function flush() {
if (in_job && bad_cmd && !has_checkout && !has_gh_repo) bad = 1
bad_cmd = 0; has_checkout = 0; has_gh_repo = 0
}
function eval_line(l) {
if (!in_job) return
if (l ~ /actions\/checkout/) has_checkout = 1
if (l ~ /^[[:space:]]*GH_REPO:/) has_gh_repo = 1
if (l ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/) {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (l !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
}
}
Comment on lines +193 to +201

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

GH_REPO declared at workflow level is not detected.

eval_line returns early when in_job is 0. A workflow that declares GH_REPO in the top-level env: block (before jobs:) satisfies every job, but the guard never records it. Such a workflow is reported as a violation.

If you want the guard to accept that pattern, track a workflow-level flag before jobs: and seed each job with it.

♻️ Proposed change
       function flush() {
-        if (in_job && bad_cmd && !has_checkout && !has_gh_repo) bad = 1
+        if (in_job && bad_cmd && !has_checkout && !has_gh_repo && !global_gh_repo) bad = 1
         bad_cmd = 0; has_checkout = 0; has_gh_repo = 0
       }
       function eval_line(l) {
-        if (!in_job) return
+        if (!in_jobs && l ~ /^[[:space:]]*GH_REPO:/) { global_gh_repo = 1; return }
+        if (!in_job) return
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function eval_line(l) {
if (!in_job) return
if (l ~ /actions\/checkout/) has_checkout = 1
if (l ~ /^[[:space:]]*GH_REPO:/) has_gh_repo = 1
if (l ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/) {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (l !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
}
}
function flush() {
if (in_job && bad_cmd && !has_checkout && !has_gh_repo && !global_gh_repo) bad = 1
bad_cmd = 0; has_checkout = 0; has_gh_repo = 0
}
function eval_line(l) {
if (!in_jobs && l ~ /^[[:space:]]*GH_REPO:/) { global_gh_repo = 1; return }
if (!in_job) return
if (l ~ /actions\/checkout/) has_checkout = 1
if (l ~ /^[[:space:]]*GH_REPO:/) has_gh_repo = 1
if (l ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/) {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (l !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@script/lib/repo_maintenance_checks.sh` around lines 193 - 201, Update
eval_line to detect GH_REPO declarations before entering a job, store that
workflow-level state, and seed each job’s has_gh_repo value from it when the job
begins. Preserve the existing per-job detection and command validation behavior.

{ line = $0; sub(/^[[:space:]]*#.*$/, "", line) }
/^jobs:[[:space:]]*$/ { in_jobs = 1; next }
in_jobs && /^ [A-Za-z0-9_-]+:/ { flush(); in_job = 1; next }
in_job && line ~ /actions\/checkout/ { has_checkout = 1 }
in_job && line ~ /^[[:space:]]*GH_REPO:/ { has_gh_repo = 1 }
in_job && line ~ /(^|[^A-Za-z0-9_-])gh[[:space:]]+((label|issue|release|run|workflow)|pr[[:space:]]+(create|list|status))([^A-Za-z0-9_-]|$)/ {
# gh は -R / --repo= も受け付ける。落とすと正当なワークフローを止める。
if (line !~ /(--repo[[:space:]=]|-R[[:space:]])/) bad_cmd = 1
pending == "" && /^jobs:[[:space:]]*$/ { in_jobs = 1; next }
pending == "" && in_jobs && /^ [A-Za-z0-9_-]+:/ { flush(); in_job = 1; next }
{
if (pending != "") { line = pending " " line; pending = "" }
if (line ~ /\\[[:space:]]*$/) {
sub(/\\[[:space:]]*$/, "", line)
pending = line
next
}
eval_line(line)
}
END {
if (pending != "") eval_line(pending)
flush()
exit bad ? 1 : 0
}
END { flush(); exit bad ? 1 : 0 }
' "$workflow"; then
output::warning "$(basename "$workflow"): gh has no repository to resolve without a checkout"
echo "Add the repository to the step environment:"
Expand Down
Loading
Loading