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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @CISCODE-MA/devops
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 2
updates:
- package-ecosystem: npm
directory: '/'
schedule:
interval: monthly
open-pull-requests-limit: 1
groups:
npm-dependencies:
patterns:
- '*'
assignees:
- CISCODE-MA/cloud-devops
labels:
- 'dependencies'
- 'npm'
commit-message:
prefix: 'chore(deps)'
include: 'scope'
rebase-strategy: auto
109 changes: 66 additions & 43 deletions .github/instructions/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,82 @@
# Copilot Instructions - React Component Library
# Copilot Instructions - HooksKit

> **Purpose**: Development guidelines for React component libraries - reusable, well-structured components for modern apps.
> **Purpose**: Development guidelines for HooksKit — production-ready React hooks with zero runtime deps.

---

## 🎯 Module Overview

**Package**: `@ciscode/ui-components` (example)
**Type**: React Component Library
**Package**: `@ciscode/hooks-kit`
**Epic**: COMPT-2 — HooksKit
**Type**: React Hooks Library
**Framework**: React 18+, TypeScript 5+
**Build**: Vite/tsup
**Build**: tsup
**Testing**: Vitest + React Testing Library
**Distribution**: NPM package
**Purpose**: Reusable, production-ready React components for building modern UIs
**Purpose**: 12 production-ready React hooks. Zero runtime deps. SSR-safe.

### Typical Module Responsibilities:
### Hook Groups:

- Atomic UI components (Button, Input, Card, etc.)
- Composite components (Form, Modal, Navigation, etc.)
- Hooks for common patterns
- Type definitions and props interfaces
- Accessibility compliance (WCAG 2.1 AA)
- Theming and customization
- Comprehensive documentation
- **State & Storage** (COMPT-30 ✅) — `useDebounce`, `useLocalStorage`, `useSessionStorage`
- **DOM & Events** (COMPT-31 ✅) — `useMediaQuery`, `useWindowSize`, `useClickOutside`, `useIntersectionObserver`
- **Async & Lifecycle** (COMPT-32 ✅) — `usePrevious`, `useToggle`, `useInterval`, `useTimeout`, `useIsFirstRender`
- **Test Suite** (COMPT-33 ✅) — Full coverage for all 12 hooks, all tests in `src/hooks/__tests__/`
- **README & Publish** (COMPT-34 ✅) — Full README with usage examples, v0.1.0 published to `@ciscode/hooks-kit`

### Module Responsibilities:

- Generic, fully-typed hooks with inference at call site
- SSR-safe (`typeof window === 'undefined'` guards in every DOM hook)
- Zero runtime dependencies
- All listeners registered in `useEffect` and cleaned up on unmount
- WCAG-accessible patterns where applicable
- Hooks ≥ 90% coverage

---

## 🏗️ Module Structure

```
src/
├── components/ # React components
│ ├── Button/
│ │ ├── Button.tsx # Component
│ │ ├── Button.test.tsx # Tests
│ │ ├── Button.types.ts # Props types
│ │ └── index.ts # Exports
│ ├── Input/
│ ├── Modal/
│ └── Form/
├── hooks/ # Custom hooks
│ ├── useModal.ts
│ ├── useForm.ts
│ └── useModal.test.ts
├── context/ # Context providers
│ ├── ThemeContext.tsx
│ └── FormContext.tsx
├── types/ # TypeScript types
│ └── common.types.ts
├── utils/ # Utilities
│ └── classNameUtils.ts
└── index.ts # Public API
├── components/ # Minimal supporting components
│ ├── NoopButton.tsx
│ └── index.ts
├── hooks/ # All public hooks
│ ├── useDebounce.ts # COMPT-30 ✅
│ ├── useLocalStorage.ts # COMPT-30 ✅
│ ├── useSessionStorage.ts # COMPT-30 ✅
│ ├── storage.ts # Internal SSR-safe storage helper
│ ├── useMediaQuery.ts # COMPT-31 ✅
│ ├── useWindowSize.ts # COMPT-31 ✅
│ ├── useClickOutside.ts # COMPT-31 ✅
│ ├── useIntersectionObserver.ts # COMPT-31 ✅
│ ├── usePrevious.ts # COMPT-32 ✅
│ ├── useToggle.ts # COMPT-32 ✅
│ ├── useInterval.ts # COMPT-32 ✅
│ ├── useTimeout.ts # COMPT-32 ✅
│ ├── useIsFirstRender.ts # COMPT-32 ✅
│ ├── index.ts # Hook barrel
│ └── __tests__/ # All hook tests (COMPT-33 ✅)
│ ├── useDebounce.test.ts
│ ├── useLocalStorage.test.ts
│ ├── useSessionStorage.test.ts
│ ├── useMediaQuery.test.ts
│ ├── useWindowSize.test.ts
│ ├── useClickOutside.test.ts
│ ├── useIntersectionObserver.test.ts
│ ├── usePrevious.test.ts
│ ├── useToggle.test.ts
│ ├── useInterval.test.ts
│ ├── useTimeout.test.ts
│ └── useIsFirstRender.test.ts
├── utils/ # Framework-agnostic utils
│ ├── noop.ts
│ └── index.ts
└── index.ts # Public API (only entry point)
```

> ⚠️ Only export from `src/index.ts`. Deep imports are forbidden.

---

## 📝 Naming Conventions
Expand Down Expand Up @@ -277,18 +301,17 @@ export type { ButtonProps, ModalProps, InputProps, FormProps } from './component
**1. Branch Creation:**

```bash
feature/UI-MODULE-123-add-datepicker
bugfix/UI-MODULE-456-fix-modal-focus
refactor/UI-MODULE-789-extract-button-styles
feat/COMPT-30-state-storage-hooks
bugfix/COMPT-XX-short-description
```

**2. Task Documentation:**
> Branch names must reference the Jira ticket (COMPT-XX format). Pull from `develop` before opening PR.

Create task file:
**PR targets:**

```
docs/tasks/active/UI-MODULE-123-add-datepicker.md
```
- Feature branches → `develop`
- `develop` → `master` on Friday release only
- Never open a PR directly to `master`

**Task structure:**

Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI - PR Validation

on:
pull_request:
branches: [develop]

permissions:
contents: read

jobs:
validate:
name: CI - PR Validation
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install
run: npm ci

- name: Format (check)
run: npm run format

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Test
run: npm test

- name: Build
run: npm run build
44 changes: 33 additions & 11 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,52 @@ jobs:
with:
fetch-depth: 0

- name: Validate tag exists on this push
- name: Validate version tag and package.json
run: |
TAG=$(git describe --exact-match --tags HEAD 2>/dev/null || echo "")
if [[ -z "$TAG" ]]; then
echo "❌ No tag found on HEAD. This push did not include a version tag."
echo "To publish, merge to master with a tag: git tag v1.0.0 && git push origin master --tags"
PKG_VERSION=$(grep '"version"' package.json | head -1 | sed 's/.*"version": "\([^"]*\)".*/\1/')
TAG="v${PKG_VERSION}"

if [[ -z "$PKG_VERSION" ]]; then
echo "❌ ERROR: Could not read version from package.json"
exit 1
fi

if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "❌ Invalid tag format: $TAG. Expected: v*.*.*"
echo "❌ ERROR: Invalid version format in package.json: '$PKG_VERSION'"
echo "Expected format: x.y.z (e.g., 1.0.0, 0.2.3)"
exit 1
fi

if ! git rev-parse "$TAG" >/dev/null 2>&1; then
echo "❌ ERROR: Tag $TAG not found!"
echo ""
echo "This typically happens when:"
echo " 1. You forgot to run 'npm version patch|minor|major' on your feature branch"
echo " 2. You didn't push the tag: git push origin <feat/your-feature> --tags"
echo " 3. The tag was created locally but never pushed to remote"
echo ""
echo "📋 Correct workflow:"
echo " 1. On feat/** or feature/**: npm version patch (or minor/major)"
echo " 2. Push branch + tag: git push origin feat/your-feature --tags"
echo " 3. PR feat/** → develop, then PR develop → master"
echo " 4. Workflow automatically triggers on master push"
echo ""
exit 1
fi
Comment on lines +23 to 54
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

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

Validate version tag and package.json only checks that the tag v${package.json version} exists somewhere in the repo, not that it points to the current commit being published. This can allow publishing from an untagged commit (or the wrong tag). Verify the tag is an exact match for HEAD (e.g., git describe --exact-match --tags HEAD) and that it equals v${PKG_VERSION}.

Copilot uses AI. Check for mistakes.
echo "✅ Valid tag found: $TAG"

echo "✅ package.json version: $PKG_VERSION"
echo "✅ Tag $TAG exists in repo"
echo "TAG_VERSION=$TAG" >> $GITHUB_ENV

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'
registry-url: 'https://registry.npmjs.org'
cache: npm
cache: 'npm'

- name: Install dependencies
run: npm install
run: npm ci

- name: Build
run: npm run build --if-present
Expand All @@ -55,6 +77,6 @@ jobs:
run: npm test --if-present 2>/dev/null || true

- name: Publish to NPM
run: npm publish --access public --no-git-checks
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Loading
Loading