Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 0 additions & 146 deletions .github/workflows/azure-app-service.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
with:
dotnet-version: '10.0.x'
global-json-file: global.json

- name: Restore .NET tools
run: dotnet tool restore
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Production releases. Run this workflow on main to cut the next version, or publish a
# GitHub release. The steps live in kasuken/.github; see its RELEASING.md.
name: Release

on:
release:
types: [published]
workflow_dispatch:
inputs:
bump:
description: Version part to increment
type: choice
options: [patch, minor, major]
default: patch
redeploy:
description: Existing tag to redeploy instead (rollback), e.g. v1.4.1
type: string
default: ''

concurrency:
group: production
cancel-in-progress: false

permissions:
contents: write
actions: read
id-token: write

jobs:
release:
uses: kasuken/.github/.github/workflows/release-azure-webapp.yml@main
with:
app-name: learnstack-prod-001
web-project: LearnStack/LearnStack.csproj
base-url: https://learnstack.cloud
bump: ${{ inputs.bump || 'patch' }}
redeploy: ${{ inputs.redeploy || '' }}
secrets: inherit
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ All notable changes to LearnStack will be documented in this file.
- Released as open source under the GNU AGPL v3.0 (`AGPL-3.0-only`), with a Contributor License Agreement for contributions
- "Source code" link in the account menu, configurable with `SourceCodeUrl` (AGPL section 13)
- Issue forms, CODEOWNERS, Dependabot configuration, `.editorconfig`, `.gitattributes` and third-party notices
- `/health/live` and `/health/ready` endpoints; every release is smoke tested against `/health/ready`

### Changed
- Deployment, release, pipeline and quick-reference guides moved from `.github/` to `docs/`
- Pushing to `main` no longer deploys or creates a `v0.1.x` release. Releases use the shared kasuken release workflow (run **Release** with a version bump), which waits for CI, deploys with Azure OIDC instead of a publish profile, smoke tests, and tags the release with the version from this changelog.

### Removed
- Accidentally tracked local files (Claude worktrees, Rider user settings, build error log)
- The deployment, release, pipeline and quick-reference guides and `version.json`, which described the old push-to-deploy pipeline

---

Expand Down
1 change: 1 addition & 0 deletions LearnStack/LearnStack.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="10.0.3" />
<PackageReference Include="MudBlazor" Version="8.15.0" />
</ItemGroup>

Expand Down
12 changes: 12 additions & 0 deletions LearnStack/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -34,6 +35,8 @@
throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddLearnStackData(connectionString, migrationsAssembly: "LearnStack");
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>("database", tags: ["ready"]);

// Persist the Data Protection key ring outside the container so that auth cookies and
// antiforgery tokens survive restarts, deployments, slot swaps and scale-out on App
Expand Down Expand Up @@ -102,6 +105,15 @@

app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
app.UseHttpsRedirection();

// /health/live tells App Service the process is up; /health/ready also checks the database
// and gates every release (see .github/workflows/release.yml).
app.MapHealthChecks("/health/live", new HealthCheckOptions { Predicate = _ => false });
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});

app.UseRequestLocalization();

app.UseRouting();
Expand Down
25 changes: 8 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Blazor Server application for organizing learning resources, turning them into content ideas, and sharing curated knowledge with other learners.

[![Deploy to Azure App Service](https://github.com/kasuken/LearnStack/actions/workflows/azure-app-service.yml/badge.svg)](https://github.com/kasuken/LearnStack/actions/workflows/azure-app-service.yml)
[![CI](https://github.com/kasuken/LearnStack/actions/workflows/ci.yml/badge.svg)](https://github.com/kasuken/LearnStack/actions/workflows/ci.yml)
![.NET 10](https://img.shields.io/badge/.NET-10-512BD4?style=flat-square)
![Blazor Server](https://img.shields.io/badge/Blazor-Server-5C2D91?style=flat-square)
![MudBlazor](https://img.shields.io/badge/UI-MudBlazor-594AE2?style=flat-square)
Expand Down Expand Up @@ -130,25 +130,16 @@ $env:ConnectionStrings__DefaultConnection="Server=tcp:your-server.database.windo

## Deployment

LearnStack includes a GitHub Actions workflow that deploys the app to Azure App Service whenever changes are pushed to `main`.
The hosted service runs on Azure App Service. Merging to `main` only runs CI; production changes when a release is cut with the [Release workflow](.github/workflows/release.yml):

The workflow:

- builds and publishes the Blazor application,
- deploys it to Azure App Service,
- calculates the next semantic version,
- creates a Git tag,
- publishes a GitHub release.

Useful docs:
```bash
gh workflow run release.yml -R kasuken/LearnStack -f bump=minor # patch | minor | major
gh workflow run release.yml -R kasuken/LearnStack -f redeploy=v1.4.1 # roll back
```

- [Azure deployment guide](./docs/DEPLOYMENT.md)
- [Release and versioning guide](./docs/RELEASES.md)
- [CI/CD pipeline overview](./docs/PIPELINE.md)
- [Quick reference](./docs/QUICK-REFERENCE.md)
The workflow waits for CI to pass on the commit, builds once, deploys through the `production` environment with Azure OIDC, smoke tests `/health/ready`, and then tags and publishes the GitHub release. The steps are shared with the other kasuken SaaS apps; see [RELEASING.md](https://github.com/kasuken/.github/blob/main/RELEASING.md).

> [!TIP]
> The workflow is configured for the `learnstack-prod-001` Azure Web App name by default. If you fork the repository, update `.github/workflows/azure-app-service.yml` and the `AZURE_WEBAPP_PUBLISH_PROFILE` secret for your own environment.
Hosted-only settings (for example `Billing__Provider=Stripe`) and all secrets are App Service application settings, never committed files.

## Project structure

Expand Down
Loading
Loading