diff --git a/.agents/skills/github-actions-docs/SKILL.md b/.agents/skills/github-actions-docs/SKILL.md new file mode 100644 index 00000000..00318307 --- /dev/null +++ b/.agents/skills/github-actions-docs/SKILL.md @@ -0,0 +1,98 @@ +--- +name: github-actions-docs +description: Use when users ask how to write, explain, customize, migrate, secure, or troubleshoot GitHub Actions workflows, workflow syntax, triggers, matrices, runners, reusable workflows, artifacts, caching, secrets, OIDC, deployments, custom actions, or Actions Runner Controller, especially when they need official GitHub documentation, exact links, or docs-grounded YAML guidance. +--- + +GitHub Actions questions are easy to answer from stale memory. Use this skill to ground answers in official GitHub documentation and return the closest authoritative page instead of generic CI/CD advice. + +## When to Use + +Use this skill when the request is about: + +- GitHub Actions concepts, terminology, or product boundaries +- Workflow YAML, triggers, jobs, matrices, concurrency, variables, contexts, or expressions +- GitHub-hosted runners, larger runners, self-hosted runners, or Actions Runner Controller +- Artifacts, caches, reusable workflows, workflow templates, or custom actions +- Secrets, `GITHUB_TOKEN`, OpenID Connect, artifact attestations, or secure workflow patterns +- Environments, deployment protection rules, deployment history, or deployment examples +- Migrating from Jenkins, CircleCI, GitLab CI/CD, Travis CI, Azure Pipelines, or other CI systems +- Troubleshooting workflow behavior when the user needs documentation, syntax guidance, or official references + +Do not use this skill for: + +- A specific failing PR check, missing workflow log, or CI failure triage. Use `gh-fix-ci`. +- General GitHub pull request, branch, or repository operations. Use `github`. +- CodeQL-specific configuration or code scanning guidance. Use `codeql`. +- Dependabot configuration, grouping, or dependency update strategy. Use `dependabot`. + +## Workflow + +### 1. Classify the request + +Decide which bucket the question belongs to before searching: + +- Getting started or tutorials +- Workflow authoring and syntax +- Runners and execution environment +- Security and supply chain +- Deployments and environments +- Custom actions and publishing +- Monitoring, logs, and troubleshooting +- Migration + +If you need a quick starting point, load `references/topic-map.md` and jump to the closest section. + +### 2. Search official GitHub docs first + +- Treat `docs.github.com` as the source of truth. +- Prefer pages under . +- Search with the user's exact terms plus a focused Actions phrase such as `workflow syntax`, `OIDC`, `reusable workflows`, or `self-hosted runners`. +- When multiple pages are plausible, compare 2-3 candidate pages and pick the one that most directly answers the user's question. + +### 3. Open the best page before answering + +- Read the most relevant page, and the exact section when practical. +- Use the topic map only to narrow the search space or surface likely starting pages. +- If a page appears renamed, moved, or incomplete, say that explicitly and return the nearest authoritative pages instead of guessing. + +### 4. Answer with docs-grounded guidance + +- Start with a direct answer in plain language. +- Include exact GitHub docs links, not just the docs homepage. +- Only provide YAML or step-by-step examples when the user asks for them or when the docs page makes an example necessary. +- Make any inference explicit. Good phrasing: + - `According to GitHub docs, ...` + - `Inference: this likely means ...` + +## Answer Shape + +Use a compact structure unless the user asks for depth: + +1. Direct answer +2. Relevant docs +3. Example YAML or steps, only if needed +4. Explicit inference callout, only if you had to connect multiple docs pages + +Keep citations close to the claim they support. + +## Search and Routing Tips + +- For concept questions, prefer overview or concept pages before deep reference pages. +- For syntax questions, prefer workflow syntax, events, contexts, variables, or expressions reference pages. +- For security questions, prefer `Secure use`, `Secrets`, `GITHUB_TOKEN`, `OpenID Connect`, and artifact attestation docs. +- For deployment questions, prefer environments and deployment protection docs before cloud-specific examples. +- For migration questions, prefer the migration hub page first, then a platform-specific migration guide. +- If the user asks for a beginner walkthrough, start with a tutorial or quickstart instead of a raw reference page. + +## Common Mistakes + +- Answering from memory without verifying the current docs +- Linking the GitHub Actions docs landing page when a narrower page exists +- Mixing up reusable workflows and composite actions +- Suggesting long-lived cloud credentials when OIDC is the better documented path +- Treating repo-specific CI debugging as a documentation question when it should be handed to `gh-fix-ci` +- Letting adjacent domains absorb the request when `codeql` or `dependabot` is the sharper fit + +## Bundled Reference + +Read `references/topic-map.md` only as a compact index of likely doc entry points. It is intentionally incomplete and should never replace the live GitHub docs as the final authority. diff --git a/.agents/skills/github-actions-docs/references/topic-map.md b/.agents/skills/github-actions-docs/references/topic-map.md new file mode 100644 index 00000000..9e5afc7d --- /dev/null +++ b/.agents/skills/github-actions-docs/references/topic-map.md @@ -0,0 +1,90 @@ +# GitHub Actions Topic Map + +This reference is a compact routing aid derived from the source catalog. It is intentionally selective and deduplicated. Use it to find the right documentation neighborhood quickly, then verify against the live docs on `docs.github.com`. + +## Getting Started + +- [Understanding GitHub Actions](https://docs.github.com/en/actions/get-started/understand-github-actions) +- [Quickstart for GitHub Actions](https://docs.github.com/en/actions/get-started/quickstart) +- [Continuous integration](https://docs.github.com/en/actions/get-started/continuous-integration) +- [Continuous deployment](https://docs.github.com/en/actions/get-started/continuous-deployment) +- [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax) +- [Events that trigger workflows](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) + +## Workflow Authoring + +- [Workflows](https://docs.github.com/en/actions/concepts/workflows-and-actions/workflows) +- [Variables](https://docs.github.com/en/actions/concepts/workflows-and-actions/variables) +- [Contexts](https://docs.github.com/en/actions/concepts/workflows-and-actions/contexts) +- [Expressions](https://docs.github.com/en/actions/concepts/workflows-and-actions/expressions) +- [Using jobs in a workflow](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-jobs) +- [Running variations of jobs in a workflow](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/run-job-variations) +- [Passing information between jobs](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/pass-job-outputs) +- [Reuse workflows](https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows) +- [Reusing workflow configurations](https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations) + +## Runners and Execution + +- [GitHub-hosted runners](https://docs.github.com/en/actions/concepts/runners/github-hosted-runners) +- [Using GitHub-hosted runners](https://docs.github.com/en/actions/how-tos/manage-runners/github-hosted-runners/use-github-hosted-runners) +- [Choosing the runner for a job](https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job) +- [Running jobs in a container](https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/run-jobs-in-a-container) +- [Self-hosted runners](https://docs.github.com/en/actions/concepts/runners/self-hosted-runners) +- [Larger runners](https://docs.github.com/en/actions/concepts/runners/larger-runners) +- [Actions Runner Controller](https://docs.github.com/en/actions/concepts/runners/actions-runner-controller) +- [Get started with Actions Runner Controller](https://docs.github.com/en/actions/tutorials/use-actions-runner-controller/get-started) + +## Security and Supply Chain + +- [Secure use reference](https://docs.github.com/en/actions/reference/security/secure-use) +- [Secrets](https://docs.github.com/en/actions/concepts/security/secrets) +- [GITHUB_TOKEN](https://docs.github.com/en/actions/concepts/security/github_token) +- [OpenID Connect](https://docs.github.com/en/actions/concepts/security/openid-connect) +- [OpenID Connect reference](https://docs.github.com/en/actions/reference/security/oidc) +- [Artifact attestations](https://docs.github.com/en/actions/concepts/security/artifact-attestations) +- [Using artifact attestations to establish provenance for builds](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations) +- [Using OpenID Connect with reusable workflows](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-with-reusable-workflows) +- [Configuring OpenID Connect in Amazon Web Services](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-aws) + +## Deployments and Environments + +- [Deployment environments](https://docs.github.com/en/actions/concepts/workflows-and-actions/deployment-environments) +- [Deployments and environments](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments) +- [Deploying with GitHub Actions](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/control-deployments) +- [Managing environments for deployment](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments) +- [Reviewing deployments](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/review-deployments) +- [Viewing deployment history](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/view-deployment-history) +- [Deploying to Amazon Elastic Container Service](https://docs.github.com/en/actions/how-tos/deploy/deploy-to-third-party-platforms/amazon-elastic-container-service) +- [Deploying Node.js to Azure App Service](https://docs.github.com/en/actions/how-tos/deploy/deploy-to-third-party-platforms/nodejs-to-azure-app-service) + +## Custom Actions and Publishing + +- [About custom actions](https://docs.github.com/en/actions/concepts/workflows-and-actions/custom-actions) +- [Metadata syntax reference](https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax) +- [Managing custom actions](https://docs.github.com/en/actions/how-tos/create-and-publish-actions/manage-custom-actions) +- [Creating a JavaScript action](https://docs.github.com/en/actions/tutorials/create-actions/create-a-javascript-action) +- [Creating a composite action](https://docs.github.com/en/actions/tutorials/create-actions/create-a-composite-action) +- [Creating a third party CLI action](https://docs.github.com/en/actions/how-tos/create-and-publish-actions/create-a-cli-action) +- [Publishing actions in GitHub Marketplace](https://docs.github.com/en/actions/how-tos/create-and-publish-actions/publish-in-github-marketplace) +- [Releasing and maintaining actions](https://docs.github.com/en/actions/how-tos/create-and-publish-actions/release-and-maintain-actions) + +## Monitoring, Logs, and Troubleshooting + +- [Using the visualization graph](https://docs.github.com/en/actions/how-tos/monitor-workflows/use-the-visualization-graph) +- [Viewing workflow run history](https://docs.github.com/en/actions/how-tos/monitor-workflows/view-workflow-run-history) +- [Using workflow run logs](https://docs.github.com/en/actions/how-tos/monitor-workflows/use-workflow-run-logs) +- [Viewing job condition expression logs](https://docs.github.com/en/actions/how-tos/monitor-workflows/view-job-condition-logs) +- [Enabling debug logging](https://docs.github.com/en/actions/how-tos/monitor-workflows/enable-debug-logging) +- [Troubleshooting workflows](https://docs.github.com/en/actions/how-tos/troubleshoot-workflows) +- [Viewing GitHub Actions metrics](https://docs.github.com/en/actions/how-tos/administer/view-metrics) + +## Migration and Tutorials + +- [Migrating to GitHub Actions](https://docs.github.com/en/actions/tutorials/migrate-to-github-actions) +- [Automating migration with GitHub Actions Importer](https://docs.github.com/en/actions/tutorials/migrate-to-github-actions/automated-migrations/use-github-actions-importer) +- [Migrating from Jenkins to GitHub Actions](https://docs.github.com/en/actions/tutorials/migrate-to-github-actions/manual-migrations/migrate-from-jenkins) +- [Migrating from CircleCI to GitHub Actions](https://docs.github.com/en/actions/tutorials/migrate-to-github-actions/manual-migrations/migrate-from-circleci) +- [Migrating from GitLab CI/CD to GitHub Actions](https://docs.github.com/en/actions/tutorials/migrate-to-github-actions/manual-migrations/migrate-from-gitlab-cicd) +- [Building and testing Node.js](https://docs.github.com/en/actions/tutorials/build-and-test-code/nodejs) +- [Use GITHUB_TOKEN for authentication in workflows](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token) +- [Store and share data with workflow artifacts](https://docs.github.com/en/actions/tutorials/store-and-share-data) diff --git a/.github/HOOKS.md b/.github/HOOKS.md new file mode 100644 index 00000000..2c20fa20 --- /dev/null +++ b/.github/HOOKS.md @@ -0,0 +1,141 @@ +# Agent Hooks & Pre-commit Setup + +**For AI Agents**: This document is essential reading. Consult it whenever you: + +- Modify hook configuration files (`.json` files in `.github/hooks/`) +- Change hook script paths or entry points +- Update the shared formatter/linter script behavior +- Need to understand when and how hooks run + +This project uses four complementary hooks to enforce code quality and provide documentation: + +1. **Copilot SessionStart Hook** (`session-start-docs.json`) +2. **Copilot PostToolUse Hook** (`format-and-lint-after-edit.json`) +3. **Copilot Stop Hook** (`type-check-at-stop.json`) +4. **Pre-commit Local Hook** (`.pre-commit-config.yaml`) + +## Overview + +| Hook | Event | Purpose | Files | +| ------------------------------ | ------------ | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| **session-start-docs** | SessionStart | Injects list of available markdown docs for agent reference at session start | [.github/hooks/session-start-docs.json](.github/hooks/session-start-docs.json) | +| **format-and-lint-after-edit** | PostToolUse | Runs oxfmt then oxlint after agent edits individual files in VS Code Copilot | [.github/hooks/format-and-lint-after-edit.json](.github/hooks/format-and-lint-after-edit.json) | +| **type-check-at-stop** | Stop | Runs tsc at agent session end; blocks if type errors found | [.github/hooks/type-check-at-stop.json](.github/hooks/type-check-at-stop.json) | +| **oxc-format-and-lint** | pre-commit | Runs oxfmt then oxlint on staged files before commit (all developers) | [.pre-commit-config.yaml](.pre-commit-config.yaml) | + +## Shared Formatter/Linter Script + +All three hooks delegate to: + +- **[.github/hooks/scripts/pre-commit-oxc.sh](.github/hooks/scripts/pre-commit-oxc.sh)** — Shared entry point that formats and lints files + +### Script Behavior + +1. **Accepts file paths** as positional arguments (from pre-commit or PostToolUse). +2. **Formats with oxfmt**: All matched files (JS/TS/JSON/Markdown/GraphQL/YAML). +3. **Lints with oxlint**: Only JS/TS-family files (`.js`, `.jsx`, `.cjs`, `.mjs`, `.ts`, `.tsx`, `.cts`, `.mts`). +4. **Fails fast**: If oxfmt fails, oxlint is skipped and the hook exits with error. + +## Setup & Installation + +### For Copilot Hooks (VS Code) + +Copilot automatically discovers hooks in `.github/hooks/*.json` when working in this workspace. No manual setup required. + +### For Pre-commit (All Developers) + +```bash +# Install pre-commit if not already done +pip install pre-commit + +# Install the git hooks +pre-commit install + +# (Optional) Test the hooks on staged files +pre-commit run --all-files +``` + +After installation, pre-commit will run automatically on `git commit` for files matching the configured patterns. + +## When to Update This Setup + +### Changing Formatter/Linter Behavior + +If you modify any of these, keep them in sync: + +1. [.pre-commit-config.yaml](.pre-commit-config.yaml) — entry point and file patterns +2. [.github/hooks/format-and-lint-after-edit.json](.github/hooks/format-and-lint-after-edit.json) — PostToolUse command +3. [.github/hooks/scripts/pre-commit-oxc.sh](.github/hooks/scripts/pre-commit-oxc.sh) — the shared script itself + +Also update the usage header comment in [pre-commit-oxc.sh](.github/hooks/scripts/pre-commit-oxc.sh) to document where it's consumed. + +**Project rule**: See [AGENTS.md](../AGENTS.md) Hook Sync Rule for details. + +### Adding New File Types + +Update the file pattern in [.pre-commit-config.yaml](.pre-commit-config.yaml): + +```yaml +files: \.(js|jsx|cjs|mjs|ts|tsx|cts|mts|json|md|markdown|graphql|gql|yml|yaml)$ +``` + +The script automatically handles linting only for JS/TS files, so adding format-only types is safe. + +## Documentation at Session Start + +The SessionStart hook ([session-start-docs.json](.github/hooks/session-start-docs.json)) injects available markdown documentation: + +- Runs when a new Copilot agent session starts. +- Lists key markdown files and their purposes in the chat. +- Helps agents understand what documentation is available without searching. +- Includes references to AGENTS.md, HOOKS.md, README.md, CHANGELOG.md, and action-specific docs. + +This ensures agents are aware of guidance documents and conventions from the very beginning of each session. + +## Type Checking at Session End + +The Stop hook ([type-check-at-stop.json](.github/hooks/type-check-at-stop.json)) enforces TypeScript checking: + +- Runs `pnpm run tsc:check` when the Copilot agent finishes. +- **Blocks the session** if type errors are found, forcing the agent to resolve them. +- Allows normal session end if tsc passes. + +This ensures the agent doesn't leave the codebase with type errors. + +## Quick Reference for AI Agents + +| Scenario | What to Do | +| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Editing hook script behavior** | Update the shared script + update the usage header comment in `pre-commit-oxc.sh` | +| **Changing hook file paths** | Update [.pre-commit-config.yaml](.pre-commit-config.yaml), [format-and-lint-after-edit.json](.github/hooks/format-and-lint-after-edit.json), and [type-check-at-stop.json](.github/hooks/type-check-at-stop.json) **in sync** | +| **Adding new file type support** | Update the `files:` pattern in [.pre-commit-config.yaml](.pre-commit-config.yaml); script handles linting only for JS/TS | +| **Agent session ends** | TypeScript check runs and **blocks** if errors are found; fix them before finishing | +| **Uncertain about sync** | Read the Hook Sync Rule in [AGENTS.md](../AGENTS.md) Code Quality section | + +## Troubleshooting + +### Hook not executing in pre-commit + +1. Verify `pre-commit install` was run. +2. Check that the files match the pattern in [.pre-commit-config.yaml](.pre-commit-config.yaml). +3. Run `pre-commit run --all-files` to manually test. + +### Hook not executing in Copilot + +1. Verify the `.json` hook config is in `.github/hooks/`. +2. Open the Output panel in VS Code and select "GitHub Copilot Chat Hooks" to see hook output. +3. Check that the hook script path is correct (e.g., [.github/hooks/scripts/pre-commit-oxc.sh](.github/hooks/scripts/pre-commit-oxc.sh)). + +### oxfmt or oxlint not found + +If hooks fail with "command not found": + +- **In pre-commit**: Run `pnpm install` to ensure dev dependencies are installed. +- **In Copilot**: Same — the workspace must have `pnpm install` completed. + +## References + +- [VS Code Agent Hooks Documentation](https://code.visualstudio.com/docs/copilot/customization/hooks) +- [Pre-commit Framework](https://pre-commit.com/) +- [oxfmt Documentation](https://oxc-project.github.io/docs/guide/oxfmt.html) +- [oxlint Documentation](https://oxc-project.github.io/docs/guide/oxlint.html) diff --git a/.github/agents/cf-pages-feature.agent.md b/.github/agents/cf-pages-feature.agent.md new file mode 100644 index 00000000..6ebc617b --- /dev/null +++ b/.github/agents/cf-pages-feature.agent.md @@ -0,0 +1,110 @@ +--- +description: 'Use when adding a new feature, input, GitHub API operation, or Cloudflare API change to this dual-mode GitHub Actions project (deploy + delete actions for Cloudflare Pages). Triggers on: add input, new action input, new GraphQL mutation/query/fragment, new Cloudflare API endpoint, add feature to deploy action, add feature to delete action.' +name: 'CF Pages Feature Agent' +tools: [read, edit, search, execute, todo] +argument-hint: "Describe the feature, input, or operation you want to add (e.g. 'add a new optional input to skip SSL verification' or 'add a GraphQL mutation to update a deployment status')" +--- + +You are a specialist for adding new features to this dual-mode GitHub Actions project that deploys to Cloudflare Pages. You have deep knowledge of the project's conventions and multi-file synchronization requirements. + +## Project Architecture + +- **Deploy action**: `src/deploy/` → bundled to `dist/deploy/index.js` → consumed by `action.yml` +- **Delete action**: `src/delete/` → bundled to `dist/delete/index.js` → consumed by `delete/action.yml` +- **Shared logic**: `src/common/` (GitHub API, Cloudflare API, batch ops, utilities) +- **Path aliases**: All imports use `@/` prefix (e.g. `@/common/utils.js`). Keep `tsconfig.json` paths in sync with `vitest.config.ts` `resolve.alias`. + +## Strict Conventions + +### Logging + +- **NEVER use `console.log`** — use `@actions/core` methods only: `core.info()`, `core.debug()`, `core.warning()`, `core.error()`, `core.setFailed()` + +### Error Handling + +- Use `raise()` from `src/common/utils.ts` for inline errors with type narrowing: + ```typescript + const {name} = (await checkEnvironment()) ?? raise('Environment required') + ``` + +### GraphQL + +- Prefix mutations: `MutationCreateGitHubDeployment` +- Prefix fragments: `EnvironmentFragment` +- Always use the tagged template: `graphql(/* GraphQL */ \`...\`)` +- Place shared fragments in `src/common/github/fragments.ts` +- **Run `pnpm run codegen` after any GraphQL change** to regenerate `__generated__/gql/` + +### Testing + +- Mock `@actions/core` via `vi.mock(import('@actions/core'))` — auto-loads `__mocks__/@actions/core.ts` +- Mock HTTP with undici `MockAgent` pattern (see `__tests__/helpers/api.ts`) +- Use `stubRequiredInputEnv()` from `__tests__/helpers/inputs.ts` for action input env vars +- Use `stubTestEnvVars()` from `__tests__/helpers/env.ts` for GitHub context env vars +- Always assert `mockApi.mockAgent.assertNoPendingInterceptors()` to catch unmatched HTTP calls +- Use `describe(functionName)` with actual function reference for IDE navigation + +## Feature Addition Workflow + +Work through these steps in order, using the todo list to track progress. + +### Step 1 — Understand the Request + +Read the relevant existing files to understand current patterns before making changes. Search for similar existing inputs/operations as reference. + +### Step 2 — Update Action Manifest + +Depending on whether this affects the deploy action, delete action, or both: + +- **Deploy**: Edit `action.yml` — add input under `inputs:` with `description`, `required`, and optional `default` +- **Delete**: Edit `delete/action.yml` — same structure +- If the input key is new, add `INPUT_KEY_*` constant to `input-keys.ts` and add to `INPUT_KEYS_REQUIRED` if mandatory + +### Step 3 — Implement in Source + +- Add input reading logic to `src/deploy/inputs.ts` or `src/delete/inputs.ts` (or both) +- For shared logic, add to `src/common/inputs.ts` +- Wire up the new input/operation in the relevant `main.ts` + +### Step 4 — Add GraphQL (if applicable) + +- Write the query/mutation/fragment in the appropriate file +- Use the `graphql(/* GraphQL */ \`...\`)` tag +- Run `pnpm run codegen` to generate types + +### Step 5 — Add Cloudflare API Types (if applicable) + +- Update types in `src/common/cloudflare/types.ts` +- Add response fixtures to `__generated__/responses/api.cloudflare.com/` if needed + +### Step 6 — Update Test Stubs + +- Add the new input to `__tests__/helpers/inputs.ts` `stubRequiredInputEnv()` if it's required +- Add fixtures to `__generated__/responses/` if new API responses are needed + +### Step 7 — Write Tests + +- Create or update the relevant `.test.ts` file under `__tests__/` mirroring the `src/` structure +- Cover happy path, error path, and edge cases +- Assert no pending interceptors after HTTP calls + +### Step 8 — Validate + +Run the full validation suite: + +```bash +pnpm run all +``` + +This runs: knip → codegen → tsc → format → lint → test → build + +Fix any errors before considering the task complete. + +## Output + +After completing all steps, summarize: + +1. Which files were changed and why +2. Any codegen commands that were run +3. Test results +4. Any follow-up actions the user should take (e.g. updating GitHub Environment permissions) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 572aad3a..00000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,157 +0,0 @@ -# GitHub Actions Cloudflare Pages - AI Agent Instructions - -## Project Overview - -Dual-mode GitHub Action for Cloudflare Pages deployments: `deploy` creates deployments via Wrangler CLI and links them to GitHub Deployments/Environments, `delete` batch-removes old deployments. Built with TypeScript ESM, GraphQL-typed GitHub API integration, and comprehensive vitest testing. - -## Architecture - -### Dual Entry Points - -- **Deploy**: [src/deploy/index.ts](../src/deploy/index.ts) → bundled to [dist/deploy/index.js](../dist/deploy/index.js) → consumed by [action.yml](../action.yml) -- **Delete**: [src/delete/index.ts](../src/delete/index.ts) → bundled to [dist/delete/index.js](../dist/delete/index.js) → consumed by [delete/action.yml](../delete/action.yml) -- **Shared Logic**: [src/common/](../src/common/) contains reusable code for both actions (GitHub API, Cloudflare deployment logic, batch operations) - -### Path Aliases (`@/` prefix) - -All imports use [tsconfig.json](../tsconfig.json) path mappings (`@/common/*`, `@/deploy/*`, `@/gql/*`, etc.). **Critical**: Keep [tsconfig.json](../tsconfig.json) paths in sync with [vitest.config.ts](../vitest.config.ts) `resolve.alias` - vi.mock() fails otherwise. - -### GraphQL Type Safety - -- GitHub GraphQL API queries/mutations in `src/**/` are type-safe via [@graphql-codegen/client-preset](../graphql.config.ts) -- Run `pnpm run codegen` to regenerate [**generated**/gql/](../__generated__/gql/) types from inline `graphql()` calls -- Uses fragments pattern (see [src/common/github/fragments.ts](../src/common/github/fragments.ts)) -- Custom client: [src/common/github/api/client.ts](../src/common/github/api/client.ts) wraps fetch with TypedDocumentString for compile-time validation -- Preview features added via [schema/github-preview/schema.graphql](../schema/github-preview/schema.graphql) - -### Cloudflare Integration - -- Executes `wrangler pages deploy` via `execAsync()` (see [src/common/cloudflare/deployment/create.ts](../src/common/cloudflare/deployment/create.ts#L47-L56)) -- Wrangler is external dependency (not bundled) - defined in [esbuild.config.js](../esbuild.config.js#L20) -- Uses REST API for deployment status polling and deletion - -### Code Generation - -Two codegen workflows: - -1. **GraphQL**: `pnpm run codegen` → generates [**generated**/gql/](../__generated__/gql/) from inline queries -2. **GitHub Event Types**: `pnpm run codegen:events` → runs [bin/codegen/index.ts](../bin/codegen/index.ts) to generate workflow event types from `@octokit/webhooks-schemas` - -## Development Workflows - -### Essential Commands - -```bash -pnpm run all # Full validation: knip → codegen → tsc → format → lint → test → build -pnpm run build # ESBuild bundle to dist/deploy & dist/delete -pnpm run test # Vitest run -pnpm run test:watch # Interactive test mode -pnpm run codegen:watch # Auto-regenerate types on GraphQL changes -pnpm run act:d # Test delete action locally with act -``` - -### Build Requirements - -- **Node 24**: Strict engine requirement in [package.json](../package.json#L102) -- **pnpm 10.15.1**: Enforced by `packageManager` field -- After modifying GraphQL queries/mutations: `pnpm run codegen` before building -- After changing input keys in [action.yml](../action.yml): Update [input-keys.ts](../input-keys.ts) - -### Debugging - -- ESBuild sourcemaps enabled ([esbuild.config.js](../esbuild.config.js#L11)) -- Use `pnpm run start` to test built action locally (requires local env vars) -- Vitest debug mode: Add `debugger` statements and run with Node inspector - -## Testing Patterns - -### Setup & Mocking - -- **Global Setup**: [vitest.setup.ts](../vitest.setup.ts) stubs all required env vars and input env vars before each test -- **Core Mocking**: [**mocks**/@actions/core.ts](../__mocks__/@actions/core.ts) provides vi.fn() wrappers for all @actions/core methods -- **HTTP Mocking**: Use undici MockAgent pattern (see [**tests**/helpers/api.ts](../__tests__/helpers/api.ts)) - -### Test Helpers - -- `stubTestEnvVars()`: Injects GitHub context env vars (see [**tests**/helpers/env.ts](../__tests__/helpers/env.ts)) -- `stubRequiredInputEnv()`: Sets all required action inputs to mock values (see [**tests**/helpers/inputs.ts](../__tests__/helpers/inputs.ts)) -- Real webhook payloads: [**generated**/payloads/](../__generated__/payloads/) imported as json modules -- API responses: [**generated**/responses/](../__generated__/responses/) for consistent test fixtures - -### Module Mocking - -```typescript -vi.mock(import('@actions/core')) // Auto-loads __mocks__/@actions/core.ts -vi.mock(import('@/common/utils.js')) // Mocks specific module -``` - -Always import using path aliases to match vitest.config.ts aliases. - -### Writing Tests - -- Use `describe(functionName)` with actual function reference for IDE navigation -- Assert `mockApi.mockAgent.assertNoPendingInterceptors()` to catch missed HTTP calls - -## Project-Specific Conventions - -### Input Key Management - -All action inputs centralized in [input-keys.ts](../input-keys.ts). When adding inputs: - -1. Add constant `INPUT_KEY_*` -2. Update `INPUT_KEYS_REQUIRED` if mandatory -3. Update corresponding [action.yml](../action.yml) or [delete/action.yml](../delete/action.yml) -4. Add to test stub in [**tests**/helpers/inputs.ts](../__tests__/helpers/inputs.ts) - -### Error Handling - -Use `raise()` utility for inline errors with type narrowing: - -```typescript -const {name} = (await checkEnvironment()) ?? raise('Environment required') -``` - -See implementation in [src/common/utils.ts](../src/common/utils.ts). - -### GraphQL Operations - -- Prefix mutations: `MutationCreateGitHubDeployment` -- Prefix fragments: `EnvironmentFragment` -- Place in same file as usage or in [src/common/github/fragments.ts](../src/common/github/fragments.ts) if shared -- Always use `graphql(/* GraphQL */ `...`)` template tag for codegen detection - -### Code Quality - -- **Knip**: Dead code detection ([knip.json](../knip.json)) - ignores [**generated**/](../__generated__/), fragments, wrangler, act -- **Oxlint**: TypeScript linting with custom rules ([.oxlintrc.json](../.oxlintrc.json)) -- **TypeScript**: Strict mode with `verbatimModuleSyntax`, `noEmit`, `checkJs` -- **No console.log**: Use `@actions/core` methods (`info`, `debug`, `warning`, `error`, `setFailed`) - -### File Organization - -- [src/common/](../src/common/): Shared between deploy/delete actions -- [src/common/github/](../src/common/github/): GitHub API interactions (deployments, comments, environments) -- [src/common/cloudflare/](../src/common/cloudflare/): Cloudflare Pages API and deployment logic -- [**generated**/](../__generated__/): Never edit manually - regenerated by codegen scripts -- [**fixtures**/](../__fixtures__/): Test data that's manually maintained -- [**tests**/](../__tests__/): Mirrors src/ structure with `.test.ts` suffix - -### ESBuild Specifics - -- Banner adds `createRequire` shim for dynamic require compatibility ([esbuild.config.js](../esbuild.config.js#L24-L35)) -- External: `wrangler` (peer dependency expected in user's environment) -- Minification: Syntax and whitespace only (not identifiers) for debugging - -## GitHub Actions Integration - -- Requires manual creation of GitHub Environments (action cannot create due to permission requirements) -- Uses `GITHUB_TOKEN` with permissions: `actions:read`, `contents:read`, `deployments:write`, `pull-requests:write` -- Supports `push`, `pull_request`, `workflow_dispatch` events only (validated in [src/deploy/main.ts](../src/deploy/main.ts#L19-L27)) -- Deployment payload includes Cloudflare metadata for deletion workflow ([src/common/github/deployment/types.ts](../src/common/github/deployment/types.ts)) - -## When Modifying Core Functionality - -1. **Adding GitHub API Operations**: Create GraphQL operation in appropriate file → run `pnpm run codegen` → use typed `request()` client -2. **New Action Inputs**: Update action.yml → add key to input-keys.ts → handle in inputs.ts → stub in test helpers -3. **Cloudflare API Changes**: Update types in [src/common/cloudflare/types.ts](../src/common/cloudflare/types.ts) → add fixtures to [**generated**/responses/](../__generated__/responses/) -4. **Breaking Changes**: Document in [CHANGELOG.md](../CHANGELOG.md) using changesets: `pnpm changeset` diff --git a/.github/hooks/format-and-lint-after-edit.json b/.github/hooks/format-and-lint-after-edit.json new file mode 100644 index 00000000..fdb6e68d --- /dev/null +++ b/.github/hooks/format-and-lint-after-edit.json @@ -0,0 +1,10 @@ +{ + "hooks": { + "PostToolUse": [ + { + "type": "command", + "command": "if [[ -n \"$TOOL_INPUT_FILE_PATH\" && -f \"$TOOL_INPUT_FILE_PATH\" ]]; then bash ./.github/hooks/scripts/pre-commit-oxc.sh \"$TOOL_INPUT_FILE_PATH\"; fi" + } + ] + } +} diff --git a/.github/hooks/scripts/pre-commit-oxc.sh b/.github/hooks/scripts/pre-commit-oxc.sh new file mode 100644 index 00000000..0ed4a351 --- /dev/null +++ b/.github/hooks/scripts/pre-commit-oxc.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Shared formatter/linter entrypoint used by: +# - .pre-commit-config.yaml (local hook: oxc-format-and-lint) +# - .github/hooks/format-and-lint-after-edit.json (Copilot PostToolUse) +# AI agents: keep both wiring points and this usage list in sync when behavior or paths change. + +# pre-commit passes staged file paths as positional arguments. +# Exit fast when nothing matched this hook's file filter. +if [[ "$#" -eq 0 ]]; then + exit 0 +fi + +# Step 1: Format every matched file first. +# If formatting fails, the script exits immediately because of `set -e`. +pnpm exec oxfmt --write "$@" + +# Step 2: Build a list of files oxlint can analyze. +# We intentionally keep this separate from formatting so unsupported +# extensions can still be formatted by oxfmt when applicable. +lintable_files=() +for file in "$@"; do + case "$file" in + *.js|*.jsx|*.cjs|*.mjs|*.ts|*.tsx|*.cts|*.mts) + # Only include files that still exist at commit time. + if [[ -f "$file" ]]; then + lintable_files+=("$file") + fi + ;; + esac +done + +# Step 3: Lint only when at least one supported file remains. +# This ensures lint runs only after successful formatting. +if [[ "${#lintable_files[@]}" -gt 0 ]]; then + pnpm exec oxlint "${lintable_files[@]}" +fi diff --git a/.github/hooks/scripts/session-start-docs.sh b/.github/hooks/scripts/session-start-docs.sh new file mode 100644 index 00000000..56763066 --- /dev/null +++ b/.github/hooks/scripts/session-start-docs.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +# SessionStart hook: Inject documentation context about available guidance. +# Confirms which local markdown files the agent should reference. + +cat <<'EOF' +{ + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": "Documentation available in this workspace:\n- AGENTS.md: AI agent instructions, conventions, and workflows\n- .github/HOOKS.md: Hook ecosystem, format/lint/type-check automation, sync rules\n- README.md: Project overview and user-facing documentation\n- CHANGELOG.md: Version history and breaking changes\n- delete/README.md: Specific documentation for delete action\n- .github/agents/cf-pages-feature.agent.md: Feature agent customization\n\nRefer to these when making changes. Hooks enforce formatting, linting, and type checking automatically." + } +} +EOF diff --git a/.github/hooks/scripts/stop-type-check.sh b/.github/hooks/scripts/stop-type-check.sh new file mode 100644 index 00000000..0cf911dd --- /dev/null +++ b/.github/hooks/scripts/stop-type-check.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Stop hook for TypeScript type checking. +# Runs `pnpm run tsc:check` and blocks the session if type errors are found, +# forcing the agent to resolve them before finishing. + +if ! command -v pnpm >/dev/null 2>&1; then + exit 0 +fi + +# Capture tsc output and exit code. +tsc_output=$(pnpm run tsc:check 2>&1) || tsc_exit=$? + +# If tsc succeeded, allow the session to stop. +if [[ "${tsc_exit:-0}" -eq 0 ]]; then + exit 0 +fi + +# tsc failed: block the session and report errors. +cat <