Skip to content
Closed
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
117 changes: 110 additions & 7 deletions .github/docs/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,119 @@ Dispatches a `cleanup_caches` event via GitHub API.

---

## 6. check_vendored_skills.yml — Detect Vendored-Skill Updates

| Property | Value |
|----------|-------|
| **Trigger** | Scheduled: **daily at 12:00 UTC** + `workflow_dispatch` |
| **Concurrency** | `check-vendored-skills`, `cancel-in-progress: false` |
| **Branch** | `feature__vendored_skills_refresh` (bot-owned; force-reset from main on every run that finds updates) |

Detects when either upstream agent-skill repo has cut a release newer than the copy vendored under `.agents/skills/`, and opens a PR that refreshes them:

- `DailybotHQ/deepworkplan-skill` → `.agents/skills/deepworkplan/`
- `DailybotHQ/agent-skill` → `.agents/skills/dailybot/`

When that PR merges, `release_and_publish.yml` (Section 5) fires normally — so **a new upstream skill release directly causes a new website release** that ships with the refreshed skills vendored inside. No manual step in between.

Companion workflow `check_and_merge_vendored_skills_pr.yml` (Section 7) auto-merges the PR once CI is green.

### Job: `check_vendored_skills`

| Step | Name | What it does |
|------|------|-------------|
| — | Checkout | `actions/checkout@v4` with `AUTOMATION_GITHUB_TOKEN`, `fetch-depth: 0` |
| — | Setup Node | 24.18.0 |
| 1 | Setup GitHub Config | Git config + `gh auth login` |
| 2 | Resolve upstream tags vs vendored versions | For each skill: `gh release view --repo <owner/repo> --json tagName -q .tagName` (unless a `workflow_dispatch` input pins it). Reads current vendored versions from `.agents/skills/*/SKILL.md`. Computes `updates_available`. |
| 3 | Reset feature branch from main | If `updates_available`: deletes the remote branch (if any) and re-branches from main so every run starts on a clean base |
| 4a | Install deepworkplan at resolved tag | `npx --yes skills add DailybotHQ/deepworkplan-skill@<tag> --skill deepworkplan --force -y` + version-match invariant assertion |
| 4b | Install dailybot at resolved tag | Same pattern for `DailybotHQ/agent-skill@<tag>` |
| 5 | Stage vendored files and check for actual changes | Stages `.agents/skills/{deepworkplan,dailybot}/` and `skills-lock.json`; short-circuits if `git diff --cached --quiet` (pinned to current) |
| 6 | Commit and push feature branch | Commits `chore: refresh vendored skills to (deepworkplan vX.Y.Z, dailybot vA.B.C)` and force-with-lease pushes to `feature__vendored_skills_refresh` |
| 7 | Open (or update) the pull request | `gh pr create` (or `gh pr edit` if a PR from that branch is already open) with a diff-style body listing the from/to versions and release-notes links |

### Manual override

Run from the Actions tab (`workflow_dispatch`) with optional inputs to pin either skill to a specific tag:

- `deepworkplan_tag` — e.g. `v2.16.3` (blank = latest release)
- `dailybot_tag` — e.g. `v3.10.3` (blank = latest release)

Useful for previewing a pre-release, or intentionally rolling back to an older tag.

### Failure modes

| Failure | Behavior |
|---------|----------|
| Both skills already at latest | Job exits 0 with a summary; no branch, no commit, no PR |
| Upstream repo has no releases / API blip | Warning in job summary; that skill is skipped, the other continues |
| `npx skills add` fails | **Fails the job** — treated as real breakage (the whole point of the smoke test) |
| Vendored `SKILL.md` version does not match the requested tag | **Fails the job** — refuses to open a PR whose title misrepresents its contents |
| No diff after install (rare pin-to-current case) | Skips commit + PR; exits 0 |

**Secrets:** `AUTOMATION_GITHUB_TOKEN`

---

## 7. check_and_merge_vendored_skills_pr.yml — Auto-Merge Vendored-Skill Refresh PR

| Property | Value |
|----------|-------|
| **Trigger** | Scheduled: **daily at 17:00 UTC** + `workflow_dispatch` |
| **Timing** | Runs 5 hours after the check workflow to allow CI (`code_check.yml`, `pull_request_check.yml`) to complete |
| **Concurrency** | `check-and-merge-vendored-skills`, `cancel-in-progress: false` |

Mirrors the pattern of `check_and_merge_packages_upgrades_pr.yml` (Section 4) but targeting the vendored-skills refresh PR.

### Job: `check_and_merge_vendored_skills_pr`

| Step | Name | What it does |
|------|------|-------------|
| — | Checkout | `actions/checkout@v4` with `AUTOMATION_GITHUB_TOKEN` |
| — | Setup Node | 24.18.0 |
| 1 | Setup GitHub Config | Git config + `gh auth login` |
| 2 | Locate PR | `gh pr list -B main -s open -H feature__vendored_skills_refresh` — exits 0 if no PR |
| 3 | Read PR title/body | For the run summary only |
| 4 | Check mergeable state and merge if clean | `gh api repos/:owner/:repo/pulls/:n` → if `mergeable_state == "clean"`, `gh pr merge <n> --merge`. Otherwise leaves PR open. |

**Key behavior:** Only merges when GitHub reports `mergeable_state == "clean"` (all required checks passed AND no conflicts AND branch up to date with base). Any other state (`blocked`, `unstable`, `dirty`, `unknown`, ...) leaves the PR open for a human to review. The workflow never force-merges.

**Merge triggers a release:** Because `release_and_publish.yml` runs on `pull_request: closed && merged == true`, the merge here directly cuts a new website release (bumps `package.json`, tags `vX.Y.Z`, publishes a GitHub Release). This is intentional — a new upstream skill release IS the reason for a new website release.

**Opting out of an individual auto-merge:** Close the PR (or push additional commits so it goes non-clean) before 17:00 UTC. The next day's check run will re-open it with the same target tags if they haven't advanced.

**Secrets:** `AUTOMATION_GITHUB_TOKEN`

---

## Workflow Dependencies

```
check_pr_size_label
release_and_publish
cleanup_caches
┌──────────────────────────────┐
│ Upstream skill release │
│ (deepworkplan / agent-skill)│
└──────────────┬───────────────┘
12:00 UTC daily ▶ check_vendored_skills.yml (or manual)
opens/updates PR
code_check + pull_request_check (on PR)
17:00 UTC daily ▶ check_and_merge_vendored_skills_pr.yml
gh pr merge --merge (if clean)
release_and_publish.yml (Section 5)
check_pr_size_label ──▶ release_and_publish ──▶ cleanup_caches
Cloudflare Pages deploy (on push to main)
```

**Note:** Cloudflare Pages deploys independently on push to `main` (configured in Cloudflare dashboard).
Expand Down
121 changes: 121 additions & 0 deletions .github/workflows/check_and_merge_vendored_skills_pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Check & Merge Vendored Skills PR

# Auto-merges the PR opened by `check_vendored_skills.yml` once CI is
# green. When the merge lands, the standard `release_and_publish.yml`
# workflow fires (it triggers on `pull_request: closed && merged`) and
# cuts the website's next patch release — so the whole chain from
# "upstream skill v2.16.4 was published" to "deepworkplan.com released
# v1.0.75 with 2.16.4 vendored" runs unattended.
#
# Mirrors `check_and_merge_packages_upgrades_pr.yml`. Only auto-merges
# when GitHub reports `mergeable_state == "clean"` (all required checks
# passed, no conflicts). If not clean, the PR stays open for a human to
# review — this workflow never force-merges.

on:
workflow_dispatch:
schedule:
# Daily at 17:00 UTC — 5 hours after the check runs at 12:00 UTC.
# Matches the "detect at 15:00 → merge at 20:00" gap used for npm
# dependency upgrades, giving CI enough time to run and, ideally,
# for a maintainer to intervene if a specific upgrade should NOT
# auto-merge (close the PR before 17:00 UTC and this run is a no-op).
- cron: '0 17 * * *'

env:
GIT_BRANCH_FOR_SKILLS_REFRESH: feature__vendored_skills_refresh

permissions:
contents: write
pull-requests: write

concurrency:
group: check-and-merge-vendored-skills
cancel-in-progress: false

jobs:
check_and_merge_vendored_skills_pr:
name: 'Check & Merge Vendored Skills PR'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
- uses: actions/setup-node@v4
with:
node-version: '24.18.0'

- name: Step 1 - ⚙️ Setup GitHub Config
run: |
git config user.name "🤖 Dailybot"
git config user.email "ops@dailybot.com"
gh auth login --with-token <<< "${{ secrets.AUTOMATION_GITHUB_TOKEN }}"

- name: Step 2 - 🔍 Locate the open vendored-skills refresh PR
id: locate_pr
run: |
set -euo pipefail
PR_NUMBER=$(gh pr list -B main -s open -L 1 \
--json number,headRefName \
-q '.[] | select(.headRefName == "'"$GIT_BRANCH_FOR_SKILLS_REFRESH"'") | .number' \
|| true)
if [ -z "$PR_NUMBER" ]; then
echo "No open PR from ${GIT_BRANCH_FOR_SKILLS_REFRESH} to main — nothing to merge."
echo "**No open PR** — this is expected on days when no upstream skill released." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"

- if: ${{ steps.locate_pr.outputs.pr_number }}
name: Step 3 - 🧾 Read PR title/body for the run summary
id: read_pr
run: |
set -euo pipefail
PR_NUMBER="${{ steps.locate_pr.outputs.pr_number }}"
PR_TITLE=$(gh pr view "$PR_NUMBER" --json title -q '.title')
echo "pr_title=$PR_TITLE" >> "$GITHUB_OUTPUT"
{
echo "## Vendored-skills refresh PR"
echo ""
echo "**#${PR_NUMBER}** — ${PR_TITLE}"
} >> "$GITHUB_STEP_SUMMARY"

- if: ${{ steps.locate_pr.outputs.pr_number }}
name: Step 4 - 🚦 Check mergeable state and merge if clean
id: merge_if_clean
run: |
set -euo pipefail
PR_NUMBER="${{ steps.locate_pr.outputs.pr_number }}"

# Prefer the REST API's `mergeable_state`. `"clean"` means all
# required status checks pass AND there are no conflicts AND the
# branch is up to date with base. Any other value (`"blocked"`,
# `"unstable"`, `"dirty"`, `"unknown"`, ...) means we should NOT
# auto-merge — leave the PR alone for a human to review.
PR_DATA=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}")
PR_MERGEABLE_STATE=$(echo "$PR_DATA" | jq -r '.mergeable_state')

echo "pr_mergeable_state=$PR_MERGEABLE_STATE" >> "$GITHUB_OUTPUT"
echo "PR #${PR_NUMBER} mergeable_state = ${PR_MERGEABLE_STATE}"

if [ "$PR_MERGEABLE_STATE" != "clean" ]; then
{
echo ""
echo "**Mergeable state:** \`${PR_MERGEABLE_STATE}\` — not merging automatically. Leaving PR open for review."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi

# `--merge` (not `--squash`) so `release_and_publish.yml`, which
# counts commits since the last release marker, picks up the
# refresh commit as a normal entry in the release notes.
gh pr merge "$PR_NUMBER" --merge
{
echo ""
echo "**Merged automatically** — \`release_and_publish.yml\` will now bump the website version and publish a release with the refreshed skills."
} >> "$GITHUB_STEP_SUMMARY"

outputs:
pr_number: ${{ steps.locate_pr.outputs.pr_number }}
pr_title: ${{ steps.read_pr.outputs.pr_title }}
pr_mergeable_state: ${{ steps.merge_if_clean.outputs.pr_mergeable_state }}
Loading
Loading