v0.3.0: CI dotnet 8.0.424 pinned with isolated install
CI / build (push) Canceled after 4m25s
CI / test (push) Canceled after 4m25s
CI / release (push) Canceled after 0s

- Pin .NET SDK to 8.0.424 in all CI jobs to avoid floating channel drift

- Install SDK to runner temp via DOTNET_INSTALL_DIR to bypass

  C:\\Program Files\\dotnet overwrite/extract conflicts on Gitea Windows runners

- Simplify release job to dotnet publish + upload-artifact

- Exclude memory-bank/ via .gitignore (internal dev memory stays local)
This commit is contained in:
fristy authored and fristy committed 2026-09-05 00:18:59 +03:00
1 parent c9e643d617
commit 43f64ec68f
23 files changed
+22 -1311

No files matched your search

+22 -16
View File
@@ -12,7 +12,7 @@ on:
jobs: jobs:
build: build:
runs-on: windows runs-on: windows-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -22,7 +22,9 @@ jobs:
- name: Setup .NET 8 SDK - name: Setup .NET 8 SDK
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.424
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}\dotnet
- name: Cache NuGet packages - name: Cache NuGet packages
uses: actions/cache@v4 uses: actions/cache@v4
@@ -43,7 +45,7 @@ jobs:
run: dotnet build src/RgbLedStripController/RgbLedStripController.csproj -c Release --no-restore run: dotnet build src/RgbLedStripController/RgbLedStripController.csproj -c Release --no-restore
test: test:
runs-on: windows runs-on: windows-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -53,7 +55,9 @@ jobs:
- name: Setup .NET 8 SDK - name: Setup .NET 8 SDK
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.424
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}\dotnet
- name: Cache NuGet packages - name: Cache NuGet packages
uses: actions/cache@v4 uses: actions/cache@v4
@@ -83,7 +87,7 @@ jobs:
- build - build
- test - test
if: startsWith(gitea.ref, 'refs/tags/v') if: startsWith(gitea.ref, 'refs/tags/v')
runs-on: windows runs-on: windows-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -93,7 +97,9 @@ jobs:
- name: Setup .NET 8 SDK - name: Setup .NET 8 SDK
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: 8.0.x dotnet-version: 8.0.424
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}\dotnet
- name: Cache NuGet packages - name: Cache NuGet packages
uses: actions/cache@v4 uses: actions/cache@v4
@@ -107,14 +113,14 @@ jobs:
- name: Restore tools - name: Restore tools
run: dotnet tool restore run: dotnet tool restore
- name: Package (Velopack, unsigned) - name: Publish (Release, self-contained, single file)
shell: pwsh run: dotnet publish src/RgbLedStripController/RgbLedStripController.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true --configfile NuGet.Config
run: |
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/release/package-velopack.ps1 -SkipSigning
- name: Upload to Gitea Releases - name: Upload release artifacts
shell: pwsh uses: actions/upload-artifact@v4
env: with:
VPK_TOKEN: ${{ secrets.VPK_TOKEN }} name: rgb-led-strip-controller-${{ gitea.ref_name }}-win-x64
run: | path: |
powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/release/upload-feed.ps1 -ExpectedTag ${{ gitea.ref_name }} src/RgbLedStripController/bin/Release/net8.0-windows10.0.19041.0/win-x64/publish/RgbLedStripController.exe
src/RgbLedStripController/bin/Release/net8.0-windows10.0.19041.0/win-x64/publish/RgbLedStripController.pdb
if-no-files-found: error
-83
View File
@@ -1,83 +0,0 @@
# Optimization Audit System Prompt v2
## 1) Identity and Objective
You are a senior optimization auditor focused on practical, production-safe improvements.
Primary objective:
Find high-ROI opportunities to improve performance, scalability, efficiency, reliability, maintainability, and cost.
## 2) Non-Negotiable Context Intake Protocol
Before any analysis, read context in this exact order:
1. User-specified `memorybank.md` path (if provided and the file exists).
2. Project-root `memorybank.md` (if it exists and was not already read in step 1).
3. Project memory bank files:
- Layer 0 first: `00_project_snapshot.md`, `20_now.md`, `21_backlog.md`
- Layer 1 on demand: `10_scope.md`, `11_product.md`, `12_architecture.md`, `13_tech.md`
- Layer 2 on demand: `decisions/ADR-*.md`, `logs/tasks/...`, `logs/digests/...`
If a configured memorybank path is missing, report a brief context notice and continue with available files.
Default analysis scope is `diff + necessary context`.
Run full-repo optimization review only when the user explicitly requests it.
## 3) Optimization Priorities
Prioritize by ROI first, then effort and risk.
Preserve correctness and readability unless tradeoffs are explicitly requested.
Use evidence confidence labels for each bottleneck:
- `measured`: profiling, benchmark, or metric evidence exists.
- `inferred`: strong code/path evidence, but not benchmark-proven.
- `hypothesis`: plausible risk needing validation.
Evaluate, when relevant:
- CPU, memory, I/O, network, database, concurrency, caching.
- Reliability and cost inefficiencies.
- Duplication, dead code, and over-abstraction that increase runtime or delivery cost.
## 4) Required Output Format (Strict and Concise)
Structure output in this order:
### 1) Optimization Summary
- Current optimization health
- Top 3 highest-impact improvements
- Biggest risk if unchanged
### 2) Findings (Prioritized)
For each finding:
#### [OPT-###] <Title>
- Category: `CPU | Memory | I/O | Network | DB | Algorithm | Concurrency | Caching | Reliability | Cost | Build | Frontend`
- Severity: `Critical | High | Medium | Low`
- Confidence: `measured | inferred | hypothesis`
- Impact: <latency/throughput/memory/cost/reliability>
- Evidence: <path/query/loop/metric>
- Why inefficient: <root cause>
- Recommended fix: <concrete change>
- Tradeoffs or risks: <brief>
- Expected impact estimate: <percent or qualitative>
- Validation signal: <metric or benchmark to watch>
- Classification: `Reuse Opportunity | Dead Code | Over-Abstracted Code | N/A`
### 3) Quick Wins
Fast, high-value changes first.
### 4) Deeper Optimizations
Larger refactors worth planning next.
### 5) Validation Plan
- Benchmarks/profiling steps
- Before vs after metrics
- Correctness regression checks
### 6) Optional Mini Patch
Include only if enough context exists.
Sections 3, 4, and 6 may be omitted if empty.
Sections 1, 2, and 5 are always required.
## 5) Behavioral Constraints
- Do not apply fixes automatically.
- Avoid premature micro-optimizations unless clearly justified.
- If evidence is incomplete, label as `inferred` or `hypothesis` and state what to measure.
- Keep findings realistic for production teams.
- Put analysis output in `OPTIMIZATION_REPORT.md` unless user asks for a different target.
-85
View File
@@ -1,85 +0,0 @@
# Security Audit System Prompt v2
## 1) Identity and Objective
You are a Senior Security Researcher and Application Security Expert.
Mindset: adversarial, evidence-driven, zero trust.
Primary objective:
Analyze code changes for exploitable security weaknesses and provide concrete, verifiable remediation guidance.
## 2) Non-Negotiable Context Intake Protocol
Before any analysis, read context in this exact order:
1. User-specified `memorybank.md` path (if provided and the file exists).
2. Project-root `memorybank.md` (if it exists and was not already read in step 1).
3. Project memory bank files:
- Layer 0 first: `00_project_snapshot.md`, `20_now.md`, `21_backlog.md`
- Layer 1 on demand: `10_scope.md`, `11_product.md`, `12_architecture.md`, `13_tech.md`
- Layer 2 on demand: `decisions/ADR-*.md`, `logs/tasks/...`, `logs/digests/...`
If a configured memorybank path is missing, report a brief context notice and continue with available files.
Default analysis scope is `diff + necessary context`.
Run full-repo security review only when the user explicitly requests it.
## 3) Analysis Scope and Protocol
Treat changed lines as attack-surface entry points, then expand only where needed.
Inspect these risk classes where relevant:
1. Injection: SQL, command, template, LDAP, NoSQL, path traversal, XSS.
2. Authentication, authorization, and session flaws: missing checks, IDOR, privilege escalation, weak session handling.
3. Secrets and sensitive data exposure: hardcoded credentials, token leakage, insecure logging, PII disclosure.
4. Cryptography and integrity: weak algorithms, bad key handling, missing signature/verification steps.
5. Security misconfiguration and hardening gaps: debug modes, permissive CORS, missing headers, unsafe defaults.
6. Dependency and supply-chain risks: vulnerable packages, unpinned sources, unsafe update patterns.
7. Deserialization, parser, and object integrity flaws.
8. SSRF and network boundary issues.
9. Concurrency and race-condition abuse paths.
10. Availability and abuse vectors: unbounded loops, no rate limits, resource exhaustion, DoS amplification.
11. Business-logic abuse and workflow bypass risks.
## 4) Required Output Format (Strict and Concise)
Start directly with:
`SECURITY AUDIT: <brief summary of changes reviewed>`
Then provide sections in this order:
### Risk Assessment
`Critical | High | Medium | Low | Secure`
### Scope
- Mode: `diff + necessary context` or `full repo`
- Assumptions and limits (short)
### Memory Intake
- Read order executed (short list)
- Missing context files (if any)
### Findings
For each finding, use this exact schema:
#### [SEC-###] <Title>
- Severity: `Critical | High | Medium | Low`
- Confidence: `Confirmed | Likely | Possible`
- Category: <risk class>
- Location: <file:line or component>
- Exploit Path: <how attacker can abuse it>
- Impact: <confidentiality/integrity/availability/business>
- Evidence: <code-level proof or strong signal>
- Fix: <concrete remediation + mini patch/snippet when possible>
- Verification: <how to prove fix works and exploit is closed>
### No Findings (only when applicable)
If no vulnerabilities are found, output:
- `No Findings` with final risk level
- Checked areas
- Residual risk and what was not fully verifiable
### Observations
Optional hardening notes that are not direct vulnerabilities.
## 5) Behavioral Constraints
- Do not apply fixes automatically.
- Keep output high signal and actionable.
- If evidence is incomplete, do not overstate certainty; downgrade confidence and provide verification steps.
- If potential secrets are found, treat as at least High and escalate to Critical when clearly valid.
- Put analysis output in `SECURITY_REPORT.md` unless user asks for a different target.
-43
View File
@@ -1,43 +0,0 @@
# v1 to v2 Migration Playbook
This playbook migrates the old 6-file memory bank to the v2 layered model.
## Mapping Table
| v1 file | v2 destination |
| --- | --- |
| `projectbrief.md` | `10_scope.md` |
| `productContext.md` | `11_product.md` |
| `systemPatterns.md` | `12_architecture.md` |
| `techContext.md` | `13_tech.md` |
| `activeContext.md` | `20_now.md` |
| `progress.md` | `21_backlog.md` + `logs/tasks/...` + `00_project_snapshot.md` |
## Migration Steps
1. Create the v2 tree from `templates/`.
2. Copy current-truth statements from v1 files into mapped v2 core files.
3. Remove historical narrative from core content while migrating.
4. Extract major decisions into ADRs (`decisions/ADR-*.md`).
5. Extract execution history into task logs (`logs/tasks/YYYY/MM/*.md`).
6. Build `00_project_snapshot.md` from current state, top risks, and top priorities.
7. Build `21_backlog.md` from remaining actionable items only.
8. If old history is large, summarize older-than-90-day items into monthly digests.
9. Run consistency checks from `OPERATIONS.md`.
## Conversion Rules
- Keep one source of truth per concept.
- If a paragraph repeats in multiple files, keep the canonical copy in one core file and link it elsewhere.
- Core files must answer "what is true now", not "what happened over time".
- Place justifications and tradeoffs in ADRs, not in core files.
## Suggested First ADRs
- `ADR-0001-memory-bank-v2-adoption.md`: why v2 was adopted.
- `ADR-0002-layered-read-protocol.md`: why layered reading is required.
- `ADR-0003-core-no-changelog-rule.md`: why history is separated from core files.
## Completion Criteria
- Layer 0 files are enough for a fast, accurate project summary.
- All backlog items are actionable and non-duplicated.
- Old historical detail is linked through task logs and digests.
- No core file breaches hard line budgets.
-48
View File
@@ -1,48 +0,0 @@
# Memory Bank v2 Operations
## Command: `update memory bank`
Use this when significant changes happen or context gets stale.
### Procedure
1. Read Layer 0 (`00_project_snapshot.md`, `20_now.md`, `21_backlog.md`).
2. Read additional Layer 1 and Layer 2 files that are relevant to touched work.
3. Refresh core files to current truth.
4. Write or update ADRs for durable decisions.
5. Write a task log for significant implementation steps.
6. Validate:
- All referenced ADR and task IDs exist.
- Core files do not contain changelog-style history.
- Budgets from `SCHEMA.md` are respected.
7. Update `Last Updated` fields in touched files.
### Expected Result
- Layer 0 gives an accurate project state in minutes.
- Decision and execution history is traceable via IDs.
## Command: `compact memory bank`
Use this manually for maintenance.
### Triggers
- Any file exceeds hard budget.
- Backlog has many stale completed items.
- Task logs older than 90 days are accumulating.
### Procedure
1. Identify oversized core files.
2. Remove historical narrative from core files.
3. Ensure old details live in ADR/task logs.
4. For each month older than 90 days, create or refresh `logs/digests/YYYY-MM.md`.
5. Replace verbose old references in core files with digest links.
6. Re-validate ID/link integrity and budgets.
### Expected Result
- Core remains concise.
- Older history stays discoverable through digests and linked logs.
## Consistency Checklist
- Every backlog item has an ID, status, and owner.
- Every ADR has status (`proposed`, `accepted`, `superseded`) and links.
- `20_now.md` and `21_backlog.md` do not conflict.
- `00_project_snapshot.md` reflects the same priorities as `20_now.md`.
-113
View File
@@ -1,113 +0,0 @@
# Memory Bank v2 Schema
## Required Tree
```text
memory-bank/
00_project_snapshot.md
10_scope.md
11_product.md
12_architecture.md
13_tech.md
20_now.md
21_backlog.md
decisions/
ADR-0001-short-title.md
logs/
tasks/YYYY/MM/TASK-YYYYMMDD-001-short-title.md
digests/YYYY-MM.md
```
## ID and Naming Rules
- ADR ID: `ADR-0001` style, incrementing sequence.
- Task ID: `TASK-YYYYMMDD-###` style, sequence resets daily.
- Backlog ID: `BG-001` style.
- Core file names are fixed and must not be renamed.
- Keep file names in English; content may be Turkish or English.
## Hard Size Budgets
| File | Max lines | Role |
| --- | ---: | --- |
| `00_project_snapshot.md` | 120 | One-page entry point and quick orientation |
| `10_scope.md` | 180 | Scope boundaries and success criteria |
| `11_product.md` | 220 | User/problem/value model |
| `12_architecture.md` | 260 | Architecture map and critical paths |
| `13_tech.md` | 220 | Stack, setup, constraints, runbook basics |
| `20_now.md` | 160 | Current focus, blockers, assumptions |
| `21_backlog.md` | 200 | Prioritized backlog and status |
| `decisions/ADR-*.md` | 180 each | Durable rationale and decision records |
| `logs/tasks/.../*.md` | 250 each | Task execution narrative |
| `logs/digests/YYYY-MM.md` | 220 each | Monthly summary of old history |
## Section Budgets
### `00_project_snapshot.md` (<=120)
- Project Card: <=8 lines
- One Paragraph Summary: <=6 lines
- Current State: <=12 bullets
- Decision Index: <=12 ADR links
- Top Risks: <=8 bullets
- Next 7 Days: <=7 bullets
- Read Map: <=6 lines
### `10_scope.md` (<=180)
- Mission: <=6 lines
- In Scope: <=25 bullets
- Out of Scope: <=25 bullets
- Constraints: <=15 bullets
- Success Criteria: <=15 bullets
- Non-goals: <=10 bullets
### `11_product.md` (<=220)
- User Segments: <=12 bullets
- Problems: <=15 bullets
- Desired Outcomes: <=15 bullets
- Core Flows: <=15 bullets
- UX Principles: <=10 bullets
- Product Risks: <=12 bullets
### `12_architecture.md` (<=260)
- System Map: <=40 lines
- Components and Contracts: <=25 bullets
- Data Flows: <=25 bullets
- Critical Paths: <=20 bullets
- Failure Modes: <=20 bullets
- Scaling Constraints: <=15 bullets
### `13_tech.md` (<=220)
- Stack Matrix: <=35 lines
- Environments: <=20 bullets
- Setup and Commands: <=25 lines
- Dependencies: <=20 bullets
- Constraints and Limits: <=15 bullets
### `20_now.md` (<=160)
- Current Objective: <=5 lines
- Active Workstreams: <=12 bullets
- Blockers: <=10 bullets
- Next Actions: <=10 bullets
- Assumptions: <=10 bullets
- Watchlist: <=10 bullets
### `21_backlog.md` (<=200)
- Prioritized table rows: <=60 items
- Completed items older than 30 days must be removed from active table and referenced from task logs/digests.
## Core File Guardrails
- Core files are current truth only.
- Forbidden in core files: long chronology, date-by-date logs, and narrative changelogs.
- Keep details in ADRs and task logs; keep summaries in core files.
- Prefer references over duplicated prose.
## Over-Limit Policy
When any file exceeds budget:
1. Keep only current truth in the core file.
2. Move historical detail to ADR/task logs.
3. Add references to moved details.
4. Re-run a consistency check (IDs resolve, no stale links).
## 90-Day Retention Policy
- Task logs in the last 90 days are hot history.
- History older than 90 days must be summarized into monthly digest files.
- Compaction is manual and triggered by `compact memory bank`.
-21
View File
@@ -1,21 +0,0 @@
# Memory Bank v2 Templates
Use files under `templates/` as copy-ready starters for each new project.
## Template Index
- `templates/00_project_snapshot.md`
- `templates/10_scope.md`
- `templates/11_product.md`
- `templates/12_architecture.md`
- `templates/13_tech.md`
- `templates/20_now.md`
- `templates/21_backlog.md`
- `templates/decisions/ADR-0000-template.md`
- `templates/logs/tasks/YYYY/MM/TASK-YYYYMMDD-001-template.md`
- `templates/logs/digests/YYYY-MM.md`
## Usage Rules
1. Keep section headings stable so tools can parse quickly.
2. Replace placeholders (`<...>`) immediately; avoid leaving TODO placeholders in active projects.
3. Keep IDs stable once created.
4. Keep core files concise and route history to ADR/log templates.
-29
View File
@@ -1,29 +0,0 @@
# Memory Bank v2 Test Checklist
## 1. Startup Test (2-3 minute orientation)
- Read only Layer 0 files.
- Confirm the summary includes objective, current status, risks, and next actions.
- Pass if no Layer 1/2 file is required for a basic project brief.
## 2. Scale Test (200+ task logs)
- Ensure core files remain within hard budgets.
- Confirm old details are reachable through task logs and digests.
- Pass if context load time remains fast and no core file turns into a log.
## 3. Audit Trace Test
- Pick one decision and trace:
1. Core summary mention
2. ADR rationale
3. Task log implementation record
- Pass if links are complete and consistent.
## 4. Anti-duplication Test
- Scan core files for repeated long paragraphs.
- Pass if repeated content is replaced with canonical links.
## 5. Compaction Test
- Run `compact memory bank` procedure.
- Confirm:
- Core files shrink to budget.
- 90+ day history appears in monthly digests.
- Link integrity is preserved.
@@ -1,35 +0,0 @@
# 00 Project Snapshot
Last Updated: YYYY-MM-DD
Project: <name>
Owner: <person-or-team>
Stage: discovery|build|stabilize|maintenance
## One Paragraph Summary
<3-5 sentences that explain what this project does and why it matters.>
## Current State
- <current truth bullet 1>
- <current truth bullet 2>
## Top Facts
- [F01] <fact>
- [F02] <fact>
## Decision Index
- [ADR-0001] <title>
## Top Risks
- <risk and impact>
## Next 7 Days
- <priority 1>
- <priority 2>
## Read Map
- Scope: `10_scope.md`
- Product: `11_product.md`
- Architecture: `12_architecture.md`
- Tech: `13_tech.md`
- Current Operations: `20_now.md`
- Backlog: `21_backlog.md`
-22
View File
@@ -1,22 +0,0 @@
# 10 Scope
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## Mission
<single paragraph>
## In Scope
- <item>
## Out of Scope
- <item>
## Constraints
- <constraint>
## Success Criteria
- <measurable criterion>
## Non-goals
- <non-goal>
-22
View File
@@ -1,22 +0,0 @@
# 11 Product
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## User Segments
- <segment>
## Problems
- <problem>
## Desired Outcomes
- <outcome>
## Core Flows
- <flow>
## UX Principles
- <principle>
## Product Risks
- <risk>
@@ -1,22 +0,0 @@
# 12 Architecture
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## System Map
<high-level architecture summary>
## Components and Contracts
- <component>: <contract>
## Data Flows
- <flow>
## Critical Paths
- <path>
## Failure Modes
- <failure mode and mitigation>
## Scaling Constraints
- <constraint>
-21
View File
@@ -1,21 +0,0 @@
# 13 Tech
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## Stack Matrix
| Layer | Technology | Notes |
| --- | --- | --- |
| <layer> | <tech> | <notes> |
## Environments
- <environment details>
## Setup and Commands
- `<command>`: <purpose>
## Dependencies
- <dependency and role>
## Constraints and Limits
- <limit>
-22
View File
@@ -1,22 +0,0 @@
# 20 Now
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## Current Objective
<single objective statement>
## Active Workstreams
- <workstream>
## Blockers
- <blocker>
## Next Actions
- [TASK-YYYYMMDD-001] <action>
## Assumptions
- <assumption>
## Watchlist
- <item to monitor>
-16
View File
@@ -1,16 +0,0 @@
# 21 Backlog
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## Prioritized Items
| ID | Status | Priority | Summary | Owner | Link |
| --- | --- | --- | --- | --- | --- |
| BG-001 | todo | p1 | <item> | <owner> | <task-log-or-doc-link> |
Status values: `todo`, `in_progress`, `blocked`, `done`.
## Deferred
| ID | Reason | Revisit Date |
| --- | --- | --- |
| BG-099 | <reason> | YYYY-MM-DD |
@@ -1,26 +0,0 @@
# ADR-0000: <short title>
Date: YYYY-MM-DD
Status: proposed|accepted|superseded
Owners: <person-or-team>
## Context
<why this decision is needed>
## Decision
<what was decided>
## Consequences
- Positive: <impact>
- Negative: <tradeoff>
## Alternatives Considered
- <option A> - <why not chosen>
- <option B> - <why not chosen>
## Links
- Related tasks: `TASK-YYYYMMDD-001`
- Related docs: `<path>`
## Supersedes / Superseded By
- <ADR-id-or-none>
@@ -1,20 +0,0 @@
# YYYY-MM Digest
Month: YYYY-MM
Last Updated: YYYY-MM-DD
Owner: <person-or-team>
## Summary
<high-level summary of this month for old-history compaction>
## Key Outcomes
- <outcome>
## Decisions Referenced
- `ADR-0001` - <title>
## Notable Tasks
- `TASK-YYYYMMDD-001` - <result>
## Carryover Risks
- <risk>
@@ -1,25 +0,0 @@
# TASK-YYYYMMDD-001: <short title>
Date: YYYY-MM-DD
Owner: <person-or-team>
Status: in_progress|done|blocked
## Intent
<what this task was trying to achieve>
## Scope
- <in>
- <out>
## Changes
- <change summary>
## Validation
- <test/check 1>
- <test/check 2>
## Decision Links
- ADR: `ADR-0001`
## Follow-ups
- [BG-001] <follow-up item>
-115
View File
@@ -1,115 +0,0 @@
# Memory Bank v2 System Prompt
You are an expert software engineer with zero session memory between runs.
Your only reliable continuity is the project Memory Bank. Maintain it with
strict structure, minimal duplication, and strong audit traceability.
## Objectives
1. Fast context load for any task.
2. Maximum useful coverage without bloated core files.
3. Strong audit trail across decisions and execution history.
4. Safe reuse of external project references and imported documentation.
## Required Contract
Use this layout under `memory-bank/`:
```text
memory-bank/
00_project_snapshot.md
10_scope.md
11_product.md
12_architecture.md
13_tech.md
20_now.md
21_backlog.md
references/
...
decisions/
ADR-*.md
logs/
tasks/YYYY/MM/*.md
digests/YYYY-MM.md
```
If a required file is missing, create a minimal stub immediately.
## Layered Read Protocol
At the start of every task:
1. Always read Layer 0: `00_project_snapshot.md`, `20_now.md`, `21_backlog.md`.
2. Read Layer 1 only when needed:
* Product intent: `11_product.md`
* Scope and goals: `10_scope.md`
* Architecture: `12_architecture.md`
* Tech/runtime/tooling: `13_tech.md`
3. Read Layer 1.5 only when external reference context is needed:
* Imported external framework or library documentation: `references/**`
* External project materials used as implementation reference: `references/**`
4. Read Layer 2 only when evidence is required:
* Rationale and permanent decisions: `decisions/ADR-*.md`
* Execution history and outcomes: `logs/tasks/...`, `logs/digests/...`
Do not read all files by default. Expand only when the task requires it.
## Reference Rules
1. `memory-bank/references/` is reserved only for external project references and imported documentation.
2. Examples include framework docs, library usage guides, API references, vendor docs, and selected external project materials.
3. Unless the user explicitly says otherwise, everything under `memory-bank/references/` is read-only for the AI/LLM.
4. Read-only means the AI/LLM must not create, modify, move, rename, summarize-overwrite, or delete files in `references/` unless the user explicitly authorizes it.
5. The AI/LLM may read and cite `references/` when relevant.
6. When project implementation depends on an external framework or tool, prefer `references/` as the authoritative source for that external behavior, while keeping project-specific decisions and adaptations inside the normal Memory Bank files.
## Write Protocol
1. Core files are `current truth only`.
2. Never keep changelog-style history inside core files.
3. Historical details belong only in `decisions/` and `logs/`.
4. Every non-trivial decision gets an ADR (or updates an existing ADR).
5. Every significant execution step gets a task log entry.
6. Prefer linking IDs (`ADR-xxxx`, `TASK-...`) instead of copying content.
7. Never write into `references/` unless the user explicitly instructs you to do so.
## Commands
When the user says `update memory bank`:
1. Re-read all core files and recently relevant ADR/task logs.
2. Re-read relevant files under `references/` only if external reference context affects current truth.
3. Refresh core files to reflect current truth.
4. Run consistency checks (links, IDs, stale sections, budget limits).
5. Add or update ADRs and task logs when needed.
6. Do not modify `references/` unless explicitly authorized by the user.
When the user says `compact memory bank`:
1. Enforce hard size budgets from `memory-bank-v2/SCHEMA.md`.
2. Move old details out of core files into ADR/log references.
3. Summarize `90+` day task history into monthly digests in `logs/digests/`.
4. Keep core files concise and decision-ready.
5. Do not compact, rewrite, or prune `references/` unless explicitly authorized by the user.
## Guardrails
1. No changelog sections in core files (`10_*` through `21_*`).
2. No duplicated long-form text across core files.
3. No orphan IDs: all ADR/task references must resolve to real files.
4. If uncertain, record assumptions explicitly in `20_now.md`.
5. Do not treat `references/` as a workspace for generated project memory.
6. Do not copy large amounts of external documentation into core files; extract only project-relevant conclusions, constraints, and decisions.
## Formatting and Language
1. File names stay in English.
2. Content can be English or Turkish.
3. Keep entries concise and scannable.
Detailed budgets, templates, operations, and migration guidance are in:
`memory-bank-v2/SCHEMA.md`, `memory-bank-v2/TEMPLATES.md`,
`memory-bank-v2/OPERATIONS.md`, and `memory-bank-v2/MIGRATION_PLAYBOOK.md`.
-48
View File
@@ -1,48 +0,0 @@
# Scripts
## Structure
- `release/`
- `package-velopack.ps1`: Gitea version guard + build + package release assets + optional Authenticode signing + installer icon
- `upload-feed.ps1`: `vpk upload gitea` ile Gitea Releases'e hedefli upload
- `version/`
- `bump-version.ps1`: bump `major.minor.patch` version fields in the app project
## Prerequisites
- .NET 8 SDK (`actions/setup-dotnet` CI'da kurar, localde PATH'te `dotnet` olmali)
- Repo-local `vpk` tool: `dotnet tool restore` (manifest: `.config/dotnet-tools.json`, vpk 0.0.1298)
## Common Commands
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/version/bump-version.ps1 -Patch`
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/version/bump-version.ps1 -Minor`
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/version/bump-version.ps1 -Major`
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/release/package-velopack.ps1 -SkipSigning`
- `powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/release/package-velopack.ps1 -PfxPath <cert.pfx>`
- `$env:VPK_TOKEN = "<gitea-token>"; powershell.exe -NoProfile -ExecutionPolicy Bypass -File scripts/release/upload-feed.ps1`
## Signing
- Authenticode signing varsayilan olarak **pasif**: `-PfxPath` verilmezse uyariyla atlanir.
- Acmak icin `-PfxPath <cert.pfx>` ver; PFX sifresi interaktif sorulur, script icinde saklanmaz.
- Timestamp (TSA) kullanilmiyor; gerekirse global bir TSA servisiyle `-SignToolPath` akisina eklenebilir.
- v0.3.0 stratejisi: GPG imzali git tag + Gitea release hash'leri yeterli.
## Packaging Guard
- `scripts/release/package-velopack.ps1` Gitea Releases API uzerinden cloud surumu kontrol eder.
- Gitea'da ayni surum (tag `v<version>`) varsa paketleme durdurulur.
- Gitea'daki surum daha yuksekse paketleme durdurulur.
- API'ye erisilemiyorsa fail-fast durur; offline paketleme icin `-SkipCloudCheck` kullanilabilir.
- Varsayilan installer ikonu `src/RgbLedStripController/Assets/AppIcon.ico` dosyasindan `vpk pack --icon` ile verilir.
## Upload Guard
- `scripts/release/upload-feed.ps1` yerel release manifest'ini okur, `full/delta .nupkg` + `Setup.exe` / `Portable.zip` varligini preflight olarak dogrular.
- `-Token` verilmezse `VPK_TOKEN` env kullanilir; ikisi de yoksa durur.
- Hedef tag (`v<version>`) Gitea'da varsa upload durdurulur (`-Merge` ile mevcut release'e birlestirilebilir).
- `-ExpectedTag` verilirse turetilen tag ile karsilastirilir (CI'da tag/csproj tutarliligi icin).
- `-WhatIf` ile dry-run yapilabilir.
- Basarili oldugunda Gitea release linkini ekrana yazar.
## CI (Gitea Actions)
- Workflow: `.gitea/workflows/ci.yml` — `build`, `test`, `release` job'lari.
- `windows` etiketli runner gerekir (WPF sadece Windows'ta derlenir).
- NuGet cache adimi `continue-on-error: true` — cache servisi yoksa build kirilmaz.
- `release` job'u sadece `v*` tag'lerinde calisir, `-SkipSigning` ile paketler.
- Gerekli repo secret: `VPK_TOKEN` (Gitea API token, release yazma yetkili).
-271
View File
@@ -1,271 +0,0 @@
[CmdletBinding()]
param(
[string]$Project = "src/RgbLedStripController/RgbLedStripController.csproj",
[string]$Configuration = "Release",
[string]$Runtime = "win-x64",
[string]$Channel = "win-x64-stable",
[string]$PackId = "Fristy.RgbLedStripController",
[string]$PackTitle = "RGB Led Strip Controller",
[string]$PackAuthors = "fristy",
[string]$GiteaRepoUrl = "https://git.fristy.net/fristy/rgb-led-strip-controller",
[string]$InstallerIcon = "src/RgbLedStripController/Assets/AppIcon.ico",
[string]$PfxPath = "",
[string]$SignToolPath = "signtool.exe",
[int]$SignParallel = 1,
[switch]$SkipSigning,
[switch]$SkipCloudCheck
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\.."))
$projectPath = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $Project))
$nugetConfigPath = [System.IO.Path]::GetFullPath((Join-Path $repoRoot "NuGet.Config"))
if (-not (Test-Path -LiteralPath $projectPath)) {
throw "Project file not found: $projectPath"
}
[xml]$projectXml = Get-Content -Raw -LiteralPath $projectPath
$versionNode = $null
foreach ($propertyGroup in @($projectXml.Project.PropertyGroup)) {
if ($propertyGroup.PSObject.Properties.Name -contains "Version" -and -not [string]::IsNullOrWhiteSpace($propertyGroup.Version)) {
$versionNode = $propertyGroup.Version
break
}
}
$version = [string]$versionNode
if ([string]::IsNullOrWhiteSpace($version)) {
throw "No <Version> found in project file: $projectPath"
}
function Assert-WorkspacePath {
param([string]$Path)
$fullPath = [System.IO.Path]::GetFullPath($Path)
if (-not $fullPath.StartsWith($repoRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to use path outside workspace: $fullPath"
}
return $fullPath
}
function Resolve-AbsolutePath {
param([string]$Path)
if ([string]::IsNullOrWhiteSpace($Path)) {
throw "Path value cannot be empty."
}
if ([System.IO.Path]::IsPathRooted($Path)) {
return [System.IO.Path]::GetFullPath($Path)
}
return [System.IO.Path]::GetFullPath((Join-Path $repoRoot $Path))
}
function Get-GiteaReleaseTags {
param([string]$RepoUrl)
$uri = [Uri]$RepoUrl
$segments = $uri.AbsolutePath.Trim("/").Split("/") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
if ($segments.Count -lt 2) {
throw "Could not parse owner/repo from GiteaRepoUrl: $RepoUrl"
}
$owner = $segments[-2]
$repo = $segments[-1]
$apiUrl = "{0}://{1}/api/v1/repos/{2}/{3}/releases?draft=false&limit=50" -f $uri.Scheme, $uri.Authority, $owner, $repo
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$releases = Invoke-RestMethod -Uri $apiUrl -Method Get -ErrorAction Stop
}
catch {
throw "Gitea releases could not be read: $apiUrl`n$($_.Exception.Message)"
}
return @($releases | ForEach-Object { $_.tag_name } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
}
function Get-MaxVersionFromTags {
param([string[]]$Tags)
$versions = @()
foreach ($tag in $Tags) {
$clean = $tag.TrimStart("v", "V")
$parsed = $null
if ([Version]::TryParse($clean, [ref]$parsed)) {
$versions += $parsed
}
}
if ($versions.Count -eq 0) {
return $null
}
return ($versions | Sort-Object -Descending | Select-Object -First 1)
}
function Read-SecretAsPlainText {
param([string]$Prompt)
$secureString = Read-Host -Prompt $Prompt -AsSecureString
if ($secureString.Length -eq 0) {
throw "Empty password is not allowed."
}
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
try {
return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
}
finally {
if ($bstr -ne [IntPtr]::Zero) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
}
}
function Resolve-SignToolExecutable {
param([string]$RequestedPath)
if (Test-Path -LiteralPath $RequestedPath) {
return [System.IO.Path]::GetFullPath($RequestedPath)
}
$command = Get-Command -Name $RequestedPath -ErrorAction SilentlyContinue
if ($null -ne $command) {
return $command.Source
}
throw "signtool.exe could not be resolved. Pass -SignToolPath with an absolute path if needed."
}
$publishDir = Assert-WorkspacePath (Join-Path $repoRoot "artifacts\publish\$Runtime\$version")
$releaseDir = Assert-WorkspacePath (Join-Path $repoRoot "artifacts\velopack\$Channel")
$mainExe = "RgbLedStripController.exe"
$dotnet = "dotnet"
$signingEnabled = (-not $SkipSigning.IsPresent) -and (-not [string]::IsNullOrWhiteSpace($PfxPath))
if ((-not $SkipSigning.IsPresent) -and [string]::IsNullOrWhiteSpace($PfxPath)) {
Write-Warning "PfxPath not supplied; Authenticode signing will be skipped. Pass -PfxPath to enable it."
}
$signToolExecutable = $null
$signToolDirectory = $null
$plainPassword = $null
$resolvedPfxPath = $null
$resolvedInstallerIconPath = Resolve-AbsolutePath -Path $InstallerIcon
$signParams = $null
if (-not (Test-Path -LiteralPath $resolvedInstallerIconPath)) {
throw "Installer icon file not found: $resolvedInstallerIconPath"
}
$env:DOTNET_CLI_HOME = Join-Path $repoRoot ".dotnet"
$env:APPDATA = Join-Path $repoRoot ".appdata"
$env:NUGET_PACKAGES = Join-Path $repoRoot ".nuget\packages"
$env:NUGET_HTTP_CACHE_PATH = Join-Path $repoRoot ".nuget\http-cache"
$localVersion = [Version]$version
$cloudVersion = $null
if (-not $SkipCloudCheck.IsPresent) {
$cloudVersion = Get-MaxVersionFromTags -Tags (Get-GiteaReleaseTags -RepoUrl $GiteaRepoUrl)
}
if ($null -ne $cloudVersion) {
if ($localVersion -lt $cloudVersion) {
throw "Local version $version is older than Gitea version $cloudVersion. Bump the project version before packaging."
}
if ($localVersion -eq $cloudVersion) {
throw "Gitea already contains version $version. Packaging with the same version is blocked."
}
}
if ($signingEnabled) {
$resolvedPfxPath = Resolve-AbsolutePath -Path $PfxPath
if (-not (Test-Path -LiteralPath $resolvedPfxPath)) {
throw "PFX file not found: $resolvedPfxPath"
}
if ($SignParallel -lt 1) {
throw "SignParallel must be at least 1."
}
$signToolExecutable = Resolve-SignToolExecutable -RequestedPath $SignToolPath
$signToolDirectory = Split-Path -Parent $signToolExecutable
if (-not [string]::IsNullOrWhiteSpace($signToolDirectory)) {
$env:PATH = $signToolDirectory + [System.IO.Path]::PathSeparator + $env:PATH
}
$plainPassword = Read-SecretAsPlainText -Prompt "PFX password"
$signParams = "/fd SHA256 /f `"$resolvedPfxPath`" /p `"$plainPassword`""
}
if (Test-Path -LiteralPath $publishDir) {
Remove-Item -LiteralPath $publishDir -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $publishDir | Out-Null
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
$publishArgs = @(
"publish",
$projectPath,
"-c", $Configuration,
"-r", $Runtime,
"--self-contained", "true",
"-p:PublishSingleFile=true",
"-o", $publishDir,
"--configfile", $nugetConfigPath
)
& $dotnet @publishArgs
if ($LASTEXITCODE -ne 0) {
throw "dotnet publish failed with exit code $LASTEXITCODE"
}
$packArgs = @(
"tool", "run", "vpk", "--",
"pack",
"--packId", $PackId,
"--packVersion", $version,
"--packDir", $publishDir,
"--mainExe", $mainExe,
"--packTitle", $PackTitle,
"--packAuthors", $PackAuthors,
"--runtime", $Runtime,
"--channel", $Channel,
"--icon", $resolvedInstallerIconPath,
"--outputDir", $releaseDir
)
if ($signingEnabled) {
$packArgs += @(
"--signParams", $signParams,
"--signParallel", $SignParallel.ToString()
)
}
& $dotnet @packArgs
if ($LASTEXITCODE -ne 0) {
throw "vpk pack failed with exit code $LASTEXITCODE"
}
Write-Host ""
Write-Host "Velopack package build complete." -ForegroundColor Green
Write-Host "Version: $version"
Write-Host "Gitea: $(if ($null -ne $cloudVersion) { $cloudVersion.ToString() } else { 'none' })"
Write-Host "Signing: $(if ($signingEnabled) { 'enabled' } else { 'skipped' })"
if ($signingEnabled -and -not [string]::IsNullOrWhiteSpace($signToolExecutable)) {
Write-Host "SignTool: $signToolExecutable"
}
Write-Host "Icon: $resolvedInstallerIconPath"
Write-Host "PublishDir: $publishDir"
Write-Host "ReleaseDir: $releaseDir"
Write-Host "Gitea repo: $GiteaRepoUrl"
Write-Host "Tag: v$version"
Write-Host ""
Write-Host "Upload with: scripts/release/upload-feed.ps1 (vpk upload gitea)." -ForegroundColor Yellow
-141
View File
@@ -1,141 +0,0 @@
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[string]$Channel = "win-x64-stable",
[string]$GiteaRepoUrl = "https://git.fristy.net/fristy/rgb-led-strip-controller",
[string]$Tag = "",
[string]$Token = $env:VPK_TOKEN,
[string]$ExpectedTag = "",
[switch]$Prerelease,
[switch]$Merge
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\.."))
if ([string]::IsNullOrWhiteSpace($Token)) {
throw "Gitea token is required. Pass -Token or set the VPK_TOKEN environment variable."
}
function Assert-WorkspacePath {
param([string]$Path)
$fullPath = [System.IO.Path]::GetFullPath($Path)
if (-not $fullPath.StartsWith($repoRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to use path outside workspace: $fullPath"
}
return $fullPath
}
function Get-GiteaReleaseTagNames {
param(
[string]$RepoUrl,
[string]$AccessToken
)
$uri = [Uri]$RepoUrl
$segments = $uri.AbsolutePath.Trim("/").Split("/") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
if ($segments.Count -lt 2) {
throw "Could not parse owner/repo from GiteaRepoUrl: $RepoUrl"
}
$owner = $segments[-2]
$repo = $segments[-1]
$apiUrl = "{0}://{1}/api/v1/repos/{2}/{3}/releases?draft=false&limit=50" -f $uri.Scheme, $uri.Authority, $owner, $repo
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$headers = @{ Authorization = "token $AccessToken" }
$releases = Invoke-RestMethod -Uri $apiUrl -Method Get -Headers $headers -ErrorAction Stop
}
catch {
throw "Gitea releases could not be read: $apiUrl`n$($_.Exception.Message)"
}
return @($releases | ForEach-Object { $_.tag_name } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
}
$releaseDir = Assert-WorkspacePath (Join-Path $repoRoot "artifacts\velopack\$Channel")
$manifestPath = Join-Path $releaseDir "releases.$Channel.json"
if (-not (Test-Path -LiteralPath $releaseDir)) {
throw "Release directory not found: $releaseDir"
}
if (-not (Test-Path -LiteralPath $manifestPath)) {
throw "Release manifest not found: $manifestPath"
}
$localManifest = Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json
if ($null -eq $localManifest.Assets -or $localManifest.Assets.Count -eq 0) {
throw "Release manifest does not contain any assets: $manifestPath"
}
$packageId = $localManifest.Assets[0].PackageId
$localVersions = @(
$localManifest.Assets |
Where-Object { $_.PackageId -eq $packageId -and -not [string]::IsNullOrWhiteSpace($_.Version) } |
ForEach-Object { [Version]$_.Version }
)
if ($localVersions.Count -eq 0) {
throw "Could not determine local package version from: $manifestPath"
}
$localVersion = ($localVersions | Sort-Object -Descending | Select-Object -First 1)
$tag = if ([string]::IsNullOrWhiteSpace($Tag)) { "v$($localVersion.ToString())" } else { $Tag }
if (-not [string]::IsNullOrWhiteSpace($ExpectedTag) -and -not [string]::Equals($tag, $ExpectedTag, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Derived tag $tag does not match expected tag $ExpectedTag. Check the pushed tag against the project version."
}
$setupFiles = @(Get-ChildItem -LiteralPath $releaseDir -Filter "*-Setup.exe" -File)
$portableFiles = @(Get-ChildItem -LiteralPath $releaseDir -Filter "*-Portable.zip" -File)
if ($setupFiles.Count -eq 0) {
throw "No Setup.exe file found in release directory: $releaseDir"
}
if ($portableFiles.Count -eq 0) {
throw "No Portable.zip file found in release directory: $releaseDir"
}
$existingTags = Get-GiteaReleaseTagNames -RepoUrl $GiteaRepoUrl -AccessToken $Token
if ($existingTags -contains $tag -and -not $Merge.IsPresent) {
throw "Gitea already contains tag $tag. Upload is blocked (pass -Merge to merge into the existing release)."
}
$releaseUrl = $GiteaRepoUrl.TrimEnd("/") + "/releases/tag/" + $tag
$vpkArgs = @(
"tool", "run", "vpk", "--",
"upload", "gitea",
"--outputDir", $releaseDir,
"--channel", $Channel,
"--repoUrl", $GiteaRepoUrl,
"--token", $Token,
"--tag", $tag,
"--publish"
)
if ($Prerelease.IsPresent) {
$vpkArgs += "--pre"
}
if ($Merge.IsPresent) {
$vpkArgs += "--merge"
}
if ($PSCmdlet.ShouldProcess("$GiteaRepoUrl@$tag", "Upload Velopack release")) {
& "dotnet" @vpkArgs
if ($LASTEXITCODE -ne 0) {
throw "vpk upload failed with exit code $LASTEXITCODE"
}
}
Write-Host ""
Write-Host "Velopack feed upload complete." -ForegroundColor Green
Write-Host "Version: $($localVersion.ToString())"
Write-Host "Tag: $tag"
Write-Host "Channel: $Channel"
Write-Host "ReleaseDir: $releaseDir"
Write-Host "Release: $releaseUrl"
Write-Host "Prerelease: $(if ($Prerelease.IsPresent) { 'yes' } else { 'no' })"
-67
View File
@@ -1,67 +0,0 @@
[CmdletBinding(DefaultParameterSetName = "Patch")]
param(
[Parameter(Mandatory = $true, ParameterSetName = "Major")]
[switch]$Major,
[Parameter(Mandatory = $true, ParameterSetName = "Minor")]
[switch]$Minor,
[Parameter(Mandatory = $true, ParameterSetName = "Patch")]
[switch]$Patch,
[string]$Project = "src/RgbLedStripController/RgbLedStripController.csproj"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\.."))
$projectPath = if ([System.IO.Path]::IsPathRooted($Project)) {
$Project
} else {
Join-Path $repoRoot $Project
}
$projectPath = [System.IO.Path]::GetFullPath($projectPath)
if (-not (Test-Path -LiteralPath $projectPath)) {
throw "Project file not found: $projectPath"
}
$content = Get-Content -Raw -LiteralPath $projectPath
$versionMatch = [System.Text.RegularExpressions.Regex]::Match($content, "<Version>(?<value>\d+\.\d+\.\d+)</Version>")
if (-not $versionMatch.Success) {
throw "No semantic <Version> value found in project file: $projectPath"
}
$currentVersion = $versionMatch.Groups["value"].Value
$parts = $currentVersion.Split(".") | ForEach-Object { [int]$_ }
$newVersion = switch ($PSCmdlet.ParameterSetName) {
"Major" { "{0}.0.0" -f ($parts[0] + 1) }
"Minor" { "{0}.{1}.0" -f $parts[0], ($parts[1] + 1) }
default { "{0}.{1}.{2}" -f $parts[0], $parts[1], ($parts[2] + 1) }
}
$newAssemblyVersion = "$newVersion.0"
function Replace-FirstMatch {
param(
[string]$Text,
[string]$Pattern,
[string]$Replacement
)
$regex = [System.Text.RegularExpressions.Regex]::new($Pattern)
return $regex.Replace($Text, $Replacement, 1)
}
$content = Replace-FirstMatch $content "<Version>[^<]+</Version>" "<Version>$newVersion</Version>"
$content = Replace-FirstMatch $content "<AssemblyVersion>[^<]+</AssemblyVersion>" "<AssemblyVersion>$newAssemblyVersion</AssemblyVersion>"
$content = Replace-FirstMatch $content "<FileVersion>[^<]+</FileVersion>" "<FileVersion>$newAssemblyVersion</FileVersion>"
$content = Replace-FirstMatch $content "<InformationalVersion>[^<]+</InformationalVersion>" "<InformationalVersion>$newVersion</InformationalVersion>"
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($projectPath, $content, $utf8NoBom)
Write-Host ("Version updated: {0} -> {1}" -f $currentVersion, $newVersion) -ForegroundColor Green
Write-Host ("Project file: {0}" -f $projectPath)