@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,265 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
<#
|
|
4
|
+
.SYNOPSIS
|
|
5
|
+
Pester 5+ tests untuk uninstall.ps1 (manifest-based safe uninstall).
|
|
6
|
+
|
|
7
|
+
.DESCRIPTION
|
|
8
|
+
Tiga area uji:
|
|
9
|
+
1) DryRun mode - fresh install harus exit 0, classification PRISTINE/MODIFIED/MISSING
|
|
10
|
+
terlihat, AGENTS.md di-skip secara default.
|
|
11
|
+
2) Manifest gone - project tanpa manifest harus warn / abort dengan exit non-zero,
|
|
12
|
+
TIDAK menghapus apa pun.
|
|
13
|
+
3) Param surface - -AllowModified harus exist sebagai parameter (AST static check).
|
|
14
|
+
|
|
15
|
+
Strategi:
|
|
16
|
+
- Build fake project root di $env:TEMP dengan struktur:
|
|
17
|
+
<fakeRoot>\
|
|
18
|
+
AGENTS.md (akan masuk manifest, default skipped uninstall)
|
|
19
|
+
docs\README.md (PRISTINE - hash match)
|
|
20
|
+
docs\edited.md (MODIFIED - hash mismatch)
|
|
21
|
+
docs\gone.md (MISSING - di-list manifest tapi sudah dihapus)
|
|
22
|
+
.claude-kit\
|
|
23
|
+
uninstall.ps1 (copy dari kit asli)
|
|
24
|
+
lib\safety.ps1, manifest-signing.ps1 (dependencies)
|
|
25
|
+
.install-manifest.json (di-craft + HMAC-signed di BeforeAll)
|
|
26
|
+
|
|
27
|
+
- Jalankan uninstall.ps1 -DryRun -Yes dari fake project, capture stdout + exit code.
|
|
28
|
+
- Assert keyword di output + exit code.
|
|
29
|
+
|
|
30
|
+
Tests TIDAK menjalankan delete sungguhan (selalu -DryRun) untuk safety.
|
|
31
|
+
#>
|
|
32
|
+
|
|
33
|
+
BeforeAll {
|
|
34
|
+
# ---- Resolve repo root (kit folder yang berisi uninstall.ps1) ----
|
|
35
|
+
$script:KitRepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') | Select-Object -ExpandProperty Path
|
|
36
|
+
$script:UninstallScript = Join-Path $script:KitRepoRoot 'uninstall.ps1'
|
|
37
|
+
$script:SigningLib = Join-Path $script:KitRepoRoot 'lib\manifest-signing.ps1'
|
|
38
|
+
$script:SafetyLib = Join-Path $script:KitRepoRoot 'lib\safety.ps1'
|
|
39
|
+
|
|
40
|
+
if (-not (Test-Path $script:UninstallScript)) {
|
|
41
|
+
throw "uninstall.ps1 not found at $script:UninstallScript - tests assume layout tests/ sibling to uninstall.ps1"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# ---- Helper: build fake project (root + .claude-kit + files) ----
|
|
45
|
+
function script:New-FakeProject {
|
|
46
|
+
param(
|
|
47
|
+
[Parameter(Mandatory)][string]$Root,
|
|
48
|
+
[switch]$WithManifest = $true,
|
|
49
|
+
[switch]$IncludeAgents = $true,
|
|
50
|
+
[switch]$IncludeMissingEntry,
|
|
51
|
+
[switch]$IncludeModifiedEntry,
|
|
52
|
+
[string]$KitVersion = '1.0.0'
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Bersihkan kalau ada sisa run sebelumnya
|
|
56
|
+
if (Test-Path -LiteralPath $Root) {
|
|
57
|
+
Remove-Item -Recurse -Force -LiteralPath $Root -ErrorAction SilentlyContinue
|
|
58
|
+
}
|
|
59
|
+
$null = New-Item -ItemType Directory -Path $Root -Force
|
|
60
|
+
$kitDir = Join-Path $Root '.claude-kit'
|
|
61
|
+
$kitLib = Join-Path $kitDir 'lib'
|
|
62
|
+
$null = New-Item -ItemType Directory -Path $kitDir -Force
|
|
63
|
+
$null = New-Item -ItemType Directory -Path $kitLib -Force
|
|
64
|
+
|
|
65
|
+
# Copy script + lib supaya dot-source di uninstall.ps1 bisa nemu file-nya
|
|
66
|
+
Copy-Item -LiteralPath $script:UninstallScript -Destination (Join-Path $kitDir 'uninstall.ps1') -Force
|
|
67
|
+
Copy-Item -LiteralPath $script:SafetyLib -Destination (Join-Path $kitLib 'safety.ps1') -Force
|
|
68
|
+
Copy-Item -LiteralPath $script:SigningLib -Destination (Join-Path $kitLib 'manifest-signing.ps1') -Force
|
|
69
|
+
|
|
70
|
+
# ---- Tulis file project yang akan ditrack manifest ----
|
|
71
|
+
$docsDir = Join-Path $Root 'docs'
|
|
72
|
+
$null = New-Item -ItemType Directory -Path $docsDir -Force
|
|
73
|
+
|
|
74
|
+
# PRISTINE candidate
|
|
75
|
+
$pristineRel = 'docs/README.md'
|
|
76
|
+
$pristineFull = Join-Path $Root 'docs\README.md'
|
|
77
|
+
$pristineContent = "# Pristine test`nhash should match`n"
|
|
78
|
+
[System.IO.File]::WriteAllText($pristineFull, $pristineContent, (New-Object System.Text.UTF8Encoding $false))
|
|
79
|
+
$pristineHash = (Get-FileHash -LiteralPath $pristineFull -Algorithm SHA256).Hash
|
|
80
|
+
|
|
81
|
+
$files = @()
|
|
82
|
+
$files += [ordered]@{ path = $pristineRel; kind = 'file'; sha256 = $pristineHash }
|
|
83
|
+
|
|
84
|
+
# AGENTS.md (default skipped during uninstall)
|
|
85
|
+
if ($IncludeAgents) {
|
|
86
|
+
$agentsFull = Join-Path $Root 'AGENTS.md'
|
|
87
|
+
$agentsContent = "# AGENTS`n"
|
|
88
|
+
[System.IO.File]::WriteAllText($agentsFull, $agentsContent, (New-Object System.Text.UTF8Encoding $false))
|
|
89
|
+
$agentsHash = (Get-FileHash -LiteralPath $agentsFull -Algorithm SHA256).Hash
|
|
90
|
+
$files += [ordered]@{ path = 'AGENTS.md'; kind = 'file'; sha256 = $agentsHash }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# MODIFIED candidate (hash mismatch — file di-edit setelah "install")
|
|
94
|
+
if ($IncludeModifiedEntry) {
|
|
95
|
+
$modFull = Join-Path $Root 'docs\edited.md'
|
|
96
|
+
[System.IO.File]::WriteAllText($modFull, "ORIGINAL", (New-Object System.Text.UTF8Encoding $false))
|
|
97
|
+
$origHash = (Get-FileHash -LiteralPath $modFull -Algorithm SHA256).Hash
|
|
98
|
+
# Sekarang ubah isi -> hash on disk beda dari yang tercatat di manifest
|
|
99
|
+
[System.IO.File]::WriteAllText($modFull, "EDITED BY USER", (New-Object System.Text.UTF8Encoding $false))
|
|
100
|
+
$files += [ordered]@{ path = 'docs/edited.md'; kind = 'file'; sha256 = $origHash }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# MISSING candidate (entry ada di manifest tapi file tidak pernah di-create di disk)
|
|
104
|
+
if ($IncludeMissingEntry) {
|
|
105
|
+
$files += [ordered]@{
|
|
106
|
+
path = 'docs/gone.md'
|
|
107
|
+
kind = 'file'
|
|
108
|
+
sha256 = 'a' * 64
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
$dirsTracked = @('docs')
|
|
113
|
+
|
|
114
|
+
if ($WithManifest) {
|
|
115
|
+
$manifest = [ordered]@{
|
|
116
|
+
schema_version = 1
|
|
117
|
+
kit_version = $KitVersion
|
|
118
|
+
installed_at = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ss')
|
|
119
|
+
installed_by = '<USER>'
|
|
120
|
+
project_name = (Split-Path -Leaf $Root)
|
|
121
|
+
project_root = '<PROJECT_ROOT>' # uninstall.ps1 treats this as "match anything"
|
|
122
|
+
metadata = [ordered]@{
|
|
123
|
+
kit_version = $KitVersion
|
|
124
|
+
installed_at = (Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')
|
|
125
|
+
installer = 'setup-pola-b.ps1'
|
|
126
|
+
}
|
|
127
|
+
files = $files
|
|
128
|
+
directories_created = $dirsTracked
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# HMAC sign manifest (dot-source signing lib sekali di scope helper)
|
|
132
|
+
. $script:SigningLib
|
|
133
|
+
try {
|
|
134
|
+
$sig = New-ManifestSignature -Manifest $manifest -KitVersion $KitVersion
|
|
135
|
+
$manifest.metadata.signature = $sig
|
|
136
|
+
} catch {
|
|
137
|
+
# Kalau signing gagal, tetap tulis (uninstall.ps1 akan treat sebagai unsigned,
|
|
138
|
+
# tests pakai -Yes untuk bypass prompt unsigned).
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
$json = $manifest | ConvertTo-Json -Depth 10
|
|
142
|
+
$manifestPath = Join-Path $kitDir '.install-manifest.json'
|
|
143
|
+
[System.IO.File]::WriteAllText($manifestPath, $json, (New-Object System.Text.UTF8Encoding $false))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return [pscustomobject]@{
|
|
147
|
+
Root = $Root
|
|
148
|
+
KitDir = $kitDir
|
|
149
|
+
Uninstall = (Join-Path $kitDir 'uninstall.ps1')
|
|
150
|
+
Manifest = (Join-Path $kitDir '.install-manifest.json')
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# ---- Helper: run uninstall.ps1 in fresh powershell, capture stdout + exit ----
|
|
155
|
+
function script:Invoke-Uninstall {
|
|
156
|
+
param(
|
|
157
|
+
[Parameter(Mandatory)][string]$ScriptPath,
|
|
158
|
+
[string[]]$Args = @()
|
|
159
|
+
)
|
|
160
|
+
$pwshExe = (Get-Process -Id $PID).Path
|
|
161
|
+
if (-not $pwshExe) { $pwshExe = 'powershell.exe' }
|
|
162
|
+
$allArgs = @('-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', $ScriptPath) + $Args
|
|
163
|
+
# Capture stdout+stderr ke string, jangan throw walau exit != 0
|
|
164
|
+
$output = & $pwshExe @allArgs 2>&1 | Out-String
|
|
165
|
+
return [pscustomobject]@{
|
|
166
|
+
Output = $output
|
|
167
|
+
ExitCode = $LASTEXITCODE
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
Describe "uninstall.ps1 DryRun mode" {
|
|
173
|
+
BeforeAll {
|
|
174
|
+
$script:FakeRoot1 = Join-Path $env:TEMP ("lintasAI-uninstall-test-dryrun-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
175
|
+
$script:Fake1 = script:New-FakeProject -Root $script:FakeRoot1 -WithManifest -IncludeAgents `
|
|
176
|
+
-IncludeMissingEntry -IncludeModifiedEntry
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
AfterAll {
|
|
180
|
+
if (Test-Path -LiteralPath $script:FakeRoot1) {
|
|
181
|
+
Remove-Item -Recurse -Force -LiteralPath $script:FakeRoot1 -ErrorAction SilentlyContinue
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
It "Exit 0 untuk fresh install" {
|
|
186
|
+
$result = script:Invoke-Uninstall -ScriptPath $script:Fake1.Uninstall -Args @('-DryRun', '-Yes')
|
|
187
|
+
$result.ExitCode | Should -Be 0
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
It "Classification per file shown" {
|
|
191
|
+
$result = script:Invoke-Uninstall -ScriptPath $script:Fake1.Uninstall -Args @('-DryRun', '-Yes')
|
|
192
|
+
# Output harus berisi setidaknya satu dari label classification.
|
|
193
|
+
# Test PRISTINE (file hash-match) + MODIFIED (kita craft) + MISSING (entry tanpa file).
|
|
194
|
+
$result.Output | Should -Match '\[PRISTINE\]'
|
|
195
|
+
$result.Output | Should -Match '\[MODIFIED\]'
|
|
196
|
+
$result.Output | Should -Match '\[MISSING\]'
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
It "AGENTS.md skipped by default" {
|
|
200
|
+
$result = script:Invoke-Uninstall -ScriptPath $script:Fake1.Uninstall -Args @('-DryRun', '-Yes')
|
|
201
|
+
# uninstall.ps1 string: "AGENTS.md skipped (pakai -DeleteAgents untuk override)"
|
|
202
|
+
# Bisa muncul di section [SKIPPED] atau di REASSURANCE FIRST block.
|
|
203
|
+
$result.Output | Should -Match 'AGENTS\.md'
|
|
204
|
+
$result.Output | Should -Match '(?i)(skip|skipped|TETAP ADA|DeleteAgents)'
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Describe "uninstall.ps1 manifest verification" {
|
|
209
|
+
BeforeAll {
|
|
210
|
+
$script:FakeRoot2 = Join-Path $env:TEMP ("lintasAI-uninstall-test-nomanifest-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
211
|
+
# Build project structure TANPA manifest -- uninstall harus abort.
|
|
212
|
+
$script:Fake2 = script:New-FakeProject -Root $script:FakeRoot2 -WithManifest:$false `
|
|
213
|
+
-IncludeAgents
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
AfterAll {
|
|
217
|
+
if (Test-Path -LiteralPath $script:FakeRoot2) {
|
|
218
|
+
Remove-Item -Recurse -Force -LiteralPath $script:FakeRoot2 -ErrorAction SilentlyContinue
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
It "Should handle no-manifest gracefully" {
|
|
223
|
+
# uninstall.ps1 dengan manifest gone: print "STOP: Tidak bisa lanjut karena file pencatat install hilang."
|
|
224
|
+
# lalu exit 1. Test: exit code non-zero + output mengandung STOP / hilang / manifest.
|
|
225
|
+
$result = script:Invoke-Uninstall -ScriptPath $script:Fake2.Uninstall -Args @('-DryRun', '-Yes')
|
|
226
|
+
$result.ExitCode | Should -Not -Be 0
|
|
227
|
+
$result.Output | Should -Match '(?i)(STOP|manifest|catatan|hilang|abort)'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
Describe "uninstall.ps1 -AllowModified flag" {
|
|
232
|
+
It "Should accept -AllowModified parameter" {
|
|
233
|
+
# Verifikasi via AST: parse script + cari ParameterAst dengan nama 'AllowModified'.
|
|
234
|
+
# Tidak panggil script (cukup static check) supaya test cepat & deterministic.
|
|
235
|
+
$tokens = $null
|
|
236
|
+
$errors = $null
|
|
237
|
+
$ast = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
238
|
+
$script:UninstallScript, [ref]$tokens, [ref]$errors
|
|
239
|
+
)
|
|
240
|
+
$errors | Should -BeNullOrEmpty -Because 'uninstall.ps1 must parse cleanly'
|
|
241
|
+
|
|
242
|
+
$params = $ast.FindAll({
|
|
243
|
+
param($node)
|
|
244
|
+
$node -is [System.Management.Automation.Language.ParameterAst]
|
|
245
|
+
}, $true)
|
|
246
|
+
$paramNames = $params | ForEach-Object { $_.Name.VariablePath.UserPath }
|
|
247
|
+
$paramNames | Should -Contain 'AllowModified'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
It "Should not break -DryRun when -AllowModified is set" {
|
|
251
|
+
# Sanity: jalankan dengan -AllowModified -DryRun, exit 0 (preview saja).
|
|
252
|
+
$rootMod = Join-Path $env:TEMP ("lintasAI-uninstall-test-allowmod-{0}" -f ([guid]::NewGuid().ToString('N').Substring(0,8)))
|
|
253
|
+
try {
|
|
254
|
+
$fakeMod = script:New-FakeProject -Root $rootMod -WithManifest -IncludeAgents -IncludeModifiedEntry
|
|
255
|
+
$result = script:Invoke-Uninstall -ScriptPath $fakeMod.Uninstall `
|
|
256
|
+
-Args @('-DryRun', '-Yes', '-AllowModified')
|
|
257
|
+
$result.ExitCode | Should -Be 0
|
|
258
|
+
$result.Output | Should -Match '(?i)AllowModified'
|
|
259
|
+
} finally {
|
|
260
|
+
if (Test-Path -LiteralPath $rootMod) {
|
|
261
|
+
Remove-Item -Recurse -Force -LiteralPath $rootMod -ErrorAction SilentlyContinue
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#Requires -Module Pester
|
|
2
|
+
|
|
3
|
+
BeforeAll {
|
|
4
|
+
$script:scriptPath = Join-Path $PSScriptRoot '..\update-kit.ps1'
|
|
5
|
+
if (-not (Test-Path $script:scriptPath)) {
|
|
6
|
+
throw "update-kit.ps1 tidak ditemukan di $script:scriptPath"
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
# Baca raw content sekali untuk pattern checks.
|
|
10
|
+
$script:scriptContent = Get-Content -Path $script:scriptPath -Raw -Encoding UTF8
|
|
11
|
+
|
|
12
|
+
# Parse AST sekali untuk param-block introspection.
|
|
13
|
+
$tokens = $null
|
|
14
|
+
$errors = $null
|
|
15
|
+
$script:scriptAst = [System.Management.Automation.Language.Parser]::ParseFile(
|
|
16
|
+
$script:scriptPath, [ref]$tokens, [ref]$errors
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Ambil top-level param block (kalau ada).
|
|
20
|
+
$script:paramBlock = $script:scriptAst.ParamBlock
|
|
21
|
+
if (-not $script:paramBlock) {
|
|
22
|
+
# Fallback: cari ParamBlockAst di seluruh AST (script bisa pakai function-level param).
|
|
23
|
+
$script:paramBlock = $script:scriptAst.Find({
|
|
24
|
+
param($node) $node -is [System.Management.Automation.Language.ParamBlockAst]
|
|
25
|
+
}, $true)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Helper: cek param name ada di paramBlock.
|
|
29
|
+
function script:Test-ParamExists {
|
|
30
|
+
param([string]$Name)
|
|
31
|
+
if (-not $script:paramBlock) { return $false }
|
|
32
|
+
foreach ($p in $script:paramBlock.Parameters) {
|
|
33
|
+
if ($p.Name.VariablePath.UserPath -ieq $Name) { return $true }
|
|
34
|
+
}
|
|
35
|
+
return $false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Describe "update-kit.ps1 parameter validation" {
|
|
40
|
+
It "Should have -AllowUnsignedTag parameter (via AST)" {
|
|
41
|
+
Test-ParamExists -Name 'AllowUnsignedTag' | Should -BeTrue
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
It "Should have -AllowUntrustedRepo parameter (via AST)" {
|
|
45
|
+
Test-ParamExists -Name 'AllowUntrustedRepo' | Should -BeTrue
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Describe "GPG verify-tag logic" {
|
|
50
|
+
It "Should reference 'describe --exact-match' atau 'ls-remote.*tags' untuk resolve tag name" {
|
|
51
|
+
$hasDescribe = $script:scriptContent -match 'describe --exact-match'
|
|
52
|
+
$hasLsRemote = $script:scriptContent -match 'ls-remote.*tags'
|
|
53
|
+
($hasDescribe -or $hasLsRemote) | Should -BeTrue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
It "Should call 'verify-tag' (bukan verify-commit)" {
|
|
57
|
+
($script:scriptContent -match 'verify-tag') | Should -BeTrue
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
It "Should NOT use 'verify-commit' (separation: tag signing, bukan commit signing)" {
|
|
61
|
+
($script:scriptContent -match 'verify-commit') | Should -BeFalse
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Describe "Version detection" {
|
|
66
|
+
It "Should define Get-LatestChangelogEntry function" {
|
|
67
|
+
$fnAst = $script:scriptAst.Find({
|
|
68
|
+
param($node)
|
|
69
|
+
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
|
|
70
|
+
$node.Name -ieq 'Get-LatestChangelogEntry'
|
|
71
|
+
}, $true)
|
|
72
|
+
$fnAst | Should -Not -BeNullOrEmpty
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
It "Should have version comparison logic (Sudah versi terbaru / sama / equal)" {
|
|
76
|
+
($script:scriptContent -match 'Sudah versi terbaru|sama|equal') | Should -BeTrue
|
|
77
|
+
}
|
|
78
|
+
}
|