diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 0000000..1a24ad7 --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,223 @@ +name: Auto Release from Issue + +# Trigger when an issue is opened or labeled with 'release' +on: + issues: + types: [opened, labeled] + +permissions: + contents: write + issues: write + +env: + # Only these users can trigger releases (comma-separated GitHub usernames) + ALLOWED_USERS: "mateof" + +jobs: + create-release: + name: Create Release from Issue + runs-on: ubuntu-latest + + # Only run if issue has 'release' label + if: contains(github.event.issue.labels.*.name, 'release') + + steps: + - name: '🔐 Verify user authorization' + env: + ISSUE_AUTHOR: ${{ github.event.issue.user.login }} + run: | + echo "Issue author: $ISSUE_AUTHOR" + echo "Allowed users: $ALLOWED_USERS" + + # Check if author is in allowed list + if [[ ! ",$ALLOWED_USERS," == *",$ISSUE_AUTHOR,"* ]]; then + echo "❌ User '$ISSUE_AUTHOR' is not authorized to create releases" + echo " Only these users can create releases: $ALLOWED_USERS" + exit 1 + fi + + echo "✅ User '$ISSUE_AUTHOR' is authorized" + + - name: '📋 Extract version from issue title' + id: version + env: + ISSUE_TITLE: ${{ github.event.issue.title }} + run: | + echo "Issue title: $ISSUE_TITLE" + + # Extract version from title (supports: v3.5.0, 3.5.0, v3.5.0.0, 3.5.0.0) + VERSION=$(echo "$ISSUE_TITLE" | grep -oE '[vV]?[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1) + + if [ -z "$VERSION" ]; then + echo "❌ No version found in issue title" + echo " Title should contain a version like: v3.5.0 or 3.5.0" + exit 1 + fi + + # Remove 'v' prefix if present + VERSION=${VERSION#v} + VERSION=${VERSION#V} + + # Ensure 4-part version for csproj (3.5.0 -> 3.5.0.0) + PARTS=$(echo "$VERSION" | tr '.' '\n' | wc -l) + if [ "$PARTS" -eq 3 ]; then + VERSION_CSPROJ="${VERSION}.0" + else + VERSION_CSPROJ="$VERSION" + fi + + # Tag version (without trailing .0 if it's 0) + VERSION_TAG="v${VERSION}" + + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "version_csproj=$VERSION_CSPROJ" >> $GITHUB_OUTPUT + echo "version_tag=$VERSION_TAG" >> $GITHUB_OUTPUT + + echo "✅ Extracted versions:" + echo " Version: $VERSION" + echo " CSPROJ: $VERSION_CSPROJ" + echo " Tag: $VERSION_TAG" + + - name: '📄 Checkout main branch' + uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: '🔍 Check if tag already exists' + id: check_tag + run: | + TAG="${{ steps.version.outputs.version_tag }}" + if git rev-parse "$TAG" >/dev/null 2>&1; then + echo "❌ Tag '$TAG' already exists!" + echo "exists=true" >> $GITHUB_OUTPUT + exit 1 + fi + echo "✅ Tag '$TAG' does not exist yet" + echo "exists=false" >> $GITHUB_OUTPUT + + - name: '📝 Update version in TelegramDownloader.csproj' + run: | + VERSION_CSPROJ="${{ steps.version.outputs.version_csproj }}" + CSPROJ_FILE="TelegramDownloader/TelegramDownloader.csproj" + + echo "Updating version in $CSPROJ_FILE to $VERSION_CSPROJ" + + # Update tag + sed -i "s|[^<]*|$VERSION_CSPROJ|g" "$CSPROJ_FILE" + + # Verify the change + echo "Updated content:" + grep -n "Version" "$CSPROJ_FILE" | head -5 + + echo "✅ Version updated to $VERSION_CSPROJ" + + - name: '📝 Update version in TFMAudioApp.csproj (if exists)' + run: | + VERSION_CSPROJ="${{ steps.version.outputs.version_csproj }}" + CSPROJ_FILE="TFMAudioApp/TFMAudioApp.csproj" + + if [ -f "$CSPROJ_FILE" ]; then + echo "Updating version in $CSPROJ_FILE to $VERSION_CSPROJ" + + # Update if exists + if grep -q "ApplicationDisplayVersion" "$CSPROJ_FILE"; then + sed -i "s|[^<]*|${{ steps.version.outputs.version }}|g" "$CSPROJ_FILE" + fi + + # Update if exists + if grep -q "" "$CSPROJ_FILE"; then + sed -i "s|[^<]*|$VERSION_CSPROJ|g" "$CSPROJ_FILE" + fi + + echo "✅ TFMAudioApp version updated" + else + echo "ℹ️ TFMAudioApp.csproj not found, skipping" + fi + + - name: '💾 Commit version changes' + run: | + VERSION="${{ steps.version.outputs.version }}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git add -A + + # Check if there are changes to commit + if git diff --staged --quiet; then + echo "ℹ️ No version changes to commit (version may already be set)" + else + git commit -m "Bump version to $VERSION" + git push origin main + echo "✅ Version changes committed and pushed" + fi + + - name: '🏷️ Create and push tag' + run: | + TAG="${{ steps.version.outputs.version_tag }}" + + git tag -a "$TAG" -m "Release $TAG" + git push origin "$TAG" + + echo "✅ Tag '$TAG' created and pushed" + + - name: '🚀 Create GitHub Release' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${{ steps.version.outputs.version_tag }}" + VERSION="${{ steps.version.outputs.version }}" + ISSUE_BODY="${{ github.event.issue.body }}" + + # Create release with issue body as release notes + gh release create "$TAG" \ + --title "Release $TAG" \ + --notes "## What's Changed + + $ISSUE_BODY + + --- + *Release created automatically from issue #${{ github.event.issue.number }}*" \ + --target main + + echo "✅ GitHub Release created" + + - name: '✅ Close issue with success comment' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${{ steps.version.outputs.version_tag }}" + + gh issue comment ${{ github.event.issue.number }} --body "## ✅ Release Created Successfully! + + - **Version:** $TAG + - **CSPROJ Version:** ${{ steps.version.outputs.version_csproj }} + - **Release:** https://github.com/${{ github.repository }}/releases/tag/$TAG + + The build workflow has been triggered and will upload the binaries shortly. + + --- + *This issue was automatically processed by the release workflow.*" + + gh issue close ${{ github.event.issue.number }} --reason completed + + echo "✅ Issue closed" + + - name: '❌ Comment on failure' + if: failure() + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh issue comment ${{ github.event.issue.number }} --body "## ❌ Release Failed + + The release process encountered an error. Please check the [workflow logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details. + + Common issues: + - Version format incorrect (use: v3.5.0 or 3.5.0) + - Tag already exists + - User not authorized + + --- + *This comment was automatically generated.*" diff --git a/.github/workflows/buildrelease.yml b/.github/workflows/buildrelease.yml index 9f89f37..ee44526 100644 --- a/.github/workflows/buildrelease.yml +++ b/.github/workflows/buildrelease.yml @@ -56,6 +56,8 @@ jobs: steps: - name: '📄 Checkout' uses: actions/checkout@v4 + with: + submodules: recursive - name: '🔧 Setup .NET 10' uses: actions/setup-dotnet@v4 diff --git a/TelegramDownloader/Dockerfile b/TelegramDownloader/Dockerfile index 9c7c800..6fb01b6 100644 --- a/TelegramDownloader/Dockerfile +++ b/TelegramDownloader/Dockerfile @@ -51,4 +51,4 @@ RUN python3 -m venv /app/venv && \ ENV PATH="/app/venv/bin:${PATH}" COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "TelegramDownloader.dll"] +ENTRYPOINT ["dotnet", "TelegramDownloader.dll"] \ No newline at end of file diff --git a/TelegramDownloader/Shared/Ddtree.razor b/TelegramDownloader/Shared/Ddtree.razor index 6a69ceb..7774d7e 100644 --- a/TelegramDownloader/Shared/Ddtree.razor +++ b/TelegramDownloader/Shared/Ddtree.razor @@ -86,7 +86,8 @@ } } - return Task.FromResult(result); + // Sort alphabetically ascending + return Task.FromResult(result.OrderBy(x => x.Name).ToList()); } private async Task> LoadTelegramFolders(string dbName, string parentId) @@ -115,7 +116,8 @@ HasChildren = f.HasChild }); } - return result; + // Sort alphabetically ascending + return result.OrderBy(x => x.Name).ToList(); } private async Task OnFolderSelected(string value) diff --git a/TelegramDownloader/TelegramDownloader.csproj b/TelegramDownloader/TelegramDownloader.csproj index bfb71a7..b5449a2 100644 --- a/TelegramDownloader/TelegramDownloader.csproj +++ b/TelegramDownloader/TelegramDownloader.csproj @@ -1,4 +1,4 @@ - + 3.4.0.0 diff --git a/switch-wtelegramclient.ps1 b/switch-wtelegramclient.ps1 new file mode 100644 index 0000000..9b97cce --- /dev/null +++ b/switch-wtelegramclient.ps1 @@ -0,0 +1,409 @@ +<# +.SYNOPSIS + Script para cambiar entre WTelegramClient oficial (NuGet) y custom (submodule) + +.DESCRIPTION + Permite alternar entre: + - Librería oficial de NuGet (WTelegramClient) + - Librería custom con optimizaciones de transferencia (submodule) + + Modifica automáticamente: + - TelegramDownloader.csproj + - Dockerfile + - GitHub Actions workflows + +.EXAMPLE + .\switch-wtelegramclient.ps1 +#> + +param( + [Parameter(Position=0)] + [ValidateSet("nuget", "custom", "status")] + [string]$Mode +) + +$ErrorActionPreference = "Stop" +$ProjectFile = "TelegramDownloader\TelegramDownloader.csproj" +$DockerFile = "TelegramDownloader\Dockerfile" +$SubmodulePath = "libs\WTelegramClient" +$NuGetVersion = "4.3.14" + +$WorkflowFiles = @( + ".github\workflows\docker-image.yml", + ".github\workflows\release-docker-image.yml", + ".github\workflows\buildrelease.yml" +) + +function Get-CurrentMode { + $content = Get-Content $ProjectFile -Raw + if ($content -match '$null + $commitMsg = git log -1 --pretty=format:"%s" 2>$null + $branch = git branch --show-current 2>$null + Pop-Location + + Write-Host " Ruta: $SubmodulePath" + Write-Host " Rama: $branch" + Write-Host " Commit: $commitId" + Write-Host " Mensaje: $commitMsg" + } + } elseif ($currentMode -eq "nuget") { + Write-Host " Modo: " -NoNewline + Write-Host "NUGET (oficial)" -ForegroundColor Yellow + Write-Host " Version: $NuGetVersion" + } else { + Write-Host " Modo: " -NoNewline + Write-Host "DESCONOCIDO" -ForegroundColor Red + } + Write-Host "" +} + +function Update-WorkflowsForNuGet { + Write-Host " Actualizando GitHub Actions workflows..." -ForegroundColor White + + foreach ($workflowFile in $WorkflowFiles) { + if (Test-Path $workflowFile) { + $content = Get-Content $workflowFile -Raw + + # Quitar submodules: recursive del checkout + $content = $content -replace '(- uses: actions/checkout@v4)\s*\n\s*with:\s*\n\s*submodules: recursive', '$1' + + # Cambiar contexto de docker build de . a ./TelegramDownloader + $content = $content -replace 'docker build \. -f \./TelegramDownloader/Dockerfile', 'docker build ./TelegramDownloader -f ./TelegramDownloader/Dockerfile' + + Set-Content $workflowFile -Value $content -NoNewline + Write-Host " [OK] $workflowFile" -ForegroundColor Green + } + } +} + +function Update-WorkflowsForCustom { + Write-Host " Actualizando GitHub Actions workflows..." -ForegroundColor White + + foreach ($workflowFile in $WorkflowFiles) { + if (Test-Path $workflowFile) { + $content = Get-Content $workflowFile -Raw + + # Añadir submodules: recursive al checkout (si no existe ya) + if ($content -notmatch 'submodules: recursive') { + $content = $content -replace '(- uses: actions/checkout@v4)(\s*\n)(\s*-|\s*\n\s*-)', "`$1`n with:`n submodules: recursive`$2`$3" + + # Patrón alternativo para buildrelease.yml que tiene nombre + $content = $content -replace "(- name: '📄 Checkout'\s*\n\s*uses: actions/checkout@v4)(\s*\n)(\s*-)", "`$1`n with:`n submodules: recursive`$2`$3" + } + + # Cambiar contexto de docker build de ./TelegramDownloader a . + $content = $content -replace 'docker build \./TelegramDownloader -f \./TelegramDownloader/Dockerfile', 'docker build . -f ./TelegramDownloader/Dockerfile' + + Set-Content $workflowFile -Value $content -NoNewline + Write-Host " [OK] $workflowFile" -ForegroundColor Green + } + } +} + +function Update-DockerfileForNuGet { + $dockerContent = @' +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# Alpine base image (~110MB vs ~220MB Debian) +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +# Build argument to optionally include FFmpeg (default: true) +# Set to "false" to create a lighter image without video transcoding support +ARG INCLUDE_FFMPEG=true + +# Install ICU libraries for globalization support (required by BlazorBootstrap NumberInput) +# Optionally install FFmpeg for video transcoding support (MKV, AVI, WMV, etc.) +RUN apk add --no-cache icu-libs && \ + if [ "$INCLUDE_FFMPEG" = "true" ]; then \ + apk add --no-cache ffmpeg; \ + fi +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false + +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +ARG BUILD_CONFIGURATION=Release +ARG VERSION=0.0.0.0 +WORKDIR /src +COPY ["TelegramDownloader.csproj", "."] +RUN dotnet restore "TelegramDownloader.csproj" +COPY . . +WORKDIR "/src/." +RUN dotnet build "./TelegramDownloader.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +ARG VERSION=0.0.0.0 +RUN dotnet publish "./TelegramDownloader.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false,InformationalVersion=$VERSION + +FROM base AS final +WORKDIR /app + +# Install Python (Alpine uses apk instead of apt) +RUN apk add --no-cache python3 py3-pip && \ + ln -sf /usr/bin/python3 /usr/bin/python + +COPY ./WebDav /app/WebDav + +# Create venv and install dependencies in single layer +RUN python3 -m venv /app/venv && \ + /app/venv/bin/pip install --no-cache-dir --upgrade pip && \ + /app/venv/bin/pip install --no-cache-dir -r /app/WebDav/requirements.txt && \ + /app/venv/bin/python -c "import uvicorn; print('uvicorn OK', uvicorn.__version__)" + +ENV PATH="/app/venv/bin:${PATH}" + +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TelegramDownloader.dll"] +'@ + Set-Content $DockerFile -Value $dockerContent -NoNewline + Write-Host " [OK] $DockerFile" -ForegroundColor Green +} + +function Update-DockerfileForCustom { + $dockerContent = @' +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# Alpine base image (~110MB vs ~220MB Debian) +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS base +WORKDIR /app +EXPOSE 80 +EXPOSE 443 + +# Build argument to optionally include FFmpeg (default: true) +# Set to "false" to create a lighter image without video transcoding support +ARG INCLUDE_FFMPEG=true + +# Install ICU libraries for globalization support (required by BlazorBootstrap NumberInput) +# Optionally install FFmpeg for video transcoding support (MKV, AVI, WMV, etc.) +RUN apk add --no-cache icu-libs && \ + if [ "$INCLUDE_FFMPEG" = "true" ]; then \ + apk add --no-cache ffmpeg; \ + fi +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false + +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +ARG BUILD_CONFIGURATION=Release +ARG VERSION=0.0.0.0 +WORKDIR /src + +# Copy submodule (WTelegramClient) first +COPY ["libs/WTelegramClient/src/WTelegramClient.csproj", "libs/WTelegramClient/src/"] +COPY ["libs/WTelegramClient/generator/MTProtoGenerator.csproj", "libs/WTelegramClient/generator/"] + +# Copy main project +COPY ["TelegramDownloader/TelegramDownloader.csproj", "TelegramDownloader/"] + +# Restore all projects +RUN dotnet restore "TelegramDownloader/TelegramDownloader.csproj" + +# Copy all source files +COPY libs/WTelegramClient/ libs/WTelegramClient/ +COPY TelegramDownloader/ TelegramDownloader/ + +WORKDIR "/src/TelegramDownloader" +RUN dotnet build "./TelegramDownloader.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +ARG VERSION=0.0.0.0 +WORKDIR "/src/TelegramDownloader" +RUN dotnet publish "./TelegramDownloader.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false,InformationalVersion=$VERSION + +FROM base AS final +WORKDIR /app + +# Install Python (Alpine uses apk instead of apt) +RUN apk add --no-cache python3 py3-pip && \ + ln -sf /usr/bin/python3 /usr/bin/python + +COPY TelegramDownloader/WebDav /app/WebDav + +# Create venv and install dependencies in single layer +RUN python3 -m venv /app/venv && \ + /app/venv/bin/pip install --no-cache-dir --upgrade pip && \ + /app/venv/bin/pip install --no-cache-dir -r /app/WebDav/requirements.txt && \ + /app/venv/bin/python -c "import uvicorn; print('uvicorn OK', uvicorn.__version__)" + +ENV PATH="/app/venv/bin:${PATH}" + +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "TelegramDownloader.dll"] +'@ + Set-Content $DockerFile -Value $dockerContent -NoNewline + Write-Host " [OK] $DockerFile" -ForegroundColor Green +} + +function Switch-ToNuGet { + Write-Host "" + Write-Host "=== Cambiando a WTelegramClient oficial (NuGet) ===" -ForegroundColor Yellow + Write-Host "" + + # Modificar csproj + $content = Get-Content $ProjectFile -Raw + + # Reemplazar ProjectReference por PackageReference + $pattern = '(?s).*?\s*\s*' + $replacement = "" + + if ($content -match $pattern) { + $content = $content -replace $pattern, $replacement + } else { + # Patrón alternativo más simple + $content = $content -replace '', $replacement + } + + Set-Content $ProjectFile -Value $content -NoNewline + Write-Host " [OK] Actualizado $ProjectFile" -ForegroundColor Green + + # Modificar Dockerfile + Write-Host " Actualizando Dockerfile..." -ForegroundColor White + Update-DockerfileForNuGet + + # Modificar workflows + Update-WorkflowsForNuGet + + Write-Host "" + Write-Host " [OK] Cambiado a NuGet v$NuGetVersion" -ForegroundColor Green + Write-Host "" + Write-Host " Archivos modificados:" -ForegroundColor Cyan + Write-Host " - $ProjectFile" + Write-Host " - $DockerFile" + foreach ($wf in $WorkflowFiles) { + Write-Host " - $wf" + } + Write-Host "" + Write-Host " Ejecuta 'git status' para ver los cambios" -ForegroundColor White + Write-Host "" +} + +function Switch-ToCustom { + Write-Host "" + Write-Host "=== Cambiando a WTelegramClient custom (submodule) ===" -ForegroundColor Green + Write-Host "" + + # Verificar si el submodule existe + if (-not (Test-Path $SubmodulePath)) { + Write-Host " El submodule no existe. Inicializando..." -ForegroundColor Yellow + git submodule add https://github.com/mateof/MyCustomWTelegramClient.git $SubmodulePath + git submodule update --init --recursive + } + + # Pedir commit ID + Write-Host "" + Write-Host " Commits recientes en el submodule:" -ForegroundColor Cyan + Push-Location $SubmodulePath + git fetch origin 2>$null + git log origin/master --oneline -5 2>$null + Pop-Location + Write-Host "" + + $commitId = Read-Host " Introduce el Commit ID (Enter para ultimo de 'master')" + + if ([string]::IsNullOrWhiteSpace($commitId)) { + $commitId = "origin/master" + } + + # Actualizar submodule al commit especificado + Push-Location $SubmodulePath + git checkout $commitId 2>$null + $actualCommit = git rev-parse HEAD + $commitMsg = git log -1 --pretty=format:"%s" + Pop-Location + + Write-Host "" + Write-Host " [OK] Submodule en commit: $actualCommit" -ForegroundColor Green + Write-Host " Mensaje: $commitMsg" + + # Modificar csproj + $content = Get-Content $ProjectFile -Raw + + # Reemplazar PackageReference por ProjectReference + $newRef = @" + + + + +"@ + + $content = $content -replace '', $newRef + + Set-Content $ProjectFile -Value $content -NoNewline + Write-Host " [OK] Actualizado $ProjectFile" -ForegroundColor Green + + # Modificar Dockerfile + Write-Host " Actualizando Dockerfile..." -ForegroundColor White + Update-DockerfileForCustom + + # Modificar workflows + Update-WorkflowsForCustom + + # Registrar cambio en submodule + git add $SubmodulePath 2>$null + + Write-Host "" + Write-Host " [OK] Cambiado a custom library" -ForegroundColor Green + Write-Host "" + Write-Host " Archivos modificados:" -ForegroundColor Cyan + Write-Host " - $ProjectFile" + Write-Host " - $DockerFile" + foreach ($wf in $WorkflowFiles) { + Write-Host " - $wf" + } + Write-Host " - $SubmodulePath (submodule)" + Write-Host "" + Write-Host " Ejecuta 'git status' para ver los cambios" -ForegroundColor White + Write-Host "" +} + +# Main +Write-Host "" +Write-Host "========================================" -ForegroundColor Cyan +Write-Host " WTelegramClient Switcher" -ForegroundColor Cyan +Write-Host "========================================" -ForegroundColor Cyan + +if (-not $Mode) { + Show-Status + Write-Host "Opciones disponibles:" -ForegroundColor White + Write-Host " 1) Cambiar a NuGet (oficial)" + Write-Host " 2) Cambiar a Custom (submodule)" + Write-Host " 3) Ver estado actual" + Write-Host " 4) Salir" + Write-Host "" + $choice = Read-Host "Selecciona una opcion (1-4)" + + switch ($choice) { + "1" { $Mode = "nuget" } + "2" { $Mode = "custom" } + "3" { $Mode = "status" } + "4" { exit 0 } + default { Write-Host "Opcion no valida" -ForegroundColor Red; exit 1 } + } +} + +switch ($Mode) { + "nuget" { Switch-ToNuGet } + "custom" { Switch-ToCustom } + "status" { Show-Status } +}