-
Notifications
You must be signed in to change notification settings - Fork 0
feat(ci): ワークフロー検査をリポジトリ横断で実行できるようにする #1075
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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[@]}" |
| 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" | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # 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 | ||||||||||||||||||||||||
|
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎.
Comment on lines
+135
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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}")
PYRepository: 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 -nRepository: 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
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 The current test suite does not cover this path. 🐛 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| 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 | ||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
If you want the guard to accept that pattern, track a workflow-level flag before ♻️ 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||
| { 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:" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.