Deployment — Build & Publish¶
How a release is built and put on the server, what the build does that is easy to miss, the two publish profiles and where they disagree, and the order a release has to happen in.
Build once per environment
Site URLs, mailboxes and several folder paths are compiled into the assembly (SiteSettings.cs
and others — see Configuration). A build made
for test carries test values. Build production from a commit whose SiteSettings.cs holds the
production values (R4-4).
Build¶
From the repository root:
The output is framework-dependent net6.0 — the server supplies the runtime through the
ASP.NET Core 6.0 Hosting Bundle. dotnet publish builds all five projects through their project
references, and also:
| Step | Detail |
|---|---|
| Generates the JavaScript bundles | Build reads bundleconfig. and writes four bundles (plus minified copies) to wwwroot/ — jquery., jqueryval., modalform., scripts.. They are gitignored build outputs, so a fresh clone has none until the first build. There is no npm step; NKO. is empty ({}). |
| Compiles the Razor views | Views are compiled at build time as usual. Runtime compilation is also registered — Add is called unconditionally in Startup, not only in Development — so in production the app also watches for changed . files and recompiles them. That costs memory and startup time for a development convenience. |
Generates web. |
The real site web. (ASP.NET Core Module, in-process) is created by publish. The Views/ in the repository is an unrelated ASP.NET MVC 5 leftover. |
What publish does not include:
appsettings.jsonfrom the repository — it is gitignored. But a local copy on the build machine is packaged, because publish includes everyappsettings*.jsonin the project folder. Publishing from a developer machine that has its ownappsettings.jsontherefore overwrites the server's configuration — connection strings, AWS keys and all. Keep production settings in environment variables (see Configuration), or exclude the file from the package;wwwroot/Rotativa/— thewkhtmltopdfbinaries are not in the repository (R5-3);- anything under
C:\upload\— it lives outside the site and is never touched by a publish.
The publish profiles¶
Deployment is done with Web Deploy from two IIS profiles under
NKO.Main/Properties/PublishProfiles/. They are not in the repository (*.pubxml is
gitignored), so they exist only on the machines they were created on. Both publish a Release,
framework-dependent net6.0 build through the Web Deploy remote agent (MSDeploy,
RemoteAgent) and ask for Web Deploy's automatic backup (EnableMSDeployBackup = true). They
differ in two settings:
| Setting | IISProfile |
IISProfile1 |
Effect |
|---|---|---|---|
Skip |
false |
true |
With false, every file on the server that is not in the package is deleted |
Enable |
false |
true |
With true, Web Deploy drops app_offline. first, which stops the app and releases its files |
IISProfile removes the PDF engine on every publish — R4-3
wkhtmltopdf.exe is placed in wwwroot\Rotativa\ by hand — it is not in the project, so it
is not in the package. With SkipExtraFilesOnServer = false, Web Deploy deletes it, and
every PDF export breaks after the publish. The same profile also leaves the site running
while it overwrites the application's assemblies; in-process hosting keeps them loaded, so the
publish can fail part-way with "file in use" errors, leaving a mix of old and new files.
Use IISProfile1's settings — keep extra files, take the app offline — or add the
Rotativa folder to the project so it is part of the package.
Web Deploy's backup only happens if backups are enabled for the site on the server; the profile setting requests it but cannot turn it on. Do not rely on it as the release backup — see Backup & Rollback.
Database migrations¶
Migrations are not applied at startup — nothing in Program.cs or Startup calls Migrate().
They must be applied as a release step, before the new code starts, for both contexts.
Generate idempotent SQL scripts, which a DBA can review and which are safe to re-run. From
NKO.Main/NKO.Main:
dotnet ef migrations script --idempotent --context NKO_DBEntities1 `
--project ../../NKO.Lib/NKO.DataAccess --startup-project . -o core.sql
dotnet ef migrations script --idempotent --context WarehouseDBContext `
--project ../../NKO.Lib/NKO.Warehouse.DataAccess --startup-project . -o warehouse.sql
The pinned EF tool is too old
.config/dotnet-tools.json pins dotnet-ef 5.0.5 while EF Core is 6.0.15, so the local
tool is unsupported against this model and may fail (R5-2). Use dotnet-ef 6.0.x — see
Installation — Database Setup.
Before applying a script, check whether it contains a DROP TABLE or DROP COLUMN. Nine of the
existing migrations do, and a migration like that cannot be undone without a database restore —
see Backup & Rollback.
Release order¶
flowchart TD
A["1. Build for this environment<br/>dotnet publish -c Release"] --> B["2. Back up NKO_DB and NKO_DB_Warehouse together"]
B --> C["3. Take the site offline<br/>app_offline.htm"]
C --> D["4. Apply core.sql, then warehouse.sql"]
D --> E["5. Publish with Web Deploy<br/>keep extra files"]
E --> F["6. Confirm wwwroot\Rotativa\wkhtmltopdf.exe is still there"]
F --> G["7. Bring the site online and smoke-test"]
Smoke test¶
The application logs almost nothing (R8-7), so a failed release shows up only by using it:
- [ ] Sign in, and open a page behind a permission key (the permission query runs on every request).
- [ ] Open a notification — its link uses
SysUrl. - [ ] Upload and download an attachment on any document (S3 and
StorageConfigs:Systems). - [ ] Upload an attachment on an asset-disposal request — fails until R4-1 is fixed.
- [ ] Export a PDF from Jobs or Advance (the server fetching its own header and footer).
- [ ] Export a warehouse Excel report (the
C:\upload\…folders). - [ ] Next morning: check that open requests were re-routed by the 03:00
UpdateNextUserJob.
If the site does not start at all, turn on stdoutLogEnabled in the generated web.config
temporarily — with almost no application logging, the ASP.NET Core Module's stdout log is the only place a
startup exception is written.