@ojokesusu/lintasai 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish-npm.yml +40 -0
- package/.github/workflows/validate.yml +93 -0
- package/AUDIT_POST_SETUP_PROMPT_v1.md +280 -0
- package/BOOTSTRAP_PROJECT_DOCS_PROMPT_v1.md +3 -0
- package/CHANGELOG.md +313 -0
- package/CLAUDE_universal_v1.md +1021 -0
- package/CONTRIBUTING.md +101 -0
- package/FIRST_SESSION_PROMPT_v1.md +7 -0
- package/JALANKAN_KIT.md +188 -0
- package/LICENSE +21 -0
- package/MULAI_DI_SINI.md +145 -0
- package/PROJECT_KICKOFF_PROMPT_v1.md +3 -0
- package/PROJECT_LIFECYCLE_PROMPT_v1.md +536 -0
- package/PROJECT_MIGRATION_PROMPT_v1.md +3 -0
- package/README.md +505 -0
- package/SETUP_POLA_B_PROMPT_v1.md +5 -0
- package/SPLIT_REPO_MIGRATION_PROMPT_v1.md +485 -0
- package/TEAM_ROLLOUT_GUIDE_v1.md +172 -0
- package/UPDATE_DOCS_PROMPT_v1.md +3 -0
- package/UPDATE_KIT_PROMPT_v1.md +213 -0
- package/bin/lintasai.js +81 -0
- package/docs/SIGNED_RELEASE.md +162 -0
- package/install-windows.ps1 +225 -0
- package/kit.ps1 +508 -0
- package/lib/agents-md.ps1 +174 -0
- package/lib/git-helpers.ps1 +104 -0
- package/lib/kit-files.psd1 +133 -0
- package/lib/manifest-signing.ps1 +65 -0
- package/lib/manifest.ps1 +267 -0
- package/lib/rollback.ps1 +241 -0
- package/lib/safety.ps1 +193 -0
- package/lib/template-deploy.ps1 +242 -0
- package/lib/version-detect.ps1 +161 -0
- package/package.json +36 -0
- package/setup-pola-b.ps1 +687 -0
- package/templates/ANALOGI_LIBRARY.md +7 -0
- package/templates/CLAUDE_TEAM_GUIDE.md +505 -0
- package/templates/CROSS_REPO_TYPES_PIPELINE.md +473 -0
- package/templates/DB_SCHEMA_SCAN_PROMPT.md +194 -0
- package/templates/DISCORD_BOT_INTEGRATION.md +187 -0
- package/templates/GLOSSARY_NON_PROGRAMMER.md +361 -0
- package/templates/INDEX.md +157 -0
- package/templates/MCP_SETUP.md +1145 -0
- package/templates/MIGRATE_TO_SUBFOLDER_PROMPT_v1.md +220 -0
- package/templates/ONBOARDING.md +172 -0
- package/templates/PROJECT_STARTER_TEMPLATES.md +264 -0
- package/templates/PROMPT_LIBRARY.md +790 -0
- package/templates/RLS_SETUP_PROMPT.md +167 -0
- package/templates/SECURITY_INCIDENT_PLAYBOOK.md +191 -0
- package/templates/SPLIT_REPO_AGENTS_TEMPLATES.md +32 -0
- package/templates/SPLIT_REPO_NON_PROGRAMMER_PROMPTS.md +604 -0
- package/templates/SPLIT_REPO_TOOLS_SETUP.md +388 -0
- package/templates/STACK_DETECTION_PATTERN.md +261 -0
- package/templates/STACK_GUIDE.md +564 -0
- package/templates/STACK_MIGRATION_GUIDE.md +154 -0
- package/templates/STACK_VERSIONS.md +31 -0
- package/templates/UPDATE_GUIDE.md +246 -0
- package/templates/_EXAMPLE.md +110 -0
- package/templates/_PATTERNS.md +173 -0
- package/templates/architecture.md +180 -0
- package/templates/architecture_auto.md +61 -0
- package/templates/decisions/README.md +108 -0
- package/templates/decisions/_TEMPLATE.md +84 -0
- package/templates/feature-flags-advanced.md +171 -0
- package/templates/github/CODEOWNERS.template +61 -0
- package/templates/github/GENERATE_TYPES_SCRIPT.md +77 -0
- package/templates/github/PUBLISH_SHARED_WORKFLOW.yml +52 -0
- package/templates/github/RECEIVE_BACKEND_UPDATE.yml +106 -0
- package/templates/github/RENOVATE_FRONTEND.json +28 -0
- package/templates/github/TRIGGER_FRONTEND_UPDATE.yml +29 -0
- package/templates/github/pull_request_template.md +44 -0
- package/templates/github/scripts/ai-review.js +153 -0
- package/templates/github/workflows/ai-review.yml +61 -0
- package/templates/github/workflows/backup-schemas.yml +169 -0
- package/templates/glossary.md +110 -0
- package/templates/split-agents/BACKEND.md +149 -0
- package/templates/split-agents/FRONTEND.md +141 -0
- package/templates/split-agents/SHARED.md +82 -0
- package/templates/split-agents/TOOLS.md +77 -0
- package/tests/Run-Tests.ps1 +19 -0
- package/tests/lib-safety.Tests.ps1 +66 -0
- package/tests/rollback.Tests.ps1 +66 -0
- package/tests/uninstall.Tests.ps1 +265 -0
- package/tests/update-kit.Tests.ps1 +78 -0
- package/uninstall.ps1 +794 -0
- package/update-kit.ps1 +907 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# TEMPLATE: `<project>-frontend/AGENTS.md`
|
|
2
|
+
|
|
3
|
+
> Template ini di-deploy oleh AI saat split repo migration.
|
|
4
|
+
> Customization: replace `<project>` dengan nama project user.
|
|
5
|
+
|
|
6
|
+
```markdown
|
|
7
|
+
# AGENTS.md - <project>-frontend
|
|
8
|
+
|
|
9
|
+
> Repo ini: Frontend UI only (Next.js SPA mode atau React Vite).
|
|
10
|
+
> Audience AI: Claude Code untuk Frontend Staff (4 orang yang akses repo `<project>-frontend` + `<project>-shared`).
|
|
11
|
+
>
|
|
12
|
+
> Privileges Frontend Staff:
|
|
13
|
+
> - DAPAT edit data (CRUD: SELECT / INSERT / UPDATE / DELETE) di DB lewat API backend
|
|
14
|
+
> - TIDAK punya DDL (CREATE TABLE / ALTER / DROP) — DDL = domain Backend staff + owner
|
|
15
|
+
|
|
16
|
+
## Scope Kamu (AI)
|
|
17
|
+
|
|
18
|
+
Kamu di repo `<project>-frontend`. Kamu BOLEH:
|
|
19
|
+
- Edit UI components, pages, layouts (di `src/app/`, `src/components/`)
|
|
20
|
+
- Call API backend via fetch/axios (CRUD data lewat endpoint yang sudah ada)
|
|
21
|
+
- Import types dari `@<project>/shared` (auto-installed npm dep)
|
|
22
|
+
- Styling (Tailwind, CSS modules)
|
|
23
|
+
- Update copy/teks UI sesuai brief dari staff content
|
|
24
|
+
|
|
25
|
+
Kamu TIDAK BOLEH:
|
|
26
|
+
- Bikin endpoint API (itu di `<project>-backend`, kamu tidak akses)
|
|
27
|
+
- Direct database query (Prisma) — termasuk dilarang DDL apapun
|
|
28
|
+
- Modify `@<project>/shared` (cuma owner / Backend staff yang publish version baru)
|
|
29
|
+
- Implement business logic (validation, calculation, workflow rules)
|
|
30
|
+
- Akses `.env` yang berisi `DATABASE_URL` atau API SECRET
|
|
31
|
+
- Hardcode URL backend; selalu pakai env var
|
|
32
|
+
|
|
33
|
+
## Stack
|
|
34
|
+
- Next.js (lihat STACK_VERSIONS.md untuk versi terbaru) atau React Vite (frontend only)
|
|
35
|
+
- TailwindCSS + Shadcn/ui untuk component primitives
|
|
36
|
+
- TypeScript strict mode
|
|
37
|
+
- React Query / SWR untuk data fetching
|
|
38
|
+
- React Hook Form + Zod (validasi client-side, schema dari `@<project>/shared`)
|
|
39
|
+
|
|
40
|
+
## Workflow
|
|
41
|
+
|
|
42
|
+
Saat staff non-programmer prompt kamu:
|
|
43
|
+
|
|
44
|
+
1. Baca prompt, identify TASK ID (e.g., `TASK-101`)
|
|
45
|
+
2. Kalau prompt vague, tanya 1-2 clarifying question (max 2!)
|
|
46
|
+
3. Cek `@<project>/shared` types yang relevan untuk endpoint yang dipakai
|
|
47
|
+
4. Bikin/edit komponen + page
|
|
48
|
+
5. Call API via fetch ke backend URL (`NEXT_PUBLIC_API_URL` env var)
|
|
49
|
+
6. Test di localhost (`npm run dev`)
|
|
50
|
+
7. Open PR ke `main`, kasih link preview Vercel ke staff
|
|
51
|
+
8. Tunggu approval owner sebelum merge
|
|
52
|
+
|
|
53
|
+
## API URL
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- Local dev: `http://localhost:3001` (backend dev server)
|
|
60
|
+
- Staging: `https://api-staging.<project>.id`
|
|
61
|
+
- Production: `https://api.<project>.id`
|
|
62
|
+
|
|
63
|
+
## Style Guide
|
|
64
|
+
|
|
65
|
+
- Format: Prettier auto, no manual semicolon decisions
|
|
66
|
+
- Naming: `camelCase` untuk function, `PascalCase` untuk Component
|
|
67
|
+
- File: `kebab-case` untuk page route, `PascalCase` untuk component file
|
|
68
|
+
- Komentar: minimal, hanya kalau non-obvious (kenapa, bukan apa)
|
|
69
|
+
- Komponen: prefer functional component + hooks, hindari class component
|
|
70
|
+
- State: lokal pakai `useState`, server state pakai React Query
|
|
71
|
+
|
|
72
|
+
## Tools
|
|
73
|
+
|
|
74
|
+
- Storybook: `npm run storybook` (UI dev tanpa app context)
|
|
75
|
+
- Vitest: `npm test` (unit test komponen)
|
|
76
|
+
- Playwright: `npm run e2e` (end-to-end di staging)
|
|
77
|
+
- Vercel preview: per PR auto-deploy, link auto-comment di PR
|
|
78
|
+
|
|
79
|
+
## Project-Specific Rules
|
|
80
|
+
|
|
81
|
+
<!-- Owner: tambahkan rules spesifik project di sini -->
|
|
82
|
+
<!-- Contoh: design tokens, brand color, accessibility level, dst. -->
|
|
83
|
+
|
|
84
|
+
## Session Start Auto-Check (Anti-Stale Context)
|
|
85
|
+
|
|
86
|
+
Saat AI Claude Code session pertama tiap hari di repo ini, AI WAJIB execute pre-flight check:
|
|
87
|
+
|
|
88
|
+
### Check 1: @<project>/shared version
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# Compare local version vs registry latest
|
|
92
|
+
LOCAL=$(node -p "require('./node_modules/@<project>/shared/package.json').version")
|
|
93
|
+
REMOTE=$(npm view @<project>/shared version --registry=https://npm.pkg.github.com 2>/dev/null || echo "?")
|
|
94
|
+
|
|
95
|
+
if [ "$LOCAL" != "$REMOTE" ]; then
|
|
96
|
+
echo "⚠️ @<project>/shared outdated: local $LOCAL vs remote $REMOTE"
|
|
97
|
+
echo "Run: npm install @<project>/shared@latest"
|
|
98
|
+
fi
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Action kalau outdated:
|
|
102
|
+
- Suggest npm install ke staff
|
|
103
|
+
- Atau auto-run kalau staff approve
|
|
104
|
+
|
|
105
|
+
### Check 2: Backend API spec (Swagger)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
curl -s --max-time 5 https://api-staging.<project>.id/docs/openapi.json > /tmp/api-spec.json 2>/dev/null
|
|
109
|
+
if [ $? -eq 0 ]; then
|
|
110
|
+
ENDPOINTS=$(jq -r '.paths | keys | length' /tmp/api-spec.json 2>/dev/null)
|
|
111
|
+
echo "✅ Swagger spec cached: $ENDPOINTS endpoints available"
|
|
112
|
+
else
|
|
113
|
+
echo "⚠️ Swagger spec unreachable. Frontend AI tidak punya context API terbaru."
|
|
114
|
+
fi
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use spec untuk:
|
|
118
|
+
- Validate API endpoint sebelum suggest fetch
|
|
119
|
+
- Check request/response shape
|
|
120
|
+
- Detect endpoint baru yang belum dipakai
|
|
121
|
+
- Refuse suggest endpoint yang tidak exist (forward escalation ke backend tim)
|
|
122
|
+
|
|
123
|
+
### Check 3: Discord context (optional)
|
|
124
|
+
|
|
125
|
+
Kalau ada Discord MCP atau webhook reader configured:
|
|
126
|
+
- Scan #akses-deps channel untuk last 24h
|
|
127
|
+
- Look for backend publish notifications
|
|
128
|
+
- Highlight relevant updates ke staff
|
|
129
|
+
|
|
130
|
+
### Logging
|
|
131
|
+
|
|
132
|
+
Output check result di awal response sesi:
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
[Session pre-flight]
|
|
136
|
+
- @<project>/shared: v1.2.0 ✅ (latest)
|
|
137
|
+
- Swagger API: 47 endpoints cached ✅
|
|
138
|
+
- Last backend publish: 2 hours ago (via Discord)
|
|
139
|
+
Ready untuk task.
|
|
140
|
+
```
|
|
141
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# TEMPLATE: `<project>-shared/AGENTS.md`
|
|
2
|
+
|
|
3
|
+
> Template ini di-deploy oleh AI saat split repo migration.
|
|
4
|
+
> Customization: replace `<project>` dengan nama project user.
|
|
5
|
+
|
|
6
|
+
```markdown
|
|
7
|
+
# AGENTS.md - <project>-shared
|
|
8
|
+
|
|
9
|
+
> Repo ini: TypeScript types + Zod schemas saja. No logic.
|
|
10
|
+
> Audience AI: Owner + backend delegate (saat publish version baru).
|
|
11
|
+
> Staff frontend = READ-ONLY (mereka cuma install sebagai npm dep).
|
|
12
|
+
|
|
13
|
+
## Scope Kamu (AI)
|
|
14
|
+
|
|
15
|
+
Kamu di repo `<project>-shared`. Kamu BOLEH:
|
|
16
|
+
- Tambah/update TypeScript type definitions
|
|
17
|
+
- Tambah/update Zod validation schemas
|
|
18
|
+
- Update version di `package.json` saat ada change
|
|
19
|
+
- Update `README.md` dengan example usage
|
|
20
|
+
|
|
21
|
+
Kamu TIDAK BOLEH:
|
|
22
|
+
- Implement actual logic (cuma type signatures + schemas)
|
|
23
|
+
- Import package eksternal selain Zod + TypeScript native
|
|
24
|
+
- Side effect (no fetch, no DB, no file I/O, no env access)
|
|
25
|
+
- Bikin React component (itu domain frontend)
|
|
26
|
+
- Bikin Prisma model (itu domain backend)
|
|
27
|
+
|
|
28
|
+
## Stack
|
|
29
|
+
- TypeScript strict mode
|
|
30
|
+
- Zod untuk runtime validation
|
|
31
|
+
- `tsup` atau `rollup` untuk build (output dual ESM + CJS)
|
|
32
|
+
- npm publish atau GitHub Packages (private)
|
|
33
|
+
|
|
34
|
+
## Workflow Update
|
|
35
|
+
|
|
36
|
+
Saat update:
|
|
37
|
+
|
|
38
|
+
1. Edit `src/types/` atau `src/schemas/`
|
|
39
|
+
2. Run `npm run build` -> `dist/` ter-generate
|
|
40
|
+
3. Run `npm test` -> pastikan schema parse contoh data dengan benar
|
|
41
|
+
4. Bump version di `package.json` (semantic versioning, lihat rules bawah)
|
|
42
|
+
5. Tag git: `git tag v1.X.Y`
|
|
43
|
+
6. Push tag -> GitHub Actions auto-publish ke npm/GitHub Packages
|
|
44
|
+
7. Notify backend + frontend tim: `@<project>/shared v1.X.Y published`
|
|
45
|
+
8. Backend + frontend update dep version di `package.json` mereka
|
|
46
|
+
|
|
47
|
+
## Versioning Rules (Semver)
|
|
48
|
+
|
|
49
|
+
- `patch` (1.0.X): bug fix di type definition, no breaking, no rename
|
|
50
|
+
- `minor` (1.X.0): tambah type baru, backward compatible (existing field tetap)
|
|
51
|
+
- `major` (X.0.0): breaking change (rename field, hapus type, ubah enum)
|
|
52
|
+
- HARUS coordinate dengan tim sebelum publish
|
|
53
|
+
- HARUS ada migration note di `CHANGELOG.md`
|
|
54
|
+
|
|
55
|
+
## Output Structure
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
dist/
|
|
59
|
+
index.js <- compiled JS (CJS)
|
|
60
|
+
index.mjs <- compiled JS (ESM)
|
|
61
|
+
index.d.ts <- TypeScript definition (yang frontend AI baca)
|
|
62
|
+
schemas/
|
|
63
|
+
user.js
|
|
64
|
+
user.d.ts
|
|
65
|
+
types/
|
|
66
|
+
api.d.ts
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Folder Convention
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
src/
|
|
73
|
+
index.ts <- barrel export semua type & schema
|
|
74
|
+
types/ <- pure TypeScript type (interface, type alias)
|
|
75
|
+
schemas/ <- Zod schema (runtime validatable)
|
|
76
|
+
utils/ <- type guard, helper type (no runtime logic)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Project-Specific Rules
|
|
80
|
+
|
|
81
|
+
<!-- Owner: tambahkan rules spesifik project di sini -->
|
|
82
|
+
```
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
> ⚠️ OPT-IN: File ini hanya dipakai kalau team >20 staff, compliance audit, ATAU kalau pakai 4-repo split (Backend Staff men-jalankan admin scripts dari repo terpisah). Untuk team kecil-sedang dengan 3-repo split default, admin scripts cukup di `<project>-backend/scripts/` (Backend Staff + owner access).
|
|
2
|
+
>
|
|
3
|
+
> Audience: owner + Backend Staff (2 orang) saat 4-repo split aktif. Frontend Staff TIDAK punya akses.
|
|
4
|
+
|
|
5
|
+
# TEMPLATE: `<project>-tools/AGENTS.md`
|
|
6
|
+
|
|
7
|
+
> Template ini di-deploy oleh AI saat split repo migration.
|
|
8
|
+
> Customization: replace `<project>` dengan nama project user.
|
|
9
|
+
|
|
10
|
+
```markdown
|
|
11
|
+
# AGENTS.md - <project>-tools
|
|
12
|
+
|
|
13
|
+
> Repo ini: Admin scripts, sync tools, migration helpers.
|
|
14
|
+
> Audience AI: Owner ONLY.
|
|
15
|
+
> Staff TIDAK punya akses (repo private, owner-only access).
|
|
16
|
+
|
|
17
|
+
## Scope Kamu (AI)
|
|
18
|
+
|
|
19
|
+
Kamu di repo `<project>-tools`. Kamu BOLEH:
|
|
20
|
+
- Bikin script CLI untuk admin tasks
|
|
21
|
+
- Bikin sync script (prod -> staging dengan PII redact)
|
|
22
|
+
- Bikin migration helpers (wrapper di atas `prisma migrate`)
|
|
23
|
+
- Bikin one-time scripts (backfill data, cleanup, dst.)
|
|
24
|
+
- Akses credential owner via `scripts/.env.owner`
|
|
25
|
+
- Akses Supabase MCP untuk owner-level ops
|
|
26
|
+
|
|
27
|
+
Kamu TIDAK BOLEH:
|
|
28
|
+
- Commit credential ke git (cek `.gitignore` sebelum stage)
|
|
29
|
+
- Jalankan script destructive ke prod tanpa konfirmasi owner eksplisit
|
|
30
|
+
- Print password / secret ke stdout / log file
|
|
31
|
+
- Share script ini ke repo lain (scope owner-only)
|
|
32
|
+
|
|
33
|
+
## Stack
|
|
34
|
+
- Node.js (ESM, `"type": "module"` di `package.json`)
|
|
35
|
+
- `pg` untuk direct DB access (saat butuh raw SQL di luar Prisma)
|
|
36
|
+
- `@faker-js/faker` untuk generate demo data
|
|
37
|
+
- `dotenv-cli` untuk env management
|
|
38
|
+
- `commander` atau `yargs` untuk CLI argument parsing
|
|
39
|
+
|
|
40
|
+
## Scripts Library
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
scripts/
|
|
44
|
+
sync-prod-to-staging.mjs <- PII redacted sample sync
|
|
45
|
+
backup-prod.mjs <- Snapshot via pg_dump
|
|
46
|
+
migrate-deploy-prod.mjs <- Wrapped `prisma migrate deploy`
|
|
47
|
+
audit-staff-access.mjs <- Check Supabase user activity log
|
|
48
|
+
rotate-secrets.mjs <- Helper rotate API key + push ke Vercel
|
|
49
|
+
cleanup-orphan-records.mjs <- One-time cleanup utility
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Security
|
|
53
|
+
|
|
54
|
+
- Owner credential CUMA di laptop owner (jangan upload ke server shared)
|
|
55
|
+
- `scripts/.env.owner` WAJIB gitignored, JANGAN commit
|
|
56
|
+
- Output log: mask password di `DATABASE_URL` (regex replace sebelum print)
|
|
57
|
+
- Audit log: tiap script run, append ke `logs/owner-actions.log`
|
|
58
|
+
- Format: `<ISO timestamp> | <script name> | <args summary> | <exit code>`
|
|
59
|
+
- Saat clone repo di laptop baru, fetch `.env.owner` dari password manager (1Password / Bitwarden)
|
|
60
|
+
|
|
61
|
+
## Workflow
|
|
62
|
+
|
|
63
|
+
Saat owner prompt untuk bikin script baru:
|
|
64
|
+
|
|
65
|
+
1. Identifikasi: ini one-time atau reusable?
|
|
66
|
+
2. Kalau one-time: simpan di `scripts/oneoffs/<date>-<desc>.mjs`
|
|
67
|
+
3. Kalau reusable: simpan di `scripts/` dengan `--help` flag + `README.md` entry
|
|
68
|
+
4. WAJIB confirm step sebelum eksekusi destructive op:
|
|
69
|
+
```js
|
|
70
|
+
await confirm('Are you sure? This will delete X rows. [y/N]')
|
|
71
|
+
```
|
|
72
|
+
5. WAJIB dry-run mode (`--dry-run` flag) untuk script yang ubah data
|
|
73
|
+
|
|
74
|
+
## Project-Specific Rules
|
|
75
|
+
|
|
76
|
+
<!-- Owner: tambahkan rules spesifik project di sini -->
|
|
77
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
$ErrorActionPreference = 'Stop'
|
|
4
|
+
|
|
5
|
+
# Install Pester kalau belum
|
|
6
|
+
if (-not (Get-Module -ListAvailable -Name Pester | Where-Object { $_.Version.Major -ge 5 })) {
|
|
7
|
+
Write-Host "Installing Pester 5.x..."
|
|
8
|
+
Install-Module -Name Pester -MinimumVersion 5.0 -Force -SkipPublisherCheck -Scope CurrentUser
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
Import-Module Pester -MinimumVersion 5.0
|
|
12
|
+
|
|
13
|
+
$testsDir = $PSScriptRoot
|
|
14
|
+
$config = New-PesterConfiguration
|
|
15
|
+
$config.Run.Path = $testsDir
|
|
16
|
+
$config.Output.Verbosity = 'Detailed'
|
|
17
|
+
$config.Run.Exit = $true
|
|
18
|
+
|
|
19
|
+
Invoke-Pester -Configuration $config
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
BeforeAll {
|
|
4
|
+
$libPath = Join-Path $PSScriptRoot '..\lib\safety.ps1'
|
|
5
|
+
. $libPath
|
|
6
|
+
Initialize-SafeProjectPath -ProjectRoot 'C:\test-project'
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
Describe "Resolve-SafeProjectPath" {
|
|
10
|
+
It "Should resolve valid relative path" {
|
|
11
|
+
$result = Resolve-SafeProjectPath -RelativePath 'src/app.ts'
|
|
12
|
+
$result | Should -Not -BeNullOrEmpty
|
|
13
|
+
$result | Should -Match 'C:\\test-project'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
It "Should REJECT absolute path outside project" {
|
|
17
|
+
{ Resolve-SafeProjectPath -RelativePath 'C:\Windows\system32' } | Should -Throw
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
It "Should REJECT path traversal via ..\" {
|
|
21
|
+
{ Resolve-SafeProjectPath -RelativePath '..\..\evil.txt' } | Should -Throw
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
It "Should REJECT prefix collision (proj vs proj-evil)" {
|
|
25
|
+
Initialize-SafeProjectPath -ProjectRoot 'C:\proj'
|
|
26
|
+
{ Resolve-SafeProjectPath -RelativePath '..\proj-evil\file.txt' } | Should -Throw
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
It "Should handle nested valid path" {
|
|
30
|
+
# Re-init: test sebelumnya (prefix collision) ganti root ke C:\proj — reset balik.
|
|
31
|
+
Initialize-SafeProjectPath -ProjectRoot 'C:\test-project'
|
|
32
|
+
$result = Resolve-SafeProjectPath -RelativePath 'src/lib/utils/format.ts'
|
|
33
|
+
$result | Should -Match 'C:\\test-project\\src\\lib\\utils'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
Describe "Test-PathHasReparsePoint" {
|
|
38
|
+
It "Returns false for regular file" {
|
|
39
|
+
$tempFile = Join-Path $env:TEMP "test-regular-$(Get-Random).txt"
|
|
40
|
+
Set-Content -Path $tempFile -Value "test" -Force
|
|
41
|
+
try {
|
|
42
|
+
Test-PathHasReparsePoint -FullPath $tempFile | Should -BeFalse
|
|
43
|
+
} finally {
|
|
44
|
+
Remove-Item -Force $tempFile -ErrorAction SilentlyContinue
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
It "Handles non-existent path gracefully" {
|
|
49
|
+
Test-PathHasReparsePoint -FullPath "C:\does-not-exist-$(Get-Random)" | Should -BeFalse
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Describe "Get-FileSha256" {
|
|
54
|
+
It "Computes correct sha256" {
|
|
55
|
+
$tempFile = Join-Path $env:TEMP "test-sha-$(Get-Random).txt"
|
|
56
|
+
# Pakai WriteAllBytes raw supaya tidak ada BOM (PS 5.1 Set-Content -Encoding UTF8 nulis BOM).
|
|
57
|
+
# Expected hash "2cf24dba..." = SHA-256 dari ASCII "hello" persis (5 byte).
|
|
58
|
+
[System.IO.File]::WriteAllBytes($tempFile, [System.Text.Encoding]::ASCII.GetBytes("hello"))
|
|
59
|
+
try {
|
|
60
|
+
$hash = Get-FileSha256 -FilePath $tempFile
|
|
61
|
+
$hash | Should -Be "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
|
|
62
|
+
} finally {
|
|
63
|
+
Remove-Item -Force $tempFile -ErrorAction SilentlyContinue
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
BeforeAll {
|
|
4
|
+
$libPath = Join-Path $PSScriptRoot '..\lib\rollback.ps1'
|
|
5
|
+
. $libPath
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
Describe "rollback.ps1 graceful no-op" {
|
|
9
|
+
It "Invoke-Rollback function exists" {
|
|
10
|
+
Get-Command Invoke-Rollback -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
It "Get-RollbackPreview function exists" {
|
|
14
|
+
Get-Command Get-RollbackPreview -ErrorAction SilentlyContinue | Should -Not -BeNullOrEmpty
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
It "Returns no-manifest status when manifest tidak ada" {
|
|
18
|
+
$tempRoot = Join-Path $env:TEMP "rollback-test-nomanifest-$(Get-Random)"
|
|
19
|
+
New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
|
|
20
|
+
try {
|
|
21
|
+
$result = Invoke-Rollback -ProjectRoot $tempRoot -Force
|
|
22
|
+
$result.status | Should -Be 'no-manifest'
|
|
23
|
+
$result.restored | Should -Be 0
|
|
24
|
+
$result.skipped | Should -Be 0
|
|
25
|
+
} finally {
|
|
26
|
+
Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
It "Should NOT throw saat no-manifest" {
|
|
31
|
+
$tempRoot = Join-Path $env:TEMP "rollback-test-nothrow-$(Get-Random)"
|
|
32
|
+
New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
|
|
33
|
+
try {
|
|
34
|
+
{ Invoke-Rollback -ProjectRoot $tempRoot -Force } | Should -Not -Throw
|
|
35
|
+
} finally {
|
|
36
|
+
Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Describe "Find-ManifestPath multi-location probe" {
|
|
42
|
+
It "Returns null when no manifest" {
|
|
43
|
+
$tempRoot = Join-Path $env:TEMP "find-manifest-null-$(Get-Random)"
|
|
44
|
+
New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
|
|
45
|
+
try {
|
|
46
|
+
$result = Find-ManifestPath -ProjectRoot $tempRoot
|
|
47
|
+
$result | Should -BeNullOrEmpty
|
|
48
|
+
} finally {
|
|
49
|
+
Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
It "Finds manifest at project root" {
|
|
54
|
+
$tempRoot = Join-Path $env:TEMP "find-manifest-root-$(Get-Random)"
|
|
55
|
+
New-Item -ItemType Directory -Path $tempRoot -Force | Out-Null
|
|
56
|
+
try {
|
|
57
|
+
$manifestPath = Join-Path $tempRoot '.install-manifest.json'
|
|
58
|
+
Set-Content -Path $manifestPath -Value '{"files":[]}' -Encoding UTF8 -Force
|
|
59
|
+
$result = Find-ManifestPath -ProjectRoot $tempRoot
|
|
60
|
+
$result | Should -Not -BeNullOrEmpty
|
|
61
|
+
$result | Should -Be $manifestPath
|
|
62
|
+
} finally {
|
|
63
|
+
Remove-Item -Recurse -Force $tempRoot -ErrorAction SilentlyContinue
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|