From 6c3e1e986b307abc2af26c1ebcdfdc2e6163e767 Mon Sep 17 00:00:00 2001 From: Raul Avila Catala Date: Thu, 20 Aug 2026 07:39:18 -0600 Subject: [PATCH 1/2] fix: place generator DLL in analyzers/dotnet/cs for NuGet (#12) --- .../EasyWindowsApplication.Generators.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/EasyWindowsApplication.Generators/EasyWindowsApplication.Generators.csproj b/src/EasyWindowsApplication.Generators/EasyWindowsApplication.Generators.csproj index 49c02e1..dafac34 100644 --- a/src/EasyWindowsApplication.Generators/EasyWindowsApplication.Generators.csproj +++ b/src/EasyWindowsApplication.Generators/EasyWindowsApplication.Generators.csproj @@ -9,6 +9,7 @@ $(NoWarn);RS2008 true true + false True EasyWindowsApplication.Generators @@ -42,4 +43,8 @@ + + + + From 46bdaca21cc2b894f303dc4a6547920eae46da95 Mon Sep 17 00:00:00 2001 From: Raul Avila Catala Date: Thu, 20 Aug 2026 07:39:23 -0600 Subject: [PATCH 2/2] feat: switch templates to NuGet package references and add template pack (#11) --- .github/pull_request_template.md | 1 + .github/workflows/dev-release.yml | 42 ++- .github/workflows/prod-release.yml | 47 ++- .gitignore | 3 + CONTRIBUTING.md | 14 +- README.md | 15 +- devtools/install-templates.ps1 | 290 ++++++++++++++++++ .../EasyWinApp/.template.config/template.json | 32 ++ .../EasyWinApp/EasyWinApp.csproj | 4 +- .../logo_projecttemplates_easywinapp.png | Bin 56 -> 8318 bytes src/ProjectTemplates/ProjectTemplates.csproj | 28 ++ .../.template.config/template.json | 32 ++ .../SimpleEasyWinApp/SimpleEasyWinApp.csproj | 4 +- .../logo_projecttemplates_easywinapp.png | Bin 56 -> 8318 bytes src/Sample/Program.cs | 2 +- 15 files changed, 497 insertions(+), 17 deletions(-) create mode 100644 devtools/install-templates.ps1 mode change 120000 => 100644 src/ProjectTemplates/EasyWinApp/logo_projecttemplates_easywinapp.png create mode 100644 src/ProjectTemplates/ProjectTemplates.csproj mode change 120000 => 100644 src/ProjectTemplates/SimpleEasyWinApp/logo_projecttemplates_easywinapp.png diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b3f7b56..096b7de 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,6 +2,7 @@ ver(core): x.x.x-preview ver(gen): x.x.x-preview +ver(template): x.x.x-preview ## Notes: diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 900d72f..de0a8c8 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -53,8 +53,16 @@ jobs: Write-Host "ver(gen) no encontrado - se omitira paquete Generators" } + $templateVersion = $null + if ($prBody -match '(?i)ver\s*\(\s*template\s*\)\s*:\s*(\d+\.\d+\.\d+-preview)') { + $templateVersion = $matches[1] + Write-Host "ver(template): $templateVersion" + } else { + Write-Host "ver(template) no encontrado - se omitira paquete Templates" + } + # 2. Validar que al menos uno fue encontrado - if (-not $coreVersion -and -not $genVersion) { + if (-not $coreVersion -and -not $genVersion -and -not $templateVersion) { Write-Host "" Write-Host "No se encontro ninguna version valida. Workflow terminado." echo "::warning::No se encontraron versiones en el PR body" @@ -62,8 +70,8 @@ jobs: exit 0 } - # 3. Determinar version del Release (prioridad: Core > Generators) - $releaseVersion = if ($coreVersion) { $coreVersion } else { $genVersion } + # 3. Determinar version del Release (prioridad: Core > Generators > Templates) + $releaseVersion = if ($coreVersion) { $coreVersion } else { if ($genVersion) { $genVersion } else { $templateVersion } } # 4. Construir cuerpo del Release en Markdown $bodyMsg = "## Preview Release`n`nEsta es una version preview automatica desde development.`n`n### Paquetes publicados" @@ -74,6 +82,9 @@ jobs: if ($genVersion) { $bodyMsg += "`n- **RandAMediaLabGroup.EasyWindowsApplication.Generators** ``$genVersion``" } + if ($templateVersion) { + $bodyMsg += "`n- **RandAMediaLabGroup.EasyWindowsApplication.Templates** ``$templateVersion``" + } $bodyMsg += "`n`n### PR Original`n$($env:PR_URL)" @@ -89,17 +100,21 @@ jobs: # Solo exportar versiones que existen para evitar en .csproj if ($coreVersion) { echo "CORE_VERSION=$coreVersion" >> $env:GITHUB_OUTPUT } if ($genVersion) { echo "GEN_VERSION=$genVersion" >> $env:GITHUB_OUTPUT } + if ($templateVersion) { echo "TEMPLATE_VERSION=$templateVersion" >> $env:GITHUB_OUTPUT } $hasCore = if ($coreVersion) { 'true' } else { 'false' } $hasGen = if ($genVersion) { 'true' } else { 'false' } + $hasTemplate = if ($templateVersion) { 'true' } else { 'false' } echo "HAS_CORE=$hasCore" >> $env:GITHUB_OUTPUT echo "HAS_GEN=$hasGen" >> $env:GITHUB_OUTPUT + echo "HAS_TEMPLATE=$hasTemplate" >> $env:GITHUB_OUTPUT Write-Host "" Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" Write-Host "RELEASE_VERSION : $releaseVersion" Write-Host "HAS_CORE : $hasCore" Write-Host "HAS_GEN : $hasGen" + Write-Host "HAS_TEMPLATE : $hasTemplate" Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - name: Update project versions @@ -128,6 +143,15 @@ jobs: Write-Host "Generators omitido (no hay version)" } + if ("${{ steps.extract_version.outputs.HAS_TEMPLATE }}" -eq "true") { + $version = "${{ steps.extract_version.outputs.TEMPLATE_VERSION }}" + $projFile = "src/ProjectTemplates/ProjectTemplates.csproj" + (Get-Content $projFile) -replace '.*', "$version" | Set-Content $projFile + Write-Host "Templates actualizado a: $version" + } else { + Write-Host "Templates omitido (no hay version)" + } + Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - name: Setup .NET 10 @@ -168,6 +192,18 @@ jobs: dotnet pack src/EasyWindowsApplication.Generators/EasyWindowsApplication.Generators.csproj --configuration Release --no-build --output ./nupkgs if ($LASTEXITCODE -ne 0) { Write-Error "Pack fallido"; exit 1 } + - name: Build & Pack Templates package + if: steps.extract_version.outputs.SKIP_WORKFLOW != 'true' && steps.extract_version.outputs.HAS_TEMPLATE == 'true' + shell: pwsh + run: | + Write-Host "Restaurando Templates..." + dotnet restore src/ProjectTemplates/ProjectTemplates.csproj + if ($LASTEXITCODE -ne 0) { Write-Error "Restore fallido"; exit 1 } + + Write-Host "Empaquetando Templates..." + dotnet pack src/ProjectTemplates/ProjectTemplates.csproj --configuration Release --no-restore --output ./nupkgs + if ($LASTEXITCODE -ne 0) { Write-Error "Pack fallido"; exit 1 } + - name: List generated packages if: steps.extract_version.outputs.SKIP_WORKFLOW != 'true' shell: pwsh diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml index be7e0a0..fe0e5fc 100644 --- a/.github/workflows/prod-release.yml +++ b/.github/workflows/prod-release.yml @@ -61,8 +61,20 @@ jobs: Write-Host "ver(gen) no encontrado - se omitira paquete Generators" } + $templatePreview = $null + $templateStable = $null + if ($prBody -match '(?i)ver\s*\(\s*template\s*\)\s*:\s*(\d+\.\d+\.\d+)-preview') { + $templatePreview = $matches[1] + "-preview" + $templateStable = $matches[1] + Write-Host "ver(template) encontrado" + Write-Host " Preview: $templatePreview" + Write-Host " Stable : $templateStable" + } else { + Write-Host "ver(template) no encontrado - se omitira paquete Templates" + } + # 2. Validar que al menos uno fue encontrado - if (-not $coreStable -and -not $genStable) { + if (-not $coreStable -and -not $genStable -and -not $templateStable) { Write-Host "" Write-Host "No se encontro ninguna version valida. Workflow terminado." echo "::warning::No se encontraron versiones en el PR body" @@ -70,8 +82,8 @@ jobs: exit 0 } - # 3. Determinar version del Release (prioridad: Core > Generators) - $releaseVersion = if ($coreStable) { $coreStable } else { $genStable } + # 3. Determinar version del Release (prioridad: Core > Generators > Templates) + $releaseVersion = if ($coreStable) { $coreStable } else { if ($genStable) { $genStable } else { $templateStable } } # 4. Construir cuerpo del Release en Markdown $bodyMsg = "## Stable Release`n`nEsta es una version estable promocionada desde preview.`n`n### Paquetes publicados" @@ -82,6 +94,9 @@ jobs: if ($genStable) { $bodyMsg += "`n- **RandAMediaLabGroup.EasyWindowsApplication.Generators** ``$genStable`` (desde ``$genPreview``)" } + if ($templateStable) { + $bodyMsg += "`n- **RandAMediaLabGroup.EasyWindowsApplication.Templates** ``$templateStable`` (desde ``$templatePreview``)" + } $bodyMsg += "`n`n### PR Original`n$($env:PR_URL)" $bodyMsg += "`n`n### Cambios`nVer el Pull Request para detalles completos de los cambios incluidos en esta version." @@ -100,17 +115,22 @@ jobs: if ($corePreview) { echo "CORE_PREVIEW=$corePreview" >> $env:GITHUB_OUTPUT } if ($genStable) { echo "GEN_STABLE=$genStable" >> $env:GITHUB_OUTPUT } if ($genPreview) { echo "GEN_PREVIEW=$genPreview" >> $env:GITHUB_OUTPUT } + if ($templateStable) { echo "TEMPLATE_STABLE=$templateStable" >> $env:GITHUB_OUTPUT } + if ($templatePreview) { echo "TEMPLATE_PREVIEW=$templatePreview" >> $env:GITHUB_OUTPUT } $hasCore = if ($coreStable) { 'true' } else { 'false' } $hasGen = if ($genStable) { 'true' } else { 'false' } + $hasTemplate = if ($templateStable) { 'true' } else { 'false' } echo "HAS_CORE=$hasCore" >> $env:GITHUB_OUTPUT echo "HAS_GEN=$hasGen" >> $env:GITHUB_OUTPUT + echo "HAS_TEMPLATE=$hasTemplate" >> $env:GITHUB_OUTPUT Write-Host "" Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" Write-Host "RELEASE_VERSION : $releaseVersion" Write-Host "HAS_CORE : $hasCore" Write-Host "HAS_GEN : $hasGen" + Write-Host "HAS_TEMPLATE : $hasTemplate" Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - name: Update project versions @@ -139,6 +159,15 @@ jobs: Write-Host "Generators omitido (no hay version)" } + if ("${{ steps.extract_version.outputs.HAS_TEMPLATE }}" -eq "true") { + $version = "${{ steps.extract_version.outputs.TEMPLATE_STABLE }}" + $projFile = "src/ProjectTemplates/ProjectTemplates.csproj" + (Get-Content $projFile) -replace '.*', "$version" | Set-Content $projFile + Write-Host "Templates actualizado a: $version (STABLE)" + } else { + Write-Host "Templates omitido (no hay version)" + } + Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - name: Setup .NET 10 @@ -179,6 +208,18 @@ jobs: dotnet pack src/EasyWindowsApplication.Generators/EasyWindowsApplication.Generators.csproj --configuration Release --no-build --output ./nupkgs if ($LASTEXITCODE -ne 0) { Write-Error "Pack fallido"; exit 1 } + - name: Build & Pack Templates package + if: steps.extract_version.outputs.SKIP_WORKFLOW != 'true' && steps.extract_version.outputs.HAS_TEMPLATE == 'true' + shell: pwsh + run: | + Write-Host "Restaurando Templates..." + dotnet restore src/ProjectTemplates/ProjectTemplates.csproj + if ($LASTEXITCODE -ne 0) { Write-Error "Restore fallido"; exit 1 } + + Write-Host "Empaquetando Templates..." + dotnet pack src/ProjectTemplates/ProjectTemplates.csproj --configuration Release --no-restore --output ./nupkgs + if ($LASTEXITCODE -ne 0) { Write-Error "Pack fallido"; exit 1 } + - name: List generated packages if: steps.extract_version.outputs.SKIP_WORKFLOW != 'true' shell: pwsh diff --git a/.gitignore b/.gitignore index 112db11..dcb79fc 100644 --- a/.gitignore +++ b/.gitignore @@ -433,3 +433,6 @@ src/ProjectTemplates/**/*.ico # Plan files plan-*.md + +# OpenCode .ignore file +.ignore diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a35f0c7..31890bc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,6 +49,16 @@ dotnet build src\EasyWindowsApplication\EasyWindowsApplication.csproj dotnet run --project src\ProjectTemplates\SimpleEasyWinApp\SimpleEasyWinApp.csproj ``` +### Plantillas (desarrollo) + +Para reinstalar o desinstalar las plantillas sin recordar comandos, ejecuta el gestor interactivo: + +```bash +.\devtools\install-templates.ps1 +``` + +El menú permite reinstalar desde el repositorio (`src\ProjectTemplates`) o desde un paquete `.nupkg` local, desinstalar, y salir. + --- ## Arquitectura de módulos @@ -181,7 +191,7 @@ Todo trabajo (funcionalidad, ajuste, bugfix) comienza con un **issue** creado co git checkout {número}-{slug} ``` 4. **Trabaja y haz push** con commits en Conventional Commits (`feat:`, `fix:`, ...). -5. **Abre un Pull Request contra `develop`** e incluye en el body los marcadores de versión (`ver(core)` / `ver(gen)`) según la sección [CI/CD](#cicd). +5. **Abre un Pull Request contra `develop`** e incluye en el body los marcadores de versión (`ver(core)` / `ver(gen)` / `ver(template)`) según la sección [CI/CD](#cicd). 6. **Mergea a `develop`** → `dev-release.yml` publica el preview automáticamente. Los usuarios prueban y dan feedback, que se canaliza en nuevos issues. Reglas: @@ -217,9 +227,11 @@ Este repositorio publica paquetes NuGet automáticamente mediante GitHub Actions ```text ver(core): 0.1.0-preview ver(gen): 0.1.0-preview + ver(template): 0.1.0-preview ``` - `core` → paquete `RandAMediaLabGroup.EasyWindowsApplication` - `gen` → paquete `RandAMediaLabGroup.EasyWindowsApplication.Generators` + - `template` → paquete `RandAMediaLabGroup.EasyWindowsApplication.Templates` (template pack `easywinapp` / `simpleeasywinapp`) 3. **Al *mergear*** a `develop`, el workflow `dev-release.yml` publica automáticamente el preview a NuGet y crea un *GitHub Release* con tag `v0.1.0-preview`. 4. **Para promover a estable**, abre un PR de `develop` → `main` con el **mismo** *body* (marcadores con `-preview`). Al *mergear*, `prod-release.yml` publica la versión estable y elimina el `-preview` del tag. diff --git a/README.md b/README.md index 2308226..2bb4b70 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,15 @@ El framework incluye **dos plantillas** oficiales: dotnet build src\EasyWindowsApplication.slnx ``` -2. **Instala las plantillas localmente:** +2. **Instala las plantillas:** + + **Opción A — Paquete NuGet (recomendado para usuarios):** + ```bash + dotnet new install RandAMediaLabGroup.EasyWindowsApplication.Templates + ``` + El paquete incluye ambas plantillas (`easywinapp` y `simpleeasywinapp`) y los proyectos que genera dependen de los **paquetes NuGet** del framework (`RandAMediaLabGroup.EasyWindowsApplication` y `RandAMediaLabGroup.EasyWindowsApplication.Generators`) en su última versión publicada (preview o estable). + + **Opción B — Desde la carpeta del repositorio (desarrollo):** ```bash dotnet new install src\ProjectTemplates\EasyWinApp dotnet new install src\ProjectTemplates\SimpleEasyWinApp @@ -84,6 +92,8 @@ El framework incluye **dos plantillas** oficiales: **Desintalaion de plantillas** ```bash + dotnet new uninstall RandAMediaLabGroup.EasyWindowsApplication.Templates + dotnet new uninstall src\ProjectTemplates\EasyWinApp dotnet new uninstall src\ProjectTemplates\SimpleEasyWinApp @@ -109,8 +119,7 @@ El framework incluye **dos plantillas** oficiales: 1. **Instala las plantillas** (una vez): ```bash - dotnet new install src\ProjectTemplates\EasyWinApp - dotnet new install src\ProjectTemplates\SimpleEasyWinApp + dotnet new install RandAMediaLabGroup.EasyWindowsApplication.Templates ``` 2. **Archivo → Nuevo → Proyecto → Busca "Easy Win App" o "Simple Easy Win App" → Siguiente → Crear.** diff --git a/devtools/install-templates.ps1 b/devtools/install-templates.ps1 new file mode 100644 index 0000000..255f8c2 --- /dev/null +++ b/devtools/install-templates.ps1 @@ -0,0 +1,290 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Gestor interactivo de plantillas de Easy Windows Application. + +.DESCRIPTION + Permite reinstalar o desinstalar las plantillas 'easywinapp' y 'simpleeasywinapp' + sin tener que recordar comandos. Al ejecutarlo se muestra un menu interactivo: + + 1. Reinstalar desde el repositorio (src\ProjectTemplates) + 2. Reinstalar desde un paquete .nupkg local + 3. Desinstalar + + Salir: presiona [Esc] + + Uso: + .\devtools\install-templates.ps1 +#> + +[CmdletBinding()] +param() + +# ================= Configuracion ================= +$RepoRoot = Split-Path -Parent $PSScriptRoot +$TemplatesFolder = [IO.Path]::GetFullPath((Join-Path $RepoRoot 'src\ProjectTemplates')).TrimEnd('\') + +function Test-DotNet { + return $null -ne (Get-Command dotnet -ErrorAction SilentlyContinue) +} + +function Get-InstallationStatus { + param() + + $status = @{ + Installed = $false + Source = 'none' + RepoSource = $null + PkgSources = @() + Templates = @() + TemplateIds = @() + } + + $output = (& dotnet new uninstall 2>&1 | Out-String) + $currentSource = $null + + foreach ($line in ($output -split "\r?\n")) { + if ($line -match '^\s{3}\S') { + $currentSource = $line.Trim() + } + elseif ($line -match '^\s{6,}\S' -and $line -match 'easywinapp|simpleeasywinapp') { + $status.Templates += $line.Trim() + if ($line -match '\((easywinapp|simpleeasywinapp)\)') { + $id = $Matches[1] + if ($id -notin $status.TemplateIds) { $status.TemplateIds += $id } + } + if ($null -ne $currentSource) { + $srcFull = '' + try { $srcFull = [IO.Path]::GetFullPath($currentSource).TrimEnd('\') } catch { } + if ($srcFull -ieq $TemplatesFolder) { + $status.RepoSource = $currentSource + } + elseif ($currentSource -notin $status.PkgSources) { + $status.PkgSources += $currentSource + } + } + } + } + + if ($status.Templates.Count -gt 0) { + $status.Installed = $true + $status.Source = if ($status.RepoSource) { 'repo' } else { 'pkg' } + } + return $status +} + +function Show-Header { + Clear-Host + Write-Host '' + Write-Host '====================================================' -ForegroundColor Cyan + Write-Host ' Easy Windows Application - Gestor de Plantillas' -ForegroundColor Cyan + Write-Host '====================================================' -ForegroundColor Cyan + Write-Host '' +} + +function Show-Status { + param($Status) + + if ($Status.Installed) { + $srcLabel = if ($Status.Source -eq 'repo') { 'desde repositorio' } else { 'desde paquete NuGet' } + Write-Host (" Estado : [OK] Plantillas instaladas ({0})" -f $srcLabel) -ForegroundColor Green + if ($Status.TemplateIds.Count -gt 0) { + Write-Host (" templates: {0}" -f ($Status.TemplateIds -join ', ')) -ForegroundColor Green + } + } + else { + Write-Host ' Estado : [--] No hay plantillas instaladas' -ForegroundColor Yellow + } + Write-Host '' +} + +function Show-MainMenu { + Write-Host ' Que deseas hacer?' + Write-Host '' + Write-Host ' Preciona [1] para Reinstalar desde repositorio' + Write-Host ' Preciona [2] para Reinstalar desde paquete NuGet' + Write-Host ' Preciona [3] para Desinstalar' + Write-Host '' + Write-Host ' Presiona [Esc] para salir' + Write-Host '' + + $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") + switch ($key.VirtualKeyCode) { + 27 { return 'quit' } + 49 { return '1' } + 50 { return '2' } + 51 { return '3' } + default { return 'invalid' } + } +} + +function Show-Confirm { + param([string]$Action) + + Write-Host '' + Write-Host (" Continuar con '{0}'?" -f $Action) -ForegroundColor Yellow + $resp = Read-Host ' [S/n]' + return ($resp.Trim().ToLower() -ne 'n') +} + +function Wait-BackToMenu { + Write-Host ' Presiona [Enter] para volver al menu o [Esc] para salir.' + $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") + if ($key.VirtualKeyCode -eq 27) { exit } + Write-Host '' +} + +function Invoke-Uninstall { + $status = Get-InstallationStatus + if (-not $status.Installed) { + Write-Host ' No hay plantillas instaladas.' -ForegroundColor Yellow + return + } + + $targets = @() + if ($status.RepoSource) { $targets += $status.RepoSource } + $targets += $status.PkgSources + + foreach ($t in $targets) { + Write-Host (" Desinstalando: {0}" -f $t) -ForegroundColor DarkGray + & dotnet new uninstall $t | Out-Null + if ($LASTEXITCODE -ne 0) { + Write-Host (" [ERROR] Fallo al desinstalar: {0}" -f $t) -ForegroundColor Red + } + } +} + +function Install-FromRepo { + $status = Get-InstallationStatus + if ($status.Installed) { + if (-not (Show-Confirm 'Reinstalar desde repositorio')) { return } + Invoke-Uninstall + } + + Write-Host '' + Write-Host (" Instalando desde: {0}" -f $TemplatesFolder) -ForegroundColor DarkGray + & dotnet new install $TemplatesFolder + if ($LASTEXITCODE -eq 0) { + Write-Host '' + Write-Host ' [OK] Plantillas instaladas desde el repositorio.' -ForegroundColor Green + } + else { + Write-Host '' + Write-Host ' [ERROR] Fallo al instalar desde el repositorio.' -ForegroundColor Red + } + Wait-BackToMenu +} + +function Install-FromPackage { + $status = Get-InstallationStatus + if ($status.Installed) { + if (-not (Show-Confirm 'Reinstalar desde paquete NuGet')) { return } + Invoke-Uninstall + } + + $nupkgBase = Join-Path $TemplatesFolder 'bin' + $files = @(Get-ChildItem -Path $nupkgBase -Filter '*.nupkg' -Recurse -ErrorAction SilentlyContinue | + Where-Object { $_.Name -notmatch '\.(symbols|snupkg)\.nupkg$' }) + + $pkg = $null + + if ($files.Count -eq 0) { + Write-Host '' + Write-Host ' No se encontraron paquetes .nupkg en src\ProjectTemplates\bin' -ForegroundColor Yellow + Write-Host '' + Write-Host ' [1] Escribir ruta manualmente' + Write-Host ' [2] Volver al menu' + Write-Host '' + $c = Read-Host ' Selecciona [1-2]' + if ($c.Trim() -eq '1') { + $pkg = (Read-Host ' Ruta del paquete .nupkg').Trim().Trim('"') + if (-not (Test-Path -LiteralPath $pkg)) { + Write-Host ' [ERROR] La ruta no existe.' -ForegroundColor Red + Wait-BackToMenu + return + } + } + else { + return + } + } + elseif ($files.Count -eq 1) { + $pkg = $files[0].FullName + } + else { + Write-Host '' + Write-Host ' Paquetes encontrados:' + for ($i = 0; $i -lt $files.Count; $i++) { + Write-Host (" [{0}] {1}" -f ($i + 1), $files[$i].Name) + } + Write-Host '' + $c = Read-Host (" Selecciona paquete [1-{0}]" -f $files.Count) + $idx = 0 + if ([int]::TryParse($c.Trim(), [ref]$idx) -and $idx -ge 1 -and $idx -le $files.Count) { + $pkg = $files[$idx - 1].FullName + } + else { + Write-Host ' [ERROR] Seleccion no valida.' -ForegroundColor Red + Wait-BackToMenu + return + } + } + + if ($null -ne $pkg -and $pkg -ne '') { + Write-Host '' + Write-Host (" Instalando desde: {0}" -f $pkg) -ForegroundColor DarkGray + & dotnet new install $pkg + if ($LASTEXITCODE -eq 0) { + Write-Host '' + Write-Host ' [OK] Plantillas instaladas desde el paquete.' -ForegroundColor Green + } + else { + Write-Host '' + Write-Host ' [ERROR] Fallo al instalar desde el paquete.' -ForegroundColor Red + } + } + Wait-BackToMenu +} + +function Uninstall-Templates { + $status = Get-InstallationStatus + if (-not $status.Installed) { + Write-Host ' No hay plantillas instaladas.' -ForegroundColor Yellow + Wait-BackToMenu + return + } + if (-not (Show-Confirm 'Desinstalar')) { return } + Invoke-Uninstall + Write-Host '' + Write-Host ' [OK] Plantillas desinstaladas.' -ForegroundColor Green + Wait-BackToMenu +} + +# ================= Ejecucion ================= +if (-not (Test-DotNet)) { + Write-Host '' + Write-Host ' [ERROR] dotnet no encontrado en el PATH.' -ForegroundColor Red + Write-Host ' Instala el .NET SDK y vuelve a intentarlo.' + Write-Host '' + exit 1 +} + +$running = $true +while ($running) { + Show-Header + $status = Get-InstallationStatus + Show-Status $status + $choice = Show-MainMenu + + switch ($choice) { + '1' { Install-FromRepo } + '2' { Install-FromPackage } + '3' { Uninstall-Templates } + 'quit' { $running = $false } + default { + Write-Host '' + Write-Host ' [ERROR] Opcion no valida.' -ForegroundColor Red + Start-Sleep -Milliseconds 900 + } + } +} diff --git a/src/ProjectTemplates/EasyWinApp/.template.config/template.json b/src/ProjectTemplates/EasyWinApp/.template.config/template.json index 62b7458..a42bfa3 100644 --- a/src/ProjectTemplates/EasyWinApp/.template.config/template.json +++ b/src/ProjectTemplates/EasyWinApp/.template.config/template.json @@ -25,6 +25,38 @@ "datatype": "bool", "defaultValue": "true", "description": "Mantiene el Layout en secciones (Resources/Layout/Behavior). Si se desactiva (--Simple false), descompone el Layout en Views/Controls. Experimental hasta el rework sem\u00E1ntico del generador." + }, + "ProjectReferenceCore": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "" + }, + "replaces": "" + }, + "ProjectReferenceGenerators": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "" + }, + "replaces": "" + }, + "EasyWAProj": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "" + }, + "replaces": "<_EasyWAProj>$(MSBuildProjectDirectory)\\..\\..\\EasyWindowsApplication\\EasyWindowsApplication.csproj" + }, + "IcoGenReference": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "<PackageReference Include="RandAMediaLabGroup.EasyWindowsApplication" Version="%2A-%2A" />" + }, + "replaces": "<ProjectReference Include="$(_EasyWAProj)" />" } }, "sources": [ diff --git a/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj b/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj index c986b96..0c0e996 100644 --- a/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj +++ b/src/ProjectTemplates/EasyWinApp/EasyWinApp.csproj @@ -15,9 +15,7 @@ - + diff --git a/src/ProjectTemplates/EasyWinApp/logo_projecttemplates_easywinapp.png b/src/ProjectTemplates/EasyWinApp/logo_projecttemplates_easywinapp.png deleted file mode 120000 index 74db9ef..0000000 --- a/src/ProjectTemplates/EasyWinApp/logo_projecttemplates_easywinapp.png +++ /dev/null @@ -1 +0,0 @@ -../../../rsc/Images/logo_projecttemplates_easywinapp.png \ No newline at end of file diff --git a/src/ProjectTemplates/EasyWinApp/logo_projecttemplates_easywinapp.png b/src/ProjectTemplates/EasyWinApp/logo_projecttemplates_easywinapp.png new file mode 100644 index 0000000000000000000000000000000000000000..0f5f605238f882573a10e1738c5a8de30a018d9e GIT binary patch literal 8318 zcmaKRWmFwO)8=rI;JG-#Ex0=uCs=TIy|@$HJqZ%roj`DRcL?qtoZ!JV_>ynm{Z@W# zpVQM_)zwwi&*^^7^h}tNf)pAu0WtspXdk7;RbJbGe-#no_1PE!6e=J@lK?+M37e@#S%fCF`IsRqH z!p6tP$HL0a!p_e0YQf~}Y42j}!DR1D`5y=3X3i!~R*o)K4)$dK92!F%TwMeyTwSe9 zA?C*BTu@U}E+(ikhba>qo2fCAu_*@|lPL#>DGwJLFS|J#l;S_(U98OicYJ&2|G@AH z2+O|_7B*(qe<}a#Llm8?%wDtj7gC5#;J?QIFP#9(zeN6z#DxB*=5?*Fz5hn$A58y^ zu9^KSr0%cSF(#y-004>ZM{!X#4};@O&ln=j%su~8GYxZfvw2?^byru-Qioz!*`&9b z9aOsbRmMi-0oO*SqgAz|2vS{eeL!!E!c=`%#)}Q4vngdVYr{pfuwQ&0WD1pdG3I??>XD!z zL%X{&E+kP zJ(G74Rq5?@dDi*ZxU;fGSUDU$n@Ya9`xoAj5lN3r&HW&?um4IOBhHr1W6C%^bc z*bbu6oi7mF$oU1ZcSMC**}w-ib+f@a2Ad#mAPj)J(4xo#2#G!bJiPzQfYF-?kUBTG zu)AnOE~&NjkZLV-ndRz4LqQF#*81*3)%kyXn+y$k-RoEEWZv^L<74y zF|f$=@YVtf8eq6k0E7OWwKZAF+@7 zQ@#Q!Luc~Xv!1JDl*YnUTLge5<3*A$s#rZ{SoauE*~?id zLWnZMJL7?F(hZP?g-0Xb&LDQ4PGtto(d9#w$RhCJks&dj3oKj!3 zZF4FhhC~d5sw_fACK5K?JzdHC$r3Aq`!Kfin@^;Q7YA7pg$M*#1OQkVISdK>vt(-g zYbr-}dj)S85;CQRaHm-H2zK@KV3sbJkmDBVKxoL+MQc;%fKnzsYVQ@$Wq~1qefIe` z1rBlo3^EAh!g$}mFO@!HFiME|N{*nAz;W(pY^7mk@3YbGYj%S{>-tkyo>H;+A%S3- zX_G6g=;yh=%Nf)+S(2IK5noxwLcs5JbF%NIK7@(?{cT*q7uEeo?s>+v{hCFGrz+(M zAt%*Pm@Ef-U#`><4ZaHmFCGZZ34nI=1k+^c-iC3v?kFOM&u0+fhvbMNW~2sPcmACn zEc{^b9DF_e`0@QHnbIFPXnZ7-uy;5g-XZnRk9iCtmvvFPXAlH7Ll`(0H}e>k91aDo zT!4AqmIVh53{#Y>hBh4exqNyu^yhwvO3?@=Onyt16@4s{B%G42;MG-Cq8b+rVkk0cqV(2E>=rE7if2J?a~| ztq4vqRHTHrtNso1=G*1s2CJ=C|w)nk^kozex2F}z=vqDrUG)i zpCfnJ_5;9x%`W(?;4>P`Ry7+|Q!b+}(1MN%7JMti1RJU2lvO;3ZFzxxR#TunsA)~5 zs`;^I$(sJ-fk>V!n#~61YnX5ehbtVKATU|k?`WJe{t3#4O%ZChHMj{>_LM)Bv-WHf z^2}K~S~EQyzOL^!EQzh-Y8;!o|LB#6<(Q!W%2%IeruIMW!*uW+_Yd0h^J}|cq!<_p zFA~`Ut;@sVKXtlt^_XB7A20ww$?h#X7rALm9EOBAxoj{}eX4viSGO&DMrVy44h(*q z>wmb*9k@(^8hG^*&o#TG3^?!}*F*c^$>=cKjoQSk*R7QP?4JX;h-!wL&|tQC9`uT;y&r|(g5nT1-WDnJLGa%pl37oH6c{5*qg9_ZnS?ZkHhRmR>!6K?3qs(X3tYlJY-;T>R zVpq)58uh~&N%*zZK9J}(ddrqh;GCtg?z&FyxRA#a4|j69dTd$5{g8H1-DbWtpCAs* zVZf|QE!V)Al`Kg~BN=LAzkVE4r03G1w9#=tU+#H$w7a>9`CJE6#Jlw2Dh;KYZvWUS?%(qx~}DN z)bu!MDxrl-mzt2*R(qA$63KH+C}IvXfeBLXgiGRpwz}Q|srsZF-D$@YV0UX8btJ%{ z-D*cm?jVnS6-m@^<^BfLjjKCJkrIsj<_B(R_QIq}qu8wI2MX(M#C2Fi049yEn$KAT z8wmIiP=L!%Jj!j@gqr}ag~z}p(=xEOV{2n|{IbRpeIj@^ z%257i!8KXv`nz|QF>A@=?&6aC`rpm?_BP8v76BJhWCVc$kBG2Twc*(cJsp;Z&-Y6f zHb_Q|urXHQMVjKoCum#rnjUtXD7P;ceUrQ0#c_iAR%A*@Kt6y9N;e9MBpx3lU0js7cgRZ=bsk4t z;~}yMagg`e4A!t}>ac*V7`7IF=&1dzpPz+CrP;r#yXSY=F#fcw`>;;4*19>NN2+&O z^$>XdGS^Y=+_^OB%8vQ92+ekbli#zl_k^E2=~Vcy%5W@mDeVz=eEPlM_3@v_`m8GX z(phznXYuLh#^~&2<%F{nbVGNC*7C7~>hP?v1D)H-sP{hcsgU0|+KaQtaqeZe3-`b5 zbkyy;P-x;CI`8gMiTRLL*M1)ua7ZkQ? zT~Isc8*#?(bzaICLJPNH~@Bj3fVgf)TJ+>;`CG|IZAfKr`u))tzW&hxj#VI7m? z6lDpjL9)XJfQR9-yqE;fB5B?PpBE!LZ>TwKeAh20UaE(WzC{Gqfd92@NSJyU5vh2dI}>$ldy+{@zrzNHdWdvw>v14wI}pK=>FR*L zALP=3YQENYv@}i1L^n-q?om!N*?4oM;tfik6qx!Eebe#nZi@0^V zPt^Rs%Vm@*y~>!VNxTL1{rH=`Ka3p2z!HyY)PBXxzp56iXO+fKU1}-mcNH+OmCvX8 zc~BQ4So&n)tYW1%@3P&vyx9H+l(!0Vq9nm2luVl+WtzWYlcvDV;||X>rN<;d!15`i zzd-Qmxu|R?ICv&_$;DG|Z&Bi&!69F$i%9h|dHd`jE+R$PT&Y;|UUZu|VY*Djor>T- zr?*cEdj?WE>>hQTeGc4OgD+NCe!=;m=`VJ#SVYcGXdGLjMcfAsh^!M^GY!27&U?`OQ;1q($d})AZ;Vi%VvR^R zsFdSNCHj=)HMp=N3MV}%Mkbf2qxz|nRKU>GAS)<+@8g)e-^uel4a{m)oY{!dHJtt;H8^HtvRrE)U&4pJnUypAUWl_c3dHv-^r%BZMF9@n)tch#!f z4!278g-XTu-ilGXavC&>r+S3=bc2b1P3h8vaeZwsiocU)JoY#H3*Wu`?7(;$DqUyD z{3cv(sYR191pV0+IwZD=3fE=KRcwx{ZYH5&DzAN^bY@)z1(6|(#!*bEeUckbyhyzX zV8FQHb8T1o{7MrnI!73VbM^G8OkWFB_*50sv+wm9Bn_#4e31Y0-D8;M4B9%gbtt_@ zU;eu|g-I{$0tdJbtXGl`6qk zjc{Vy12>5)dgq{`5syo$+UGPa|BPtQQ%xl+o;7xrDvDwozL5msuvR+?r8ylE9M@=n zUaRhNv3YM8n6vOv172_lnjaHeZ%s_ARJRfDyql#W2BU>5bh-Hu@Bi3!VgVQU$f~dK z7(jO2_2k`v>iJ~!811TOd`++sADvphDfCCc|9;(x>E6?Auw)y9#a%mqkqwy&_ zn9}BQi2%6{(e(6JSF=kqFGQP!!JPhGjS8_y6A6g6DBeq1CP^*#y@pf}--bME+@O}M zdVT~)>OBHYoIw|&*Dsv{o9kbeR2vFf0&e(OawrlXPuMS$o@2Fsz2$n>vaCb(bv2H5 zDBnsw(PB+Vl12DiE)XEch=^+sQDx5HO@}=9l>SyE{g@nI=!vD!VjF}WBBHO(_a$Iy zc!}91j!VBVF^zF~%=SJo9aP+SYB{mqz@^79tM>QktpWGbDvN>54!)|p2%^+HFHe}L z5U=iA!n&pVC0+0JonsW+Wui|FE9Wc|QAb{zraIf=BtlM7w>irQQYQJ;o3R(N(-L#~ z7#eB-1q;Q;kJD`ebcrSXY7SH}HN18)!`LaN4fxYsmW1D^;n&RaN`pE)iZulPxQCl$ zk{b$UGvyZB(uLC6lZF?H8?lx}!Ci(}4+RP6mzCr%6b!-&`hF$WaNGV?7>3A6d{kHkvhr}Y#2m+^s<1E1~6_4X4@ zL$8;DOA>xXKW9^w$MdNnCYWdovZKRuPw}@u+r)bV$y!?<+MlnV$8=lUx?4M)X}uN{ z4MIIJ2D(UBmvENX_Waza1U-*j9;GSt{eI8PjhIQie%vLa3#E4Y)T~5rs_R=JPPL(7 z)r^caRFp>G$D889wMU@Z!8d7tzjMRYf9f+g^uAx8Ca(l zAJ^PtsXk+qkduExof~j|^oO||NKrvXL`<`yV(zj;t;uC(6hTznQ7o?9eyflnOX_`d zH`N>6&gpk3wZ}0vz*EN`b+S6E1HhdvZ_4GUy)Rt}8rb2GT4XkU(^z@?eM7`?hRLp? zOK{!G4SIT;O=#227!PJh)jaD+nw`Lp;%OpB56B%TXP%z9@o~5^n zk%7<+`Vdd&^S2%Yl@ZoA)<|J~+f;unA0=O`q;xL3#*2tmAXRi=JPN8rFgb%;#49eO zoDk6*2PpsnN;R{%8^cN>*oe^MHl9h+rh^83U;f+Uqb7?zVjGeZf6ZGdP%C8pbPap$ zv>G`ppnuepJq`+!2@F6f&dTIy%^HJT4S#U3e+Zlhtd?9oW*;148gHG%z{Kf0n`*Cmf8b{MrT^Ua4uG@F?zouR>FUjY!b#O;CaLeDt-5ld(dXZ zUx|qNM;K3A)*32Bz`ov_AdnLBHzV99X*_%HEsyhFkxbG>OoPj;Rl(MEztglgH^(Qd zzNZB@C#?e<*Gv(Qgim+JxAe3VASD`d*gd|=5?vv+HzTkXQUM7o!I!n}S1ns2|g z-A*d)bjM-~aPsC0{vC{nzx96}pqVdF!NgtF@G!FZt5>Y?j{L{>mhz|@spy~FS?|U# z&C`+Q%P%HP^0asTg*mn#o+GeyJ05%VLh7&on{5iaf1L-5f-hd5FLM~?Ipp4+s z0?8m~1T;$scKr2`cOz?9_Oo0rN%G!>IKFD85}+R3QUaUldCE&Rsw5LQPY404!-~=N z|DE>rOc51VCPXbFuxyC}=ZY`5&h-V%LiY#`GJ;6_$#?I*W@jBdT}mn1J&r~zls_dK zX1!IQwvm&KKqOsW@NQr+6~Z2)nb=p0WtG&EpYqH>G)?WwQ+)foQrIp0=_K$n>GZ|C z?{aX)q+2R06;beF)MH;tQ6hNat(ms~cf=~y8Ka(7M#oLrB6PAp#TBPSsSrbx^mpx- zOzzTE8;o)>e??u_hc9cjM9aMFX@Wu?wnrlU4k#3rlUD9w_Ss9LeEgK;JM(CQzwMZ| zHj5wshQ$vV%3CmKvx8Tl=#UmrO4=_^jy?Q+Tc#Tc9fc9Eos0?|7hGL2#-D3zQAVyg z94=W#cYPk4dW_1$mNo*qdbs2BqDa3{zCK0P>zDryN)(J`+1tt(`msKbqtx2tc39^; z2sN*o3+Z~Fc2yB0-{I>^dwE8j$x>;*lSMGzmqj|>$w^)1roq~}OxGQVTM3jd6U{w` zmi}7bthjM+DtW@#`&Q3i@^lbaDqJ zWyjhMNjEf||5n!?>FCt@+Qt5?brnX2c04is;P2I9adNyODyhPw%pvByb8E}mI_~+k zJ*xN9F@9>3SGxg`VkBA?1Cij<(c~>ku~YcU+7q*}?jPS(W5t0lISb3Ve{yIFII7j& zd!bW?_%t+d*>xnb2D2(`7pk7G^bM_}zxYrod?CDbd)_nZ>u@tk7oN1*^$^me-NwrA zOI70|sQrYln{J`rXHwgE$+$ql&{u9b`wg5c5K)p5R=}RPrNxLvOhVrQCY2*9UdQyy zZY?#tyyuQC)T}$(>Onis$Yuq|m6W%g1hR#MlRS6o4YQsa+eU{V%{}C)rS^H!GcAJ=MKiwfo?Y!z%xO;=Mzl$; z<*^HKJ6Pw1C8~Fu2tYt`G;2DMZ#(vDGQ zO=qH>h;bB#(A`s})O>bTtoO*?f@(he}EFOk(ximHm70_j!#!X6r$nJ;SeI8{rw$+P_>Iv8z%r^WN;BeoqK;7%S;z|zwb-g6afXs!1N%Xbx+ zQF5KEOj)vIZPr+iu<`4h*9+MwSDsdPoe#NDAUp2Ri%gWZ$9VxrD|N_$ZZzTJ)aV;+ z+&ya~qwim5!YMB(T8ze`QhUu|*$E4m`0yy{EB9Q_ITFschs_w{Uf#Rf*6cQMv}LWU z*cOcD^SxMK6o3P|uX|sAkByt9RW4}Py$zVkmJ}uxoVplFN?aRv`w;)^)Pj32W(h01 zXli`k>CbBKA;(O^{P;r{F4f(JlA)xLFNw`(A50BX;^$<&0@1lJqp zzBqWmnPPHifUW>Pmcd;FRRcGi4gne4 + + + netstandard2.0 + true + false + content + Template + EasyWindowsApplication.Templates + RandAMediaLabGroup.EasyWindowsApplication.Templates + 0.0.0 + cl2raul66 + RandAMediaLabGroup + Plantillas dotnet new (easywinapp / simpleeasywinapp) para el framework EasyWindowsApplication + template; win32; windows-application; easy-windows-application; easywinapp; simpleeasywinapp + MIT + True + https://github.com/cl2raul66/EasyWindowsApplication + https://github.com/cl2raul66/EasyWindowsApplication + + + + + + + + + \ No newline at end of file diff --git a/src/ProjectTemplates/SimpleEasyWinApp/.template.config/template.json b/src/ProjectTemplates/SimpleEasyWinApp/.template.config/template.json index 6aaaf40..63debea 100644 --- a/src/ProjectTemplates/SimpleEasyWinApp/.template.config/template.json +++ b/src/ProjectTemplates/SimpleEasyWinApp/.template.config/template.json @@ -19,6 +19,38 @@ "description": "Target framework", "replaces": "net10.0-windows10.0.19041.0", "defaultValue": "net10.0-windows10.0.19041.0" + }, + "ProjectReferenceCore": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "" + }, + "replaces": "" + }, + "ProjectReferenceGenerators": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "" + }, + "replaces": "" + }, + "EasyWAProj": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "" + }, + "replaces": "<_EasyWAProj>$(MSBuildProjectDirectory)\\..\\..\\EasyWindowsApplication\\EasyWindowsApplication.csproj" + }, + "IcoGenReference": { + "type": "generated", + "generator": "constant", + "parameters": { + "value": "<PackageReference Include="RandAMediaLabGroup.EasyWindowsApplication" Version="%2A-%2A" />" + }, + "replaces": "<ProjectReference Include="$(_EasyWAProj)" />" } }, "sources": [ diff --git a/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj b/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj index 9b1a1a5..7c51e20 100644 --- a/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj +++ b/src/ProjectTemplates/SimpleEasyWinApp/SimpleEasyWinApp.csproj @@ -15,9 +15,7 @@ - + diff --git a/src/ProjectTemplates/SimpleEasyWinApp/logo_projecttemplates_easywinapp.png b/src/ProjectTemplates/SimpleEasyWinApp/logo_projecttemplates_easywinapp.png deleted file mode 120000 index 74db9ef..0000000 --- a/src/ProjectTemplates/SimpleEasyWinApp/logo_projecttemplates_easywinapp.png +++ /dev/null @@ -1 +0,0 @@ -../../../rsc/Images/logo_projecttemplates_easywinapp.png \ No newline at end of file diff --git a/src/ProjectTemplates/SimpleEasyWinApp/logo_projecttemplates_easywinapp.png b/src/ProjectTemplates/SimpleEasyWinApp/logo_projecttemplates_easywinapp.png new file mode 100644 index 0000000000000000000000000000000000000000..0f5f605238f882573a10e1738c5a8de30a018d9e GIT binary patch literal 8318 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