Compare commits

...
14 changed files with 869 additions and 869 deletions

No files matched your search

+1
View File
@@ -10,3 +10,4 @@ COM_ERMNVLDMR_STATIC_S3_REGION="encrypted:BAj3iuo7tWWNR2V4yQ+LHnFCB4JcZp7TTfUtvv
COM_ERMNVLDMR_STATIC_S3_ENDPOINT_URL="encrypted:BNkQrgsrxtz544vuJbgwWUh5cPjHG5wbmHNcxTUWrzbFJJsdncaAitviCvIrS7Q41w+FiWkssv3D05Tu8Inn2P62E6G3PU4gNxO2oLdmGEdrzUqB/xvxOvLvos42j54QOZf7lOqMPKH6SL2/rheCAo10gSCicCqkfh4xsCAbYpyL"
COM_ERMNVLDMR_STATIC_AWS_ACCESS_KEY_ID="encrypted:BCsp8p4bp3cB+9grxHo634isRUaFGU3MOc1lr8ClSFRYLjqjqukyrbJCTZWV2Xdo8iQ33EylmbzUltbwzTWMQcjJSvIskesVLyV93q60o/Mu9L9jg/apwHuBgHv4zVuHstJ0zkHWgapbNTIlWD3RCm0ujeX/H1gBH3U="
COM_ERMNVLDMR_STATIC_AWS_SECRET_ACCESS_KEY="encrypted:BM7aPliOyiPrqFPZMfqpn534Nu+Y0Nbh/YRoxv2KY6XAi2dNO0LSEf97DqSUQYdsnu9iibZmVNktF2K/D6mtHr9+gumF6KsmaLH65KE7c9ly7NdFQHL9I26wxRq1Sr3Zgrfs954J6oURSlBRKqJFdcTsBltuuupGuqJHyLS9+DE0jD+bl0Qg0lA="
SOPS_AGE_KEY="encrypted:BOJ7q79Tws4DMxpFuSymgv//IC4f+j713Bd7mNlUCEaUYVSfJMayMVPzks0FcsYo12XvlbH6gQ+tCf3t9yj/LcKKJzl3wtxlSVjvCRr0hasEBohcoxkLnFZ73DFQGzWcVC5Jf5IMK7qdYdNVzW0dVY/sn/NlTgl+A3YB6/08XYPhljl9993ky76zQqWDJQ66bKJ7YxWvst2TUNoT0lJEL0M4pg9ztRsuGqNZ"
+175
View File
@@ -0,0 +1,175 @@
---
name: github-fix
description: Diagnose CI failures and review feedback on GitHub Pull Requests or Issues, repair code, verify quality checks, and commit changes.
---
# GitHub Fix Skill
Use this skill when a user submits a `/fix` command on a GitHub Pull Request or Issue.
## Overview
The `/fix` command resolves build failures, test errors, and reviewer feedback. This skill enforces unified diagnosis, clean code repair, strict verification, and transparent progress reporting.
```text
/fix [optional instructions]
│
├── 1. Unified Diagnosis (Fetch CI logs, inspect diff, parse comments)
├── 2. Target Code Repair (Fix build errors and review feedback in one pass)
├── 3. Verification (Run typecheck, lint, and tests)
├── 4. Git Operations (Commit and push to the active branch)
└── 5. Thread Summary (Post itemized comment with mandatory disclosure)
```
---
## Operational Workflow
### Step 1: Unified Diagnosis
Collect all diagnostic context before making code edits.
#### Case A: Pull Request Context
1. Identify the current PR number and branch:
```bash
gh pr view --json number,headRefName,baseRefName
```
2. Fetch failed CI workflow logs:
```bash
gh run view --log-failed
```
3. Inspect current changes in the PR:
```bash
gh pr diff
```
4. Read all review comments and discussion threads:
```bash
gh pr view --comments
```
5. Check inline review comments on specific files:
```bash
gh api repos/{owner}/{repo}/pulls/{pull_number}/comments
```
#### Case B: Issue Context
1. Read the issue description and thread comments:
```bash
gh issue view <issue-number> --comments
```
2. Extract error stack traces, reproduction steps, and user instructions.
---
### Step 2: Target Code Repair
Apply all necessary corrections in a single pass.
1. Address all failures from CI logs (TypeScript errors, linter violations, test failures).
2. Apply requested changes from code review comments.
3. Incorporate any optional user instructions provided with `/fix`.
4. Obey all repository conventions defined in `GEMINI.md`:
- **Strict TypeScript**: Do not use `any`, `as Type` assertions, or non-null `!` assertions. Use `castRef` from `@ermnvldmr/stl` when casting DOM references.
- **Type Imports**: Use `import type` for type declarations.
- **Interfaces**: Define object contracts with `interface`, not `type` aliases.
- **TSDoc**: Add descriptive JSDoc comments to all exported symbols and interfaces.
- **Styling**: Use Tailwind CSS v4 and `cva`. Merge class names with `cn` from `@ermnvldmr/stl`.
- **UI Primitives**: Use React Aria hooks and Radix UI primitives.
- **Import Boundaries**: Use relative paths for files within the same workspace package.
---
### Step 3: Verification
Execute the complete verification sequence. All checks must pass with zero warnings or errors before proceeding.
```bash
# 1. Verify TypeScript types
pnpm typecheck
# 2. Verify ESLint rules
pnpm lint
# 3. Run unit and component tests
pnpm test
```
> [!IMPORTANT]
> If any check fails, resolve the failure immediately and rerun the verification sequence.
---
### Step 4: Git Operations
Stage, commit, and push the resolved changes to the active branch.
1. Stage modified and created files:
```bash
git add <modified-files>
```
2. Commit with standard message:
```bash
git commit -m "fix: apply review adjustments and CI fixes"
```
3. Push changes to the remote branch:
```bash
git push origin HEAD
```
---
### Step 5: Post Summary Comment
Post an itemized summary comment to the GitHub PR or Issue thread.
1. Format the comment with the template below.
2. Submit the comment via GitHub CLI:
- For a Pull Request:
```bash
gh pr comment <pr-number> --body "<comment-body>"
```
- For an Issue:
```bash
gh issue comment <issue-number> --body "<comment-body>"
```
---
## GitHub Comment Template
```markdown
### Automated Fix Summary
I addressed the reported issues and review feedback.
#### Root Causes Identified
- **CI / Test Failure**: `<Brief description of the failure>`
- **Review Feedback**: `<Brief description of reviewer comments>`
#### Changes Applied
- `packages/<pkg>/src/...`: Fixed `<specific problem>`
- `services/<svc>/src/...`: Updated `<specific problem>`
#### Verification Results
- `pnpm typecheck` passed (0 errors).
- `pnpm lint` passed (0 warnings).
- `pnpm test` passed (all tests clean).
Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.
```
---
## Verification Checklist
Before completing the task, verify every item in this checklist:
- [ ] Failed CI logs inspected with `gh run view --log-failed`.
- [ ] PR diff and review comments retrieved and analyzed.
- [ ] Code repairs satisfy all requirements without introducing regressions.
- [ ] No `any`, `as Type`, or `!` operators introduced.
- [ ] `pnpm typecheck` completed with 0 errors.
- [ ] `pnpm lint` completed with 0 warnings.
- [ ] `pnpm test` completed with all tests passing.
- [ ] Changes committed and pushed to the active branch.
- [ ] Summary comment posted to the PR or Issue.
- [ ] Mandatory disclosure line included at the end of the comment.
+125
View File
@@ -0,0 +1,125 @@
---
name: github-implement
description: Implement code changes from an approved issue plan, run monorepo validation, and create a pull request.
---
# GitHub Implement Skill
Use this skill when an issue receives the `/implement` command.
## Overview
This skill guides the implementation of approved design plans. It enforces repository rules, executes automated validation, and prepares pull requests for the `next` branch.
## Procedural Workflow
### Phase 1: Context Ingestion
1. Read the complete issue description and title.
2. Locate the approved implementation plan in the issue comments.
3. Parse additional instructions from the `/implement` comment.
4. If requirements conflict or miss critical details, stop and request clarification.
### Phase 2: Code Implementation
Apply all modifications according to `GEMINI.md` conventions:
1. **TypeScript Constraints**:
- Enable strict typing for all new code.
- Do not use the `any` type.
- Do not use type assertions (`as Type`).
- Do not use the non-null assertion operator (`!`).
- Use explicit type guards or helper functions such as `castRef`.
- Use `import type` for type-only imports.
- Define object structures with `interface`, not `type` aliases.
2. **Documentation Rules**:
- Add JSDoc comments to all exported functions, interfaces, and types.
- Include clear description text in every JSDoc block.
3. **Component Architecture**:
- Place components in dedicated directories under `packages/ui/src/components/<ComponentName>/`.
- Provide `<ComponentName>.tsx` for component logic.
- Provide `<ComponentName>.stories.tsx` for Storybook stories.
- Provide `<ComponentName>.test.tsx` for Vitest tests.
- Use `cva` for component variant management.
- Merge CSS utility classes with `cn(...)` from `@ermnvldmr/stl`.
- Use React Aria hooks and Radix UI primitives for accessible controls.
4. **Monorepo Imports and State**:
- Use relative paths within the same package.
- Do not use self-referential package names.
- Use standard React state primitives. Do not add unapproved state managers.
5. **Protected Boundaries**:
- Do not modify `.env.keys`, `pnpm-lock.yaml`, or `LICENSE`.
- Do not edit content files in `services/docs/content/` or `services/www/content/` unless explicitly instructed.
### Phase 3: Monorepo Verification & Self-Correction
Run validation commands in strict sequence:
1. Run `pnpm typecheck`. Ensure zero compiler errors.
2. Run `pnpm lint`. Ensure zero warnings and zero errors.
3. Run `pnpm test`. Ensure all unit and component tests pass.
4. Run `pnpm build`. Ensure clean compilation across all packages.
**Iterative Self-Correction Loop**:
- If any check fails, inspect the diagnostic output.
- Identify the root cause.
- Apply targeted fixes adhering to all code rules.
- Re-run the verification sequence from step 1.
- Repeat until all four checks pass completely.
### Phase 4: Commit & Pull Request Preparation
1. **Commit Message**:
- Format commits according to Conventional Commits: `<type>: implement issue #<number> (<title>)`.
- Example: `feat: implement issue #42 (add theme toggle)`.
2. **Pull Request Target**:
- Set base branch to `next`.
3. **Pull Request Body**:
- Link the issue: `Resolves #<number>`.
- Provide a concise summary of changes.
- Detail the implementation walkthrough.
- List the executed verification checks.
- Append the mandatory attribution line.
4. **Mandatory Attribution**:
- Conclude all PR bodies and issue comments with:
`Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.`
## Pull Request Template
```markdown
## Summary
Resolves #<number>
<Brief explanation of the implemented feature or fix>
## Walkthrough of Changes
- `<package-or-path>`: <Description of specific change>
- `<package-or-path>`: <Description of specific change>
## Verification Results
- [x] `pnpm typecheck` passed (0 errors)
- [x] `pnpm lint` passed (0 warnings)
- [x] `pnpm test` passed (all tests green)
- [x] `pnpm build` passed (clean build)
Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.
```
## Verification Checklist
- [ ] Issue context, approved plan, and user instructions are satisfied.
- [ ] TypeScript strict mode rules are respected (`no any`, `no as`, `no !`).
- [ ] Exported symbols include JSDoc comments.
- [ ] UI components reside in dedicated folders with tests and stories.
- [ ] Monorepo boundaries and relative imports are respected.
- [ ] All four verification checks passed cleanly.
- [ ] Target branch is set to `next`.
- [ ] PR description includes `Resolves #<number>`.
- [ ] Mandatory attribution line is present.
+136
View File
@@ -0,0 +1,136 @@
---
name: github-plan
description: Analyze an issue thread, iterate on feedback, and produce or update an implementation plan for ermnvldmr.com.
---
# GitHub Issue Planning Skill (ASD-STE100 Standard)
## Overview
Use this skill when an issue receives the `/plan [optional instructions]` command.
This skill creates or updates an implementation plan before code modification starts. All outputs must follow ASD-STE100 language constraints and repository standards.
---
## ASD-STE100 Writing Rules
Follow these rules for all generated plans and documentation:
| Rule | Limit | Approved Example | Non-Approved Example |
| :--- | :--- | :--- | :--- |
| **Procedural Sentences** | Max **20 words** | "Run `pnpm test`. Verify that all unit tests pass." | "You should run the pnpm test command to verify that all the unit tests pass properly without errors." |
| **Descriptive Sentences** | Max **25 words** | "The `Button` component uses `cva` to manage style variants." | "The Button component is implemented using the cva library because it helps us manage complex style variants across different themes easily." |
| **Voice** | **Active voice** | "The function validates the user input." | "The user input is validated by the function." |
| **Mood and Tense** | **Present tense** and **Imperative mood** | "Click **Submit**. The form transmits the data." | "You will click submit and the form will be transmitting the data." |
| **Noun Clusters** | Max **3 nouns** | "Theme configuration file" | "Theme variant style configuration file" |
### Controlled Vocabulary Guidelines
- Do not use conversational filler (for example: "basically", "let's dive in", "simply", "feel free to").
- Use approved verbs: use *use* (not *utilize*), *stop* (not *terminate*), *start* (not *initiate*), *show* (not *exhibit*).
- Use clear and literal technical terms.
---
## Planning Responsibilities
### 1. Ingest Full Issue Context
- Read the issue title, issue description, and all subsequent comments.
- Identify user requirements, technical constraints, and reported symptoms.
- Inspect relevant source files in the repository before proposing changes.
- Read `GEMINI.md` to confirm current project conventions.
### 2. Multi-Turn Plan Iteration
- Check if an earlier implementation plan exists in the issue thread.
- If an earlier plan exists:
- Do not create a plan from zero.
- Retain approved architectural decisions from previous turns.
- Apply the requested modifications or additions from the latest comment.
- Summarize the specific adjustments made to the previous plan.
- If no earlier plan exists:
- Create a new baseline implementation plan from the issue requirements.
### 3. Enforce Repository Architecture (`GEMINI.md`)
Every proposed plan must comply with repository architecture:
- **Build System**: Use Rsbuild and Rspack. Do not propose Vite, Webpack, or Next.js.
- **Strict TypeScript**:
- Do not use `any`.
- Do not use type assertions (`as Type`). Use type guards or `castRef` from `@ermnvldmr/stl`.
- Do not use non-null assertions (`!`).
- Use `import type` for type-only imports.
- Define object shapes with `interface`, not `type` aliases.
- Require JSDoc comments on all exported symbols.
- **Component Architecture** (`packages/ui`):
- Place components in `packages/ui/src/components/<ComponentName>/`.
- Provide `<ComponentName>.tsx`, `<ComponentName>.stories.tsx`, and `<ComponentName>.test.tsx`.
- Use `cva` for variant definitions.
- Merge class names with `cn(...)` from `@ermnvldmr/stl`.
- **Accessibility & State**:
- Use React Aria hooks (`useButton`, `useFocusRing`, `useHover`) and Radix UI primitives.
- Use standard React state (`useState`, `useReducer`, `useContext`) and `react-stately`.
- Do not introduce external state management libraries.
- **Monorepo Import Rules**:
- Use relative import paths within the same package.
- Do not import `@ermnvldmr/<package>` inside `<package>` itself.
- **Protected Files**:
- Do not modify `.env.keys`, `pnpm-lock.yaml`, `LICENSE`, `services/docs/content/`, or `services/www/content/`.
---
## Output Plan Structure
Format the generated plan with the following markdown template:
```markdown
### Goal
[Describe the objective in 1-2 sentences. Keep each sentence under 25 words.]
### Architecture & Technical Decisions
- [Decision 1: Explain pattern, primitive, or component structure.]
- [Decision 2: Explain typing or API contracts.]
- [Decision 3: Explain state management or accessibility integration.]
### File Changes
- `[NEW]` `packages/ui/src/components/NewComponent/NewComponent.tsx` - [Purpose]
- `[NEW]` `packages/ui/src/components/NewComponent/NewComponent.stories.tsx` - [Purpose]
- `[NEW]` `packages/ui/src/components/NewComponent/NewComponent.test.tsx` - [Purpose]
- `[MODIFY]` `packages/ui/src/index.ts` - [Export new component]
### Verification Plan
1. `pnpm typecheck` - Verify zero TypeScript compiler errors.
2. `pnpm lint` - Verify zero ESLint warnings and errors.
3. `pnpm test` - Run unit and integration tests.
4. `pnpm build` - Run full workspace build.
### Open Questions
- [List open questions or ambiguities. If no open questions exist, write "None.".]
Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.
```
---
## Common Traps and Solutions
| Anti-Pattern | Correct Action |
| :--- | :--- |
| Regenerating a new plan after user feedback | Update the existing plan and highlight modified sections. |
| Using `as Type` assertions in the proposed code | Propose explicit type guards or `castRef` helpers. |
| Missing Storybook or Vitest files for UI components | Include `.stories.tsx` and `.test.tsx` in the file plan. |
| Proposing external state libraries | Use React 19 primitives and `react-stately`. |
| Omitting the attribution line | Append the mandatory disclosure line at the end. |
---
## Verification Checklist
Before posting the plan response, verify all items:
- [ ] The issue thread context and all user comments are analyzed.
- [ ] Existing plans are updated rather than replaced from scratch.
- [ ] Proposed code satisfies all `GEMINI.md` constraints (`strict: true`, no `any`, no `as`, no `!`).
- [ ] All UI components include unit tests and Storybook stories.
- [ ] All sentences follow ASD-STE100 limits (<=20 words for instructions, <=25 words for descriptions).
- [ ] Active voice is used throughout the plan.
- [ ] The output concludes with: `Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.`
+273
View File
@@ -0,0 +1,273 @@
name: gemini - manual
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
concurrency:
group: agent-issue-${{ github.event.issue.number || github.event.pull_request.number }}
cancel-in-progress: false
permissions:
contents: write
issues: write
pull-requests: write
actions: read
jobs:
agent-dispatch:
name: Agent / Dispatch
if: >
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR') &&
(contains(github.event.comment.body, '/plan') ||
contains(github.event.comment.body, '/implement') ||
contains(github.event.comment.body, '/fix'))
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Verify Collaborator Authorization
id: auth-check
run: |
AUTHOR_ASSOC="${{ github.event.comment.author_association }}"
echo "Author association: $AUTHOR_ASSOC"
if [[ "$AUTHOR_ASSOC" != "OWNER" && "$AUTHOR_ASSOC" != "MEMBER" && "$AUTHOR_ASSOC" != "COLLABORATOR" ]]; then
echo "::error::Unauthorized trigger by user with association '$AUTHOR_ASSOC'."
exit 1
fi
- name: Acknowledge Command (Add Eyes Reaction)
if: success()
env:
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
COMMENT_ID: ${{ github.event.comment.id }}
run: |
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}/reactions" -f content=eyes || true
- name: Checkout Codebase
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- name: Setup Node.js with pnpm cache
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install Dependencies & Build Workspace Packages
run: |
pnpm install --frozen-lockfile
pnpm build:deps
- name: Restore Agent Conversation Context
uses: actions/cache@v4
id: agent-context-cache
with:
path: |
~/.gemini/antigravity-cli/conversations
~/.gemini/antigravity-cli/brain
key: agent-context-issue-${{ github.event.issue.number }}-${{ github.run_id }}
restore-keys: |
agent-context-issue-${{ github.event.issue.number }}-
- name: Setup Antigravity CLI & Credentials
env:
AGY_AUTH_TOKEN: ${{ secrets.AGY_AUTH_TOKEN }}
run: |
# 1. Install Antigravity CLI (agy) if not present
if ! command -v agy &> /dev/null; then
echo "Installing Antigravity CLI (agy)..."
curl -fsSL https://antigravity.google/install.sh | bash || true
mkdir -p "$HOME/.local/bin"
echo "$HOME/.local/bin" >> $GITHUB_PATH
echo "$HOME/.gemini/antigravity-cli/bin" >> $GITHUB_PATH
export PATH="$HOME/.local/bin:$HOME/.gemini/antigravity-cli/bin:$PATH"
fi
# 2. Inject OAuth Token
mkdir -p ~/.gemini/antigravity-cli
if [ -n "$AGY_AUTH_TOKEN" ]; then
if echo "$AGY_AUTH_TOKEN" | base64 --decode >/dev/null 2>&1; then
echo "$AGY_AUTH_TOKEN" | base64 --decode > ~/.gemini/antigravity-cli/antigravity-oauth-token
else
echo "$AGY_AUTH_TOKEN" > ~/.gemini/antigravity-cli/antigravity-oauth-token
fi
chmod 600 ~/.gemini/antigravity-cli/antigravity-oauth-token
else
echo "::warning::AGY_AUTH_TOKEN secret is not set."
fi
- name: Configure Git Bot Identity
run: |
git config --global user.name "AI Assistant Bot"
git config --global user.email "bot@users.noreply.github.com"
# --- COMMAND: /plan ---
- name: Handle /plan (Issue Planning)
if: contains(github.event.comment.body, '/plan') && github.event.issue.pull_request == null
env:
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
echo "Generating plan for Issue #${ISSUE_NUMBER}..."
ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --comments --json title,body,comments)
CONTINUE_FLAG=""
if [ -d "$HOME/.gemini/antigravity-cli/conversations" ] && [ "$(ls -A $HOME/.gemini/antigravity-cli/conversations 2>/dev/null)" ]; then
CONTINUE_FLAG="--continue"
fi
PROMPT="You are operating via GitHub Actions on Issue #${ISSUE_NUMBER}.\n\nIssue Context:\n${ISSUE_DATA}\n\nLatest instruction:\n${COMMENT_BODY}\n\nPlease generate/update a detailed technical implementation plan adhering to project GEMINI.md guidelines."
PLAN_OUTPUT=$(agy $CONTINUE_FLAG --mode plan -p "$PROMPT" || echo "Agent encountered an error generating the plan.")
DISCLOSURE="\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
gh issue comment "$ISSUE_NUMBER" --body "${PLAN_OUTPUT}${DISCLOSURE}"
# --- COMMAND: /implement ---
- name: Handle /implement (Issue Implementation & PR)
if: contains(github.event.comment.body, '/implement') && github.event.issue.pull_request == null
env:
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
BRANCH_NAME="agent/issue-${ISSUE_NUMBER}"
git checkout -b "$BRANCH_NAME"
ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --comments --json title,body,comments)
CONTINUE_FLAG=""
if [ -d "$HOME/.gemini/antigravity-cli/conversations" ] && [ "$(ls -A $HOME/.gemini/antigravity-cli/conversations 2>/dev/null)" ]; then
CONTINUE_FLAG="--continue"
fi
PROMPT="You are operating via GitHub Actions to implement Issue #${ISSUE_NUMBER}.\n\nContext:\n${ISSUE_DATA}\n\nLatest instruction:\n${COMMENT_BODY}\n\nImplement the changes cleanly in the repository according to GEMINI.md rules."
agy $CONTINUE_FLAG --dangerously-skip-permissions -p "$PROMPT"
echo "Running repository verification gates..."
pnpm typecheck
pnpm lint
pnpm test
ISSUE_TITLE=$(gh issue view "$ISSUE_NUMBER" --json title -q .title)
git add -A
if git diff --staged --quiet; then
echo "No changes produced by agent."
gh issue comment "$ISSUE_NUMBER" --body "No changes were produced for implementation.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
exit 0
fi
git commit -m "feat: implement issue #${ISSUE_NUMBER} (${ISSUE_TITLE})"
git push -u origin "$BRANCH_NAME" --force
PR_BODY="Resolves #${ISSUE_NUMBER}\n\nAutomated implementation generated via \`/implement\`.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
gh pr create \
--title "feat: ${ISSUE_TITLE}" \
--body "$PR_BODY" \
--base next \
--head "$BRANCH_NAME"
# --- COMMAND: /fix ---
- name: Handle /fix (PR Review Adjustments & CI Failure Debugging)
if: contains(github.event.comment.body, '/fix')
env:
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
ISSUE_OR_PR_NUMBER: ${{ github.event.issue.number }}
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
IS_PR="${{ github.event.issue.pull_request != null }}"
if [ "$IS_PR" = "true" ]; then
IS_CROSS_REPO=$(gh pr view "$ISSUE_OR_PR_NUMBER" --json isCrossRepository -q .isCrossRepository)
if [ "$IS_CROSS_REPO" = "true" ]; then
echo "::error::Cross-repository fork PRs cannot be automatically processed in privileged context."
exit 1
fi
gh pr checkout "$ISSUE_OR_PR_NUMBER"
BRANCH_NAME=$(git branch --show-current)
PR_DIFF=$(gh pr diff "$ISSUE_OR_PR_NUMBER" || true)
FAILED_RUN_ID=$(gh run list --branch "$BRANCH_NAME" --status failure --limit 1 --json databaseId -q '.[0].databaseId' || true)
CI_LOGS=""
if [ -n "$FAILED_RUN_ID" ] && [ "$FAILED_RUN_ID" != "null" ]; then
echo "Found failed CI run: #$FAILED_RUN_ID. Fetching failure logs..."
CI_LOGS=$(gh run view "$FAILED_RUN_ID" --log-failed | tail -n 200 || true)
fi
CONTINUE_FLAG=""
if [ -d "$HOME/.gemini/antigravity-cli/conversations" ] && [ "$(ls -A $HOME/.gemini/antigravity-cli/conversations 2>/dev/null)" ]; then
CONTINUE_FLAG="--continue"
fi
PROMPT="You are operating via GitHub Actions to fix/adjust PR #${ISSUE_OR_PR_NUMBER} on branch ${BRANCH_NAME}.\n\nFeedback / Comment:\n${COMMENT_BODY}\n\nPR Diff:\n${PR_DIFF}\n\nFailed CI Logs (if any):\n${CI_LOGS}\n\nApply the necessary fixes, making sure all lint, typecheck, and test checks pass."
agy $CONTINUE_FLAG --dangerously-skip-permissions -p "$PROMPT"
echo "Running repository verification gates..."
pnpm typecheck
pnpm lint
pnpm test
git add -A
if git diff --staged --quiet; then
echo "No additional changes were needed."
gh pr comment "$ISSUE_OR_PR_NUMBER" --body "Analyzed the request; no additional changes were needed.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
exit 0
fi
git commit -m "fix: apply review adjustments and CI fixes"
git push origin "$BRANCH_NAME"
gh pr comment "$ISSUE_OR_PR_NUMBER" --body "Applied adjustments and verified build/test suite.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*"
else
echo "Handling /fix on Issue #${ISSUE_OR_PR_NUMBER}..."
BRANCH_NAME="agent/fix-issue-${ISSUE_OR_PR_NUMBER}"
git checkout -b "$BRANCH_NAME"
ISSUE_DATA=$(gh issue view "$ISSUE_OR_PR_NUMBER" --comments --json title,body,comments)
PROMPT="Fix issue #${ISSUE_OR_PR_NUMBER}.\n\nContext:\n${ISSUE_DATA}\n\nInstruction:\n${COMMENT_BODY}"
agy --dangerously-skip-permissions -p "$PROMPT"
pnpm typecheck
pnpm lint
pnpm test
git add -A
git commit -m "fix: resolve issue #${ISSUE_OR_PR_NUMBER}"
git push -u origin "$BRANCH_NAME" --force
gh pr create \
--title "fix: resolve issue #${ISSUE_OR_PR_NUMBER}" \
--body "Resolves #${ISSUE_OR_PR_NUMBER}\n\nFix applied via \`/fix\`.\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*" \
--base next \
--head "$BRANCH_NAME"
fi
- name: Complete Command (Add Rocket Reaction)
if: success()
env:
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
COMMENT_ID: ${{ github.event.comment.id }}
run: |
gh api "repos/${{ github.repository }}/issues/comments/${COMMENT_ID}/reactions" -f content=rocket || true
- name: Report Failure on Comment
if: failure()
env:
GH_TOKEN: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
gh issue comment "$ISSUE_NUMBER" --body "⚠️ Workflow failed during execution. View run logs: [Action Run #${{ github.run_id }}](${RUN_URL})\n\n---\n*Disclosure: I'm an AI assistant helping deytenit; I'm not deytenit.*" || true
@@ -1,66 +0,0 @@
name: services/www - next/checkout-release-branch
on:
workflow_dispatch:
inputs:
increment_major:
description: 'Force major version increment (e.g. v2.0 instead of v1.1)'
type: boolean
required: false
default: false
permissions:
contents: read
actions: read
jobs:
workflow-release-branch:
name: Workflow - Create or Update Release Branch
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine Mode and Branch Name
id: plan
run: |
REF="${{ github.ref_name }}"
if [[ "$REF" =~ ^releases/www/v[0-9]+\.[0-9]+$ ]]; then
# Dispatched against an existing release branch: update it.
echo "mode=update" >> "$GITHUB_OUTPUT"
echo "branch_name=$REF" >> "$GITHUB_OUTPUT"
else
# Dispatched against next (or any other ref): create a new release branch.
BRANCH_NAME=$(bash ci/scripts/semantic-release.sh checkout services/www "${{ inputs.increment_major }}")
echo "mode=create" >> "$GITHUB_OUTPUT"
echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
fi
- name: Create and Push Branch
if: steps.plan.outputs.mode == 'create'
run: |
git checkout -b ${{ steps.plan.outputs.branch_name }}
git push origin ${{ steps.plan.outputs.branch_name }}
- name: Rebase Existing Branch onto next
if: steps.plan.outputs.mode == 'update'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# Bring the release branch up to date with its parent (next),
# replaying any release-branch-only commits on top of the latest next.
git checkout ${{ steps.plan.outputs.branch_name }}
git fetch origin next
git rebase origin/next
git push --force-with-lease origin ${{ steps.plan.outputs.branch_name }}
- name: Trigger Release Workflow
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run services-www-releases-required.yml --ref ${{ steps.plan.outputs.branch_name }}
+129 -17
View File
@@ -10,6 +10,7 @@ on:
- 'packages/i18n/**'
- '.github/workflows/services-www-next-required.yml'
- 'pnpm-lock.yaml'
workflow_dispatch:
concurrency:
group: ci-www-${{ github.ref_name }}
@@ -330,17 +331,25 @@ jobs:
path: services/www/rsdoctor/
retention-days: 30
release-docker:
name: Release - Docker Image
needs: build-www
build-docker:
name: Build - Docker Image
needs: [lint-www, typecheck-www, test-www, test-storybook, build-www]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
short_sha: ${{ steps.vars.outputs.short_sha }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Calculate Short SHA
id: vars
run: |
SHORT_SHA="sha-$(echo ${{ github.sha }} | cut -c1-7)"
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
@@ -348,18 +357,8 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/www
tags: |
type=raw,value=next
type=sha,format=short
- name: Compute short SHA
id: vars
run: echo "short_sha=sha-$(echo ${{ github.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@v5
@@ -367,8 +366,121 @@ jobs:
context: .
file: ci/docker/www/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ github.repository }}/www:${{ steps.vars.outputs.short_sha }}
build-args: |
COMMIT_SHA=${{ steps.vars.outputs.short_sha }}
VERSION=${{ steps.vars.outputs.short_sha }}
release-tag-docker:
name: Release - Tag & Docker
needs: build-docker
runs-on: ubuntu-latest
environment: services-www-releases-publish
permissions:
contents: write
packages: write
outputs:
version: ${{ steps.calver.outputs.version }}
short_sha: ${{ needs.build-docker.outputs.short_sha }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Calculate CalVer Version
id: calver
run: |
VERSION=$(bash ci/scripts/calver-release.sh)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create and Push Tag
run: |
TAG_NAME="${{ steps.calver.outputs.version }}"
git tag "$TAG_NAME"
git push origin "$TAG_NAME"
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Tag and Push Release Docker Image
run: |
docker buildx imagetools create \
ghcr.io/${{ github.repository }}/www:${{ needs.build-docker.outputs.short_sha }} \
--tag ghcr.io/${{ github.repository }}/www:${{ steps.calver.outputs.version }}
deploy:
name: Deploy - Kubernetes (GitOps)
needs: release-tag-docker
runs-on: ubuntu-latest
environment:
name: services-www-releases-deploy
url: https://ermnvldmr.com
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: next
token: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
- name: Setup Kustomize
uses: imranismail/setup-kustomize@v2
with:
kustomize-version: "5.3.0"
- name: Deploy via GitOps
run: |
WWW_TAG="${{ needs.release-tag-docker.outputs.short_sha }}"
bash ci/scripts/deploy-gitops.sh www "${WWW_TAG}"
verify-rollout:
name: Verify - Production Rollout
needs: [release-tag-docker, deploy]
runs-on: ubuntu-latest
steps:
- name: Await production rollout
run: |
TARGET_COMMIT="${{ needs.release-tag-docker.outputs.short_sha }}"
ENDPOINT="https://www.ermnvldmr.com/version.json"
echo "========================================================"
echo "Verifying production deployment on $ENDPOINT"
echo "Expected commit: $TARGET_COMMIT"
echo "========================================================"
MAX_ATTEMPTS=45
ATTEMPT=1
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Polling $ENDPOINT..."
RESPONSE=$(curl -sfL --max-time 10 -H "User-Agent: GitHub-Actions-Rollout-Verifier" "$ENDPOINT" 2>/dev/null || true)
if [ -n "$RESPONSE" ]; then
LIVE_COMMIT=$(echo "$RESPONSE" | jq -r '.commit // empty')
echo " Live commit: ${LIVE_COMMIT:-<none>}"
if [ "$LIVE_COMMIT" = "$TARGET_COMMIT" ]; then
echo "✅ Success! Production has successfully rolled out $TARGET_COMMIT."
exit 0
fi
else
echo " Endpoint did not return valid JSON or timed out."
fi
ATTEMPT=$((ATTEMPT + 1))
sleep 10
done
echo "❌ Deployment verification timed out after 7.5 minutes."
exit 1
@@ -1,382 +0,0 @@
name: services/www - releases/dispatch-release
on:
workflow_dispatch:
inputs:
skip_ci:
description: 'Skip CI checks (fast build, push & GitOps deploy)'
type: boolean
required: false
default: false
permissions:
contents: read
actions: read
jobs:
setup-stage:
name: Setup - Stage
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
outputs:
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- name: Setup Node.js with pnpm cache
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Cache pnpm store
id: cache-pnpm-store
uses: actions/cache@v3
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
build-configs:
name: Build - Configs
needs: setup-stage
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Rsbuild config
run: pnpm --filter @ermnvldmr/rsbuild-config build
- name: Build Storybook config
run: pnpm --filter @ermnvldmr/storybook-config build
- name: Build i18n
run: pnpm --filter @ermnvldmr/i18n build
- name: Upload Config Artifacts
uses: actions/upload-artifact@v4
with:
name: config-artifacts
path: |
packages/rsbuild-config/dist
packages/storybook-config/dist
packages/i18n/dist
retention-days: 1
lint-www:
name: Lint - WWW
needs: build-configs
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Lint WWW service
run: pnpm --filter @ermnvldmr/www lint
typecheck-www:
name: Typecheck - WWW
needs: build-configs
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Typecheck WWW service
run: pnpm --filter @ermnvldmr/www typecheck
build-storybook:
name: Build - Storybook
needs: build-configs
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Build WWW Storybook
run: pnpm --filter @ermnvldmr/www build-storybook
- name: Upload Storybook build
uses: actions/upload-artifact@v4
with:
name: storybook-static
path: services/www/storybook-static/
retention-days: 1
test-www:
name: Test - Unit
needs: build-configs
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Test WWW service
run: pnpm --filter @ermnvldmr/www test:coverage
test-storybook:
name: Test - Storybook
needs: build-storybook
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Install Playwright browsers
run: pnpm --filter @ermnvldmr/www exec playwright install --with-deps chromium
- name: Download Storybook build
uses: actions/download-artifact@v4
with:
name: storybook-static
path: services/www/storybook-static/
- name: Serve Storybook and run tests
run: pnpm --filter @ermnvldmr/www test:storybook:ci
build-www:
name: Build - WWW Service
needs: build-configs
if: ${{ !inputs.skip_ci }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Build WWW service
run: pnpm --filter @ermnvldmr/www build
release-tag-docker:
name: Release - Tag & Docker
needs: [lint-www, typecheck-www, test-www, test-storybook, build-www]
if: ${{ always() && (inputs.skip_ci || (!contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled'))) }}
runs-on: ubuntu-latest
environment: services-www-releases-publish
permissions:
contents: write
packages: write
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Calculate Release Version
id: version
run: |
RELEASE_VERSION=$(bash ci/scripts/semantic-release.sh release ${{ github.ref_name }})
# Extract MAJOR.MINOR for tagging
MAJOR_MINOR=$(echo "${{ github.ref_name }}" | cut -d/ -f3 | sed 's/^v//')
MAJOR=$(echo "$MAJOR_MINOR" | cut -d. -f1)
SHORT_SHA="sha-$(echo ${{ github.sha }} | cut -c1-7)"
echo "version=$RELEASE_VERSION" >> $GITHUB_OUTPUT
echo "major_minor=$MAJOR_MINOR" >> $GITHUB_OUTPUT
echo "major=$MAJOR" >> $GITHUB_OUTPUT
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
- name: Create and Push Tag
run: |
TAG_NAME="releases/www/v${{ steps.version.outputs.version }}"
git tag $TAG_NAME
git push origin $TAG_NAME
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/www
tags: |
type=raw,value=v${{ steps.version.outputs.version }}
type=raw,value=v${{ steps.version.outputs.major_minor }}
type=raw,value=v${{ steps.version.outputs.major }}
type=raw,value=latest
type=sha,format=short
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ci/docker/www/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
COMMIT_SHA=${{ steps.version.outputs.short_sha }}
VERSION=v${{ steps.version.outputs.version }}
deploy:
name: Deploy - Kubernetes (GitOps)
needs: release-tag-docker
runs-on: ubuntu-latest
environment:
name: services-www-releases-deploy
url: https://ermnvldmr.com
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: next
token: ${{ secrets.GITOPS_TOKEN || secrets.GITHUB_TOKEN }}
- name: Setup Kustomize
uses: imranismail/setup-kustomize@v2
with:
kustomize-version: "5.3.0"
- name: Deploy via GitOps
run: |
WWW_TAG="sha-$(echo ${{ github.sha }} | cut -c1-7)"
bash ci/scripts/deploy-gitops.sh www "${WWW_TAG}"
verify-rollout:
name: Verify - Production Rollout
needs: [release-tag-docker, deploy]
runs-on: ubuntu-latest
steps:
- name: Await production rollout
run: |
TARGET_COMMIT="sha-$(echo ${{ github.sha }} | cut -c1-7)"
TARGET_VERSION="v${{ needs.release-tag-docker.outputs.version }}"
ENDPOINT="https://www.ermnvldmr.com/version.json"
echo "========================================================"
echo "Verifying production deployment on $ENDPOINT"
echo "Expected commit: $TARGET_COMMIT"
echo "Expected version: $TARGET_VERSION"
echo "========================================================"
MAX_ATTEMPTS=45
ATTEMPT=1
while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Polling $ENDPOINT..."
RESPONSE=$(curl -sfL --max-time 10 -H "User-Agent: GitHub-Actions-Rollout-Verifier" "$ENDPOINT" 2>/dev/null || true)
if [ -n "$RESPONSE" ]; then
LIVE_COMMIT=$(echo "$RESPONSE" | jq -r '.commit // empty')
LIVE_VERSION=$(echo "$RESPONSE" | jq -r '.version // empty')
echo " Live commit: ${LIVE_COMMIT:-<none>}"
echo " Live version: ${LIVE_VERSION:-<none>}"
if [ "$LIVE_COMMIT" = "$TARGET_COMMIT" ] || [ "$LIVE_VERSION" = "$TARGET_VERSION" ]; then
echo "✅ Success! Production has successfully rolled out $TARGET_VERSION ($TARGET_COMMIT)."
exit 0
fi
else
echo " Endpoint did not return valid JSON or timed out."
fi
ATTEMPT=$((ATTEMPT + 1))
sleep 10
done
echo "❌ Deployment verification timed out after 7.5 minutes."
exit 1
@@ -1,278 +0,0 @@
name: services/www - releases/required
on:
push:
branches: ['releases/www/**']
workflow_dispatch:
concurrency:
group: ci-www-rel-${{ github.ref_name }}
cancel-in-progress: true
permissions:
contents: read
actions: read
jobs:
setup-stage:
name: Setup - Stage
runs-on: ubuntu-latest
outputs:
cache-hit: ${{ steps.cache-pnpm-store.outputs.cache-hit }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- name: Setup Node.js with pnpm cache
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Cache pnpm store
id: cache-pnpm-store
uses: actions/cache@v3
with:
path: ~/.pnpm-store
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
build-configs:
name: Build - Configs
needs: setup-stage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build Rsbuild config
run: pnpm --filter @ermnvldmr/rsbuild-config build
- name: Build Storybook config
run: pnpm --filter @ermnvldmr/storybook-config build
- name: Build i18n
run: pnpm --filter @ermnvldmr/i18n build
- name: Upload Config Artifacts
uses: actions/upload-artifact@v4
with:
name: config-artifacts
path: |
packages/rsbuild-config/dist
packages/storybook-config/dist
packages/i18n/dist
retention-days: 1
lint-www:
name: Lint - WWW
needs: build-configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Lint WWW service
run: pnpm --filter @ermnvldmr/www lint
typecheck-www:
name: Typecheck - WWW
needs: build-configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Typecheck WWW service
run: pnpm --filter @ermnvldmr/www typecheck
build-storybook:
name: Build - Storybook
needs: build-configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Build WWW Storybook
run: pnpm --filter @ermnvldmr/www build-storybook
- name: Upload Storybook build
uses: actions/upload-artifact@v4
with:
name: storybook-static
path: services/www/storybook-static/
retention-days: 1
test-www:
name: Test - Unit
needs: build-configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Test WWW service
run: pnpm --filter @ermnvldmr/www test:coverage
test-storybook:
name: Test - Storybook
needs: build-storybook
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Install Playwright browsers
run: pnpm --filter @ermnvldmr/www exec playwright install --with-deps chromium
- name: Download Storybook build
uses: actions/download-artifact@v4
with:
name: storybook-static
path: services/www/storybook-static/
- name: Serve Storybook and run tests
run: pnpm --filter @ermnvldmr/www test:storybook:ci
build-www:
name: Build - WWW Service
needs: build-configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.12.3
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Download Config Artifacts
uses: actions/download-artifact@v4
with:
name: config-artifacts
path: packages
- name: Build WWW service
run: pnpm --filter @ermnvldmr/www build
release-rc-docker:
name: Release - RC Docker Image
needs: build-www
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Calculate RC Version
id: version
run: |
RC_VERSION=$(bash ci/scripts/semantic-release.sh rc ${{ github.ref_name }})
echo "rc_version=$RC_VERSION" >> $GITHUB_OUTPUT
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/www
tags: |
type=raw,value=v${{ steps.version.outputs.rc_version }}
type=sha,format=short
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ci/docker/www/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+1 -2
View File
@@ -123,7 +123,7 @@ ermnvldmr.com/
- **DO NOT** use self-referential package imports within workspace packages; use relative paths.
- **DO NOT** suppress linter or compiler errors with `@ts-ignore`, `@ts-nocheck`, or inline eslint disables without explicit user approval.
- **DO NOT** commit unencrypted `.env` files or secret keys (`.env.keys`).
- **DO NOT** run `git commit` or execute automated git commits under any circumstances.
- **DO NOT** run destructive Git operations (such as force pushing to `main`/`next` or dropping uncommitted user work) without authorization.
## 5. Critical Workflows & Safety Directives
@@ -143,5 +143,4 @@ The agent MUST NOT modify the following files or directories unless explicitly r
- `.env.keys` (untracked decryption keys).
- `pnpm-lock.yaml` (dependencies must be managed exclusively through `pnpm` CLI commands).
- `LICENSE` file.
- `ci/` deployment configurations, Dockerfiles, and GitHub Actions workflows.
- `services/docs/content/` and `services/www/content/` creative and literary works.
+21
View File
@@ -0,0 +1,21 @@
#!/bin/bash
set -euo pipefail
YEAR_MONTH=$(date -u +'%Y.%m')
git fetch --tags --quiet 2>/dev/null || true
# Find highest MICRO version for current YYYY.MM (matching tags formatted like YYYY.MM.MICRO)
LATEST_TAG=$(git tag -l "${YEAR_MONTH}.*" | sort -V | tail -n1 || echo "")
if [ -z "$LATEST_TAG" ]; then
MICRO=0
else
LATEST_MICRO="${LATEST_TAG#"${YEAR_MONTH}."}"
if [[ "$LATEST_MICRO" =~ ^[0-9]+$ ]]; then
MICRO=$((LATEST_MICRO + 1))
else
MICRO=0
fi
fi
echo "${YEAR_MONTH}.${MICRO}"
-116
View File
@@ -1,116 +0,0 @@
#!/bin/bash
set -e
MODE=$1
TARGET=$2
INCREMENT_MAJOR=${3:-false}
if [ -z "$MODE" ] || [ -z "$TARGET" ]; then
echo "Usage: $0 <mode: checkout|rc|release> <target: service_path|branch_name> [increment_major: true|false]"
exit 1
fi
case $MODE in
checkout)
SERVICE_PATH=$TARGET
SERVICE_NAME=$(basename "$SERVICE_PATH")
# Ensure we have the latest tags and branches
git fetch --all --tags --quiet 2>/dev/null || true
BRANCH_NAME=$(node -e '
const { execSync } = require("child_process");
const serviceName = process.argv[1];
const servicePath = process.argv[2];
const incrementMajor = process.argv[3] === "true" || process.argv[3] === "major" || process.argv[3] === "1";
let rawBranchesAndTags = "";
try {
const remoteBranches = execSync(`git branch -r --list "origin/releases/${serviceName}/v*"`, { encoding: "utf8" });
const localBranches = execSync(`git branch --list "releases/${serviceName}/v*"`, { encoding: "utf8" });
const tags = execSync(`git tag -l "releases/${serviceName}/v*"`, { encoding: "utf8" });
rawBranchesAndTags = `${remoteBranches}\n${localBranches}\n${tags}`;
} catch (e) {}
const versionMatches = Array.from(rawBranchesAndTags.matchAll(/releases\/[^\/]+\/v([0-9]+)\.([0-9]+)/g));
if (versionMatches.length === 0) {
let pkgVersion = "1.0";
try {
pkgVersion = require(`./${servicePath}/package.json`).version.split(".").slice(0, 2).join(".");
} catch (e) {}
console.log(`releases/${serviceName}/v${pkgVersion}`);
process.exit(0);
}
let maxMajor = 1;
let maxMinor = 0;
for (const match of versionMatches) {
const maj = parseInt(match[1], 10);
const min = parseInt(match[2], 10);
if (!isNaN(maj) && !isNaN(min)) {
if (maj > maxMajor || (maj === maxMajor && min > maxMinor)) {
maxMajor = maj;
maxMinor = min;
}
}
}
if (incrementMajor) {
console.log(`releases/${serviceName}/v${maxMajor + 1}.0`);
} else {
console.log(`releases/${serviceName}/v${maxMajor}.${maxMinor + 1}`);
}
' "$SERVICE_NAME" "$SERVICE_PATH" "$INCREMENT_MAJOR")
echo "$BRANCH_NAME"
;;
rc|release)
BRANCH_NAME=$TARGET
# Extract service name and major.minor from branch: releases/www/v1.2
if [[ ! $BRANCH_NAME =~ ^releases/([^/]+)/v([0-9]+\.[0-9]+)$ ]]; then
echo "Error: Branch name must follow 'releases/<service>/v<major>.<minor>' format"
exit 1
fi
SERVICE_NAME="${BASH_REMATCH[1]}"
MAJOR_MINOR="${BASH_REMATCH[2]}"
TAG_PREFIX="releases/$SERVICE_NAME/v$MAJOR_MINOR"
# Ensure we have the latest tags
git fetch --tags --quiet
# Find the latest patch version for this Major.Minor
LATEST_TAG=$(git tag -l "$TAG_PREFIX.*" | grep -v "\-rc" | sort -V | tail -n1 || echo "")
if [ -z "$LATEST_TAG" ]; then
PATCH=0
else
LATEST_VERSION=${LATEST_TAG#"$TAG_PREFIX."}
PATCH=$((LATEST_VERSION + 1))
fi
VERSION_TAG="$MAJOR_MINOR.$PATCH"
if [ "$MODE" == "release" ]; then
echo "$VERSION_TAG"
else
# Calculate RC based on commit count since last tag
# This ensures unique, increasing RC numbers without needing to tag every RC
LAST_SERVICE_TAG=$(git describe --tags --match "releases/$SERVICE_NAME/*" --abbrev=0 HEAD 2>/dev/null || echo "")
if [ -z "$LAST_SERVICE_TAG" ]; then
COMMITS_COUNT=$(git rev-list --count HEAD)
else
COMMITS_COUNT=$(git rev-list --count "$LAST_SERVICE_TAG"..HEAD)
fi
RC=$((COMMITS_COUNT + 1))
echo "$VERSION_TAG-rc.$RC"
fi
;;
*)
echo "Unknown mode: $MODE"
exit 1
;;
esac
@@ -39,10 +39,10 @@ ci/k8s/
* **Workflow**: `.github/workflows/services-docs-next-required.yml`.
* **Action**: Builds Docker image, updates `ci/k8s/apps/docs/kustomization.yaml`, commits to `next`, and runs automated `verify-rollout`.
2. **`services/www` (Semantic Release Dispatch)**:
* **Trigger**: Dispatched via `.github/workflows/services-www-releases-dispatch-release.yml`.
* **Standard Mode**: Runs full verification suite (Vitest, Storybook Playwright, Lint, Typecheck, Build) ➔ Tags git release ➔ Pushes image to GHCR ➔ Updates GitOps tag ➔ Runs `verify-rollout`.
* **Fast-Track Mode (`skip_ci: true`)**: Bypasses test suites for emergency deploys and rollbacks, immediately building the image, updating GitOps, and verifying the production rollout.
2. **`services/www` (Trunk-Based CD with CalVer Release Gate)**:
* **Trigger**: Push to `next` modifying `services/www/**` or workspace dependencies (`packages/ui/**`, `packages/stl/**`, `packages/i18n/**`), or manual `workflow_dispatch`.
* **Workflow**: `.github/workflows/services-www-next-required.yml`.
* **Pipeline**: Full validation suite (Vitest, Storybook Playwright, Lint, Typecheck, Build) ➔ Builds Docker image with `sha-<commit>` tag ➔ Approval gate (`services-www-releases-publish`) tags CalVer `YYYY.MM.MICRO` in Git & GHCR ➔ Approval gate (`services-www-releases-deploy`) updates GitOps tag ➔ Runs `verify-rollout`.
---
@@ -39,10 +39,10 @@ ci/k8s/
* **Workflow**: `.github/workflows/services-docs-next-required.yml`.
* **Действие**: Сборка Docker-образа, обновление `ci/k8s/apps/docs/kustomization.yaml`, коммит в `next` и запуск задачи `verify-rollout`.
2. **`services/www` (Семантический релиз через Dispatch)**:
* **Триггер**: Запуск вручную через `.github/workflows/services-www-releases-dispatch-release.yml`.
* **Стандартный режим**: Полный цикл тестов (Vitest, Storybook Playwright, Lint, Typecheck, Build) ➔ Git-тег релиза ➔ Пуш в GHCR ➔ Обновление тега в GitOps ➔ Проверка через `verify-rollout`.
* **Быстрый режим (`skip_ci: true`)**: Пропуск тестов для экстренных деплоев и откатов с мгновенной сборкой образа, пушем в GitOps и верификацией.
2. **`services/www` (Trunk-Based CD с релизным гейтом CalVer)**:
* **Триггер**: Push в ветку `next` с изменениями в `services/www/**` или зависимых пакетах (`packages/ui/**`, `packages/stl/**`, `packages/i18n/**`), либо ручной `workflow_dispatch`.
* **Workflow**: `.github/workflows/services-www-next-required.yml`.
* **Пайплайн**: Полный цикл тестов и проверок (Vitest, Storybook Playwright, Lint, Typecheck, Build) ➔ Сборка Docker-образа с тегом `sha-<commit>` ➔ Гейт подтверждения (`services-www-releases-publish`) выставляет тег CalVer `YYYY.MM.MICRO` в Git и GHCR ➔ Гейт подтверждения (`services-www-releases-deploy`) обновляет GitOps ➔ Проверка раскатки через `verify-rollout`.
---