diff --git a/.forgejo/workflows/buildAndTest.yml b/.forgejo/workflows/buildAndTest.yml index 9b3403db..cda246cc 100644 --- a/.forgejo/workflows/buildAndTest.yml +++ b/.forgejo/workflows/buildAndTest.yml @@ -37,23 +37,17 @@ jobs: build: - runs-on: docker + runs-on: debian-latest steps: - - name: Clone yavsc - run: | - cd /src - git clone https://forgejo.pschneider.fr/notazof/yavsc.git _src - cd _src - if [ -n "${GITHUB_REF:-}" ]; then - git fetch origin "$GITHUB_REF" - git checkout FETCH_HEAD - fi - git submodule update --init --recursive - echo "Checked out at $(git rev-parse HEAD) on $(git branch --show-current 2>/dev/null || echo detached HEAD)" + - uses: actions/checkout@v6 + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + dotnet-version: 9.0.x - name: Restore dependencies - run: cd /src/_src && dotnet restore + run: dotnet restore - name: Build - run: cd /src/_src && dotnet build --no-restore + run: dotnet build --no-restore - name: Test - run: cd /src/_src && dotnet test --no-build --verbosity normal + run: dotnet test --no-build --verbosity normal diff --git a/.github/workflows/docker-publish-android.yml b/.github/workflows/docker-publish-android.yml index ebf52a6d..98237136 100644 --- a/.github/workflows/docker-publish-android.yml +++ b/.github/workflows/docker-publish-android.yml @@ -5,14 +5,8 @@ on: branches: - main tags: - - '*' + - 'v*' workflow_dispatch: - inputs: - force_unstable: - description: 'Publier une release avec suffixe (ex. 1.0.0-rc1) malgré le fail-fast par défaut.' - required: false - type: boolean - default: false # softprops/action-gh-release a besoin de contents: write # pour publier une release + uploader un asset. @@ -47,113 +41,11 @@ jobs: path: ./PostIt.Android.apk retention-days: 7 - # Job de validation : parse le tag, vérifie le format, applique la règle - # de parité du patch (pair=stable / impair=preview / suffixe=instable), - # et s'assure que CHANGELOG.md contient une section cohérente. - # Sans ce job, le job publish-release peut être bypassé (un attaquant - # qui contrôle un tag ne peut pas publier de release sans une section - # changelog cohérente). - validate-release: - if: startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - name: Checkout du code - uses: actions/checkout@v7 - - - name: Valider le tag et la section CHANGELOG - env: - FORCE_UNSTABLE: ${{ inputs.force_unstable || github.event.inputs.force_unstable || 'false' }} - run: | - TAG="${GITHUB_REF_NAME}" - - # Parse semver : MAJOR.MINOR.PATCH[-SUFFIX] - if [[ ! "$TAG" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.*)?$ ]]; then - echo "::error::Tag '$TAG' does not match MAJOR.MINOR.PATCH[-SUFFIX] format." - exit 1 - fi - - MAJOR="${BASH_REMATCH[1]}" - MINOR="${BASH_REMATCH[2]}" - PATCH="${BASH_REMATCH[3]}" - SUFFIX="${BASH_REMATCH[4]}" - - # Classification du canal par parité du patch. - # Patch pair + pas de suffixe -> stable. - # Patch impair + pas de suffixe -> preview. - # Suffixe présent -> instable. - if [[ -n "$SUFFIX" ]]; then - CHANNEL="unstable" - elif (( PATCH % 2 == 0 )); then - CHANNEL="stable" - else - CHANNEL="preview" - fi - - echo "Tag $TAG classifié comme channel=$CHANNEL" - - # Fail-fast sur instable sauf opt-in explicite via workflow_dispatch. - if [[ "$CHANNEL" == "unstable" && "$FORCE_UNSTABLE" != "true" ]]; then - echo "::error::Tag '$TAG' is unstable (suffix '$SUFFIX'). Refusing to publish." - echo "Set force_unstable=true via workflow_dispatch to override." - exit 1 - fi - - # Lecture du CHANGELOG.md (doit exister à la racine du repo). - if [[ ! -f CHANGELOG.md ]]; then - echo "::error::CHANGELOG.md not found at repo root." - exit 1 - fi - - # Extraction de la section [TAG]. On cherche la première ligne - # commençant par '## [' qui contient '[TAG]' (entre '## [' et - # la prochaine ligne '## [' ou fin de fichier). awk en mode - # paragraphe suffit et reste POSIX. - BODY=$(awk -v tag="[$TAG]" ' - /^## \[/ { - if (in_section) exit - if (index($0, tag) > 0) in_section=1 - next - } - in_section { print } - ' CHANGELOG.md) - - if [[ -z "$BODY" ]]; then - echo "::error::No section matching '## [$TAG]' found in CHANGELOG.md." - echo "Add a '## [$TAG] - $CHANNEL' section before tagging." - exit 1 - fi - - # Vérification cohérence du canal déclaré dans le suffixe. - # Format attendu : "## [TAG] - stable" / "- preview" / "- unstable". - if [[ "$BODY" != *" - $CHANNEL"* ]]; then - echo "::error::Section '## [$TAG]' must declare suffix '- $CHANNEL' to match tag parity." - echo "Current section body (first 5 lines):" - echo "$BODY" | head -5 - exit 1 - fi - - echo "Section CHANGELOG validée pour [$TAG] - $CHANNEL" - - # Exposition aux étapes suivantes via $GITHUB_ENV. - # heredoc <> "$GITHUB_ENV" - publish-release: - # Déclenché uniquement par un push de tag. Le job apk-deploy produit - # l'artefact ; validate-release garantit la cohérence du tag et du - # changelog avant publication. - if: startsWith(github.ref, 'refs/tags/') - needs: [apk-deploy, validate-release] + # Uniquement déclenché par un tag v*. Le job apk-deploy tourne en + # parallèle, on partage l'artefact entre jobs. + if: startsWith(github.ref, 'refs/tags/v') + needs: apk-deploy runs-on: ubuntu-latest steps: - name: Récupérer l'APK depuis l'artefact @@ -169,9 +61,7 @@ jobs: # apparaîtra dans l'asset et donc dans le permalink : # https://github.com///releases/latest/download/PostIt.Android.apk files: ./PostIt.Android.apk - # Le body est extrait de la section CHANGELOG.md correspondant - # au tag, exposée par validate-release via $GITHUB_ENV. - body: ${{ env.RELEASE_BODY }} - # stable -> false (marque comme Latest). - # preview / unstable -> true (visible mais pas Latest). - prerelease: ${{ env.IS_PRERELEASE }} + # generate_release_notes: true -> évite d'avoir à maintenir + # le corps de release à la main. Décommente si tu veux. + # generate_release_notes: true + diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index eadf3c7f..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "external/dotnet-android-build-image"] - path = external/dotnet-android-build-image - url = https://forgejo.pschneider.fr/notazof/dotnet-android-build-image.git diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index eb596070..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,29 +0,0 @@ -# Changelog - -Toutes les modifications notables de PostIt et de la plateforme Yavsc -sont documentées dans ce fichier. - -Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/), -et ce projet adhère au [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -À noter : la **parité du numéro de patch** porte une signification de canal : - -- **patch pair** (ex. `1.0.0`, `1.0.2`) → **stable** -- **patch impair** (ex. `1.0.1`, `1.0.3`) → **preview** -- **suffixe** (ex. `1.0.0-rc1`, `1.0.0-alpha`) → **instable** - -Cette convention est partagée avec le dépôt -[`postit-debian`](https://forgejo.pschneider.fr/notazof/postit-debian) -pour la production des paquets `.deb`. - -## [Unreleased] - -### Added - -### Changed - -### Fixed - -### Removed - -[Unreleased]: https://github.com/pazof/yavsc/compare/HEAD diff --git a/Directory.Build.props b/Directory.Build.props index aec8c990..873845c4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,6 +11,5 @@ from without conflicting names. --> true - NU1701, NU1901, NU1902 diff --git a/Dockerfile b/Dockerfile index 795b70ab..88b11683 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,10 @@ COPY src/PostIt/PostIt.Desktop/*.csproj ./src/PostIt/PostIt.Desktop/ # (2) Tout le code source COPY . . +# (3) Source NuGet interne (Letsencrypt, certificat auto-signé côté +# serveur, justifié par build privé). +RUN dotnet nuget add source https://isn.pschneider.fr/api/v3/index.json --allow-insecure-connections + # (4) Restore RUN dotnet restore diff --git a/Dockerfile.backend b/Dockerfile.backend index a4e9a54a..76ad9ea0 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -25,6 +25,9 @@ COPY src/PostIt/PostIt.Desktop/*.csproj ./src/PostIt/PostIt.Desktop/ # 4. Copie de l'intégralité du code source COPY . . +# 3. Restauration des dépendances avec vos workloads actifs +RUN dotnet nuget add source https://isn.pschneider.fr/api/v3/index.json + # 4. Restauration des dépendances pour tous les projets RUN dotnet restore diff --git a/NuGet.config b/NuGet.config deleted file mode 100644 index c601d09b..00000000 --- a/NuGet.config +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - diff --git a/external/dotnet-android-build-image b/external/dotnet-android-build-image deleted file mode 160000 index 0695a6c1..00000000 --- a/external/dotnet-android-build-image +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0695a6c1fea6508f1a88f7ad0ad9cb93733aa52d diff --git a/src/PostIt.Tests/BlogApiTestFakes.cs b/src/PostIt.Tests/BlogApiTestFakes.cs index 755ce105..9ec10f89 100644 --- a/src/PostIt.Tests/BlogApiTestFakes.cs +++ b/src/PostIt.Tests/BlogApiTestFakes.cs @@ -1,7 +1,6 @@ using PostIt.Models; using PostIt.Services; using PostIt.ViewModels; -using Yavsc.Models; namespace PostIt.Tests; diff --git a/src/PostIt/PostIt/App.axaml.cs b/src/PostIt/PostIt/App.axaml.cs index b5740f2f..1e0afeaf 100644 --- a/src/PostIt/PostIt/App.axaml.cs +++ b/src/PostIt/PostIt/App.axaml.cs @@ -24,7 +24,7 @@ public partial class App : Application /// binding sink with a cross-thread exception inside /// DataValidationErrors.SetErrors. /// - public IServiceProvider? ServiceProvider { get; private set; } + public IServiceProvider? Services { get; private set; } private MainWindow window; public App() { @@ -91,17 +91,19 @@ public partial class App : Application services.AddSingleton(sessionStatus); services.AddTransient(); - ServiceProvider = services.BuildServiceProvider(); + var provider = services.BuildServiceProvider(); // Bind the canonical Settings to the static accessor so any // code path that can't easily take a constructor parameter // (designer surfaces, Avalonia data templates) still gets // the same instance the rest of the app is using. Idempotent: // re-binding from a second App boot (tests) is a no-op. - Settings.BindToServiceProvider(ServiceProvider); + Settings.BindToServiceProvider(provider); + + Services = provider; DataTemplates.Clear(); - DataTemplates.Add(new ViewLocator(ServiceProvider)); + DataTemplates.Add(new ViewLocator(provider)); // Wire the Settings singleton onto the SettingsPage singleton // once, at composition time. The page is registered as a @@ -111,7 +113,7 @@ public partial class App : Application // DataContext, and the TwoWay bindings inside the page keep // mutating the same in-memory Settings instance that the rest // of the app reads (OidcClientOptions construction, etc.). - ServiceProvider.GetRequiredService().DataContext = settings; + provider.GetRequiredService().DataContext = settings; // Settings.DarkMode was previously a dead field: it round- // tripped through the settings file and the SettingsPage @@ -132,8 +134,8 @@ public partial class App : Application if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - var homePage = ServiceProvider.GetRequiredService(); - homePage.DataContext = ServiceProvider.GetRequiredService(); + var homePage = provider.GetRequiredService(); + homePage.DataContext = provider.GetRequiredService(); window = new MainWindow(); window.SessionBanner.DataContext = sessionStatus; @@ -153,8 +155,8 @@ public partial class App : Application { var w = (MainWindow)((IClassicDesktopStyleApplicationLifetime)ApplicationLifetime!).MainWindow!; var nav = w.NavRoot; - var hp = ServiceProvider.GetRequiredService(); - hp.DataContext = ServiceProvider.GetRequiredService(); + var hp = provider.GetRequiredService(); + hp.DataContext = provider.GetRequiredService(); _ = nav.PopToRootAsync(); }; @@ -185,7 +187,7 @@ public partial class App : Application sessionStatus.OpenSettingsRequested += () => { var w = (MainWindow)((IClassicDesktopStyleApplicationLifetime)ApplicationLifetime!).MainWindow!; - var settingsPage = ServiceProvider.GetRequiredService(); + var settingsPage = provider.GetRequiredService(); var stack = w.NavRoot.NavigationStack; if (stack.Count > 0 && ReferenceEquals(stack[stack.Count - 1], settingsPage)) { @@ -194,13 +196,13 @@ public partial class App : Application _ = w.NavRoot.PushAsync(settingsPage); }; - window.Opened += async (_, _) => await BootAsync(ServiceProvider, api); + window.Opened += async (_, _) => await BootAsync(provider, api); } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleView) { singleView.MainView = new MainWindow { - DataContext = ServiceProvider.GetRequiredService() + DataContext = provider.GetRequiredService() }; } } @@ -241,8 +243,8 @@ public partial class App : Application public static async Task PushMainPageAsync() { var app = (App)Current; - var mainVm = app.ServiceProvider.GetRequiredService(); - var mainPage = app.ServiceProvider.GetRequiredService(); + var mainVm = app.Services.GetRequiredService(); + var mainPage = app.Services.GetRequiredService(); mainPage.DataContext = mainVm; await app.window.FindControl("NavRoot").PushAsync(mainPage).ConfigureAwait(true); } diff --git a/src/PostIt/PostIt/Models/BlogPost.cs b/src/PostIt/PostIt/Models/BlogPost.cs index e62fcea2..7867eb02 100644 --- a/src/PostIt/PostIt/Models/BlogPost.cs +++ b/src/PostIt/PostIt/Models/BlogPost.cs @@ -1,37 +1,16 @@ using System; -using Yavsc.Abstract.Identity; -using Yavsc.Abstract.Identity.Security; -using Yavsc.Blogspot; namespace PostIt.Models; -public class BlogPost : IBlogPost +public class BlogPost { - public string AuthorId { get; set; } - - public IApplicationUser Author { get; set; } - - public string Article { get; set ; } - public string Photo { get; set ; } - public long Id { get; set ; } - public DateTime DateCreated { get; set ; } - public string UserCreated { get; set ; } - public DateTime DateModified { get; set ; } - public string UserModified { get; set ; } - public string Title { get; set ; } - - public bool AuthorizeCircle(long circleId) - { - throw new NotImplementedException(); - } - - public ICircleAuthorization[] GetACL() - { - throw new NotImplementedException(); - } - - public string[] GetTags() - { - throw new NotImplementedException(); - } + public long Id { get; set; } + public string Title { get; set; } = string.Empty; + public string? Article { get; set; } + public string? Photo { get; set; } + public string? AuthorId { get; set; } + public DateTime DateCreated { get; set; } + public string? UserCreated { get; set; } + public DateTime DateModified { get; set; } + public string? UserModified { get; set; } } diff --git a/src/PostIt/PostIt/ViewModels/HomePageViewModel.cs b/src/PostIt/PostIt/ViewModels/HomePageViewModel.cs index 876f862c..c11e396a 100644 --- a/src/PostIt/PostIt/ViewModels/HomePageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/HomePageViewModel.cs @@ -1,5 +1,4 @@ using CommunityToolkit.Mvvm.Input; -using Microsoft.Extensions.DependencyInjection; using PostIt; using PostIt.Services; namespace PostIt.ViewModels; @@ -8,7 +7,6 @@ public class HomePageViewModel : ViewModelBase { public YavscApiClient Api { get; } public Settings Settings { get; } - public SessionStatusViewModel SessionStatus { get; } private string _welcomeText = "Welcome to PostIt!"; public string WelcomeText @@ -20,12 +18,10 @@ public class HomePageViewModel : ViewModelBase public override bool CanNavigateNext { get => true; protected set => throw new System.NotImplementedException(); } public override bool CanNavigatePrevious { get => false; protected set => throw new System.NotImplementedException(); } - public HomePageViewModel(YavscApiClient api, Settings settings, SessionStatusViewModel sessionStatus) + public HomePageViewModel(YavscApiClient api, Settings settings) { Api = api; Settings = settings; - SessionStatus = sessionStatus; - } public RelayCommand OpenBlogs { get; set; } = new RelayCommand(() => App.PushMainPageAsync()); /// @@ -37,8 +33,5 @@ public class HomePageViewModel : ViewModelBase /// (thread-safe dispatcher marshalling on PropertyChanged) — a /// designer-only duplicate instance is therefore harmless. /// - public HomePageViewModel() : this(null!, new Settings(), new SessionStatusViewModel()) - { - - } + public HomePageViewModel() : this(null!, new Settings()) { } } diff --git a/src/PostIt/PostIt/Views/HomePage.axaml b/src/PostIt/PostIt/Views/HomePage.axaml index 16e66cc0..7eb6dbb3 100644 --- a/src/PostIt/PostIt/Views/HomePage.axaml +++ b/src/PostIt/PostIt/Views/HomePage.axaml @@ -16,7 +16,6 @@ HorizontalAlignment="Center"/>