367 lines
17 KiB
Markdown
367 lines
17 KiB
Markdown
# AyaNova v8 release and server update procedure
|
|
|
|
How a release goes from finished code to customers, and how the hosted subscription droplets are
|
|
updated every week. Replaces John's notes scattered across info cases (the Subversion-era
|
|
release notes and the subscription upgrade scripts of info case 3450). The build itself (tools,
|
|
`build-release.bat`, packages) is in `BUILD.md`; release-specific extras (for 8.3.0: the .NET 10
|
|
runtime, the one-time droplet hardening) are in that release's checklist
|
|
(`devdocs/modernization/NET10-PORT.md`, section 5).
|
|
|
|
Every command is a complete line for the shell named above it: PowerShell on the workstation,
|
|
bash on a droplet or the ayanova.com web server (logged in as root).
|
|
|
|
## Release order at a glance
|
|
|
|
1. Code complete: every case committed and pushed, local E2E passes
|
|
2. Docs: changelog, manual copyright date
|
|
3. Build with `build-release.bat`
|
|
4. Stage the installers and docs in the `next` folders on ayanova.com
|
|
5. Test on devtest: upgrade it from `next`, remote E2E, check each change
|
|
6. Tag the release in git
|
|
7. Publish: docs and downloads go live (not announced yet)
|
|
8. **Upgrade the subscription droplets** (Thursday, by time zone)
|
|
9. **Announce**: upload `latest-version.json`
|
|
10. Download every published file back and check its version
|
|
11. Tell subscription customers
|
|
12. Start the next version
|
|
|
|
**Subscription droplets are upgraded before the release is announced** (John, 2026-10-26, from
|
|
8.3.0 on). Uploading `latest-version.json` is what tells every AyaNova installation that a new
|
|
version exists, so it is the very last publishing step. Between steps 7 and 9 the new docs and
|
|
downloads are already visible on the website; that gap is accepted.
|
|
|
|
## 1. Code complete
|
|
|
|
- All "next release" cases done, each committed with its case number and pushed, in every repo.
|
|
- Every repo clean and in step with its remote. All four must say `nothing to commit, working tree clean` and `up to date with 'origin/main'`:
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven status; git -C C:\data\code\raven-client status; git -C C:\data\code\raven-launcher status; git -C C:\data\code\raven-test-integration status
|
|
```
|
|
|
|
- Integration tests pass against the local server (`raven-test-integration`, see `devdocs/modernization/TESTING.md`).
|
|
- E2E passes against the local debug server (API on 7575, client dev server on 8080). In `raven-test\e2e`, `cypress.json` `expectedServerVersion` must be the new version:
|
|
|
|
```powershell
|
|
cd C:\data\code\raven-test\e2e; .\local-run.bat
|
|
```
|
|
|
|
## 2. Docs
|
|
|
|
**Often missed: check carefully against the previous release.**
|
|
|
|
- `docs/8.0/ayanova/docs/changelog.md`: the new version's section is complete and reads well; remove "IN PROGRESS".
|
|
- `docs/8.0/ayanova/mkdocs.yml`: `copyright` line, year range and `REV-` date.
|
|
- `dist/latest-version.json`: the new version and its changelog anchor (for example `#ayanova-830`). Set at the version bump; check it here.
|
|
|
|
## 3. Build
|
|
|
|
Before the build, update the .NET SDK so the Windows standalone package and the launcher carry the
|
|
newest .NET runtime (they bundle the runtime of the SDK that builds them; see `BUILD.md`, Tools).
|
|
If winget finds nothing newer, nothing changes:
|
|
|
|
```powershell
|
|
winget upgrade --id Microsoft.DotNet.SDK.10 -e --source winget
|
|
```
|
|
|
|
Then run the full build; it must end with "COMPLETED WITHOUT ERROR". Details in `BUILD.md`.
|
|
|
|
```powershell
|
|
cd C:\data\code\raven; .\build-release.bat
|
|
```
|
|
|
|
Write down the commit each repo was built from (the tag in step 6 goes on exactly these):
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven log --oneline -1; git -C C:\data\code\raven-client log --oneline -1; git -C C:\data\code\raven-launcher log --oneline -1
|
|
```
|
|
|
|
## 4. Stage in `next`
|
|
|
|
On the ayanova.com web server (`ssh root@ayanova.com`), empty the previous release's `next` folders:
|
|
|
|
```bash
|
|
rm -r /var/www/html/ayanova.com/download/next/* /var/www/html/ayanova.com/docs-next/*
|
|
```
|
|
|
|
Then, from PowerShell on the workstation, upload the installers and the built manual (each
|
|
command passes `scp` the full list of files and folders to copy):
|
|
|
|
```powershell
|
|
scp (Get-ChildItem C:\data\code\raven\dist\installers -File).FullName root@ayanova.com:/var/www/html/ayanova.com/download/next/
|
|
```
|
|
|
|
```powershell
|
|
scp -r (Get-ChildItem C:\data\code\raven\server\AyaNova\wwwroot\docs).FullName root@ayanova.com:/var/www/html/ayanova.com/docs-next/
|
|
```
|
|
|
|
## 5. Test on devtest
|
|
|
|
devtest.onayanova.com mirrors the customer droplets; it is the final confirmation before any
|
|
customer is touched. For earlier, riskier tests, create a temporary droplet when needed and
|
|
destroy it when done, so it does not run up costs (for 8.3.0 that was `devtestn10`).
|
|
|
|
1. Bring its OS up to date as in the [weekly update](#weekly-os-update), reboot.
|
|
2. Any one-time steps this release needs (for 8.3.0: [the .NET 10 runtime and hardening](#one-time-steps-for-830)).
|
|
3. Upgrade it with the [next version upgrade commands](#upgrade-commands).
|
|
4. Remote E2E:
|
|
|
|
```powershell
|
|
cd C:\data\code\raven-test\e2e; .\remote-run.bat
|
|
```
|
|
|
|
5. Check each item in the changelog on it, and that the new version number shows everywhere (login page, About form).
|
|
|
|
If anything fails: fix it as a case, rebuild (step 3), restage (step 4) and test again. Nothing
|
|
is tagged or published until devtest passes.
|
|
|
|
## 6. Tag the release in git
|
|
|
|
A tag is a permanent name on one commit, so `8.3.0` always points at exactly the code that was
|
|
shipped, years later and whatever happens on `main` afterwards. An **annotated** tag (`-a`) also
|
|
records who tagged it, when, and a message. Tags are not sent by an ordinary `git push`, so each
|
|
one is pushed by name. Tag the repos whose code is in the release, at the commits noted in step 3
|
|
(if nothing was committed since, that is the current `main`):
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven tag -a 8.3.0 -m "AyaNova 8.3.0"; git -C C:\data\code\raven push origin 8.3.0
|
|
```
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven-client tag -a 8.3.0 -m "AyaNova 8.3.0"; git -C C:\data\code\raven-client push origin 8.3.0
|
|
```
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven-launcher tag -a 8.3.0 -m "AyaNova 8.3.0"; git -C C:\data\code\raven-launcher push origin 8.3.0
|
|
```
|
|
|
|
Check that the tags are on the server (each lists `refs/tags/8.3.0`):
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven ls-remote --tags origin; git -C C:\data\code\raven-client ls-remote --tags origin; git -C C:\data\code\raven-launcher ls-remote --tags origin
|
|
```
|
|
|
|
A pushed tag is never moved or deleted: if a problem turns up after release, the fix becomes the
|
|
next patch version (8.3.1) with its own tag. To see later what any release contained:
|
|
`git -C C:\data\code\raven show 8.3.0`, or compare two releases with
|
|
`git -C C:\data\code\raven log --oneline 8.2.4..8.3.0` (from 8.3.0 on; earlier releases were not tagged in git).
|
|
|
|
## 7. Publish (not announced yet)
|
|
|
|
On the ayanova.com web server. In the archive folder name (`YYYY-MM-DD-vX.Y.Z`), replace the
|
|
example date and version with the real release date and version:
|
|
|
|
```bash
|
|
rm -r /var/www/html/ayanova.com/docs/* && cp -r /var/www/html/ayanova.com/docs-next/* /var/www/html/ayanova.com/docs
|
|
```
|
|
|
|
```bash
|
|
mkdir /var/www/html/ayanova.com/download/old/2026-11-05-v8.3.0 && cp -r /var/www/html/ayanova.com/download/next/* /var/www/html/ayanova.com/download/old/2026-11-05-v8.3.0
|
|
```
|
|
|
|
Remove the oldest release folder in `download/old`: only the three most recent releases are kept
|
|
(since December 2023; customers whose maintenance lapsed must purchase to upgrade, and the older
|
|
versions are kept only to diagnose a regression). List the folder, then remove the oldest; at
|
|
8.3.0 that is 8.2.2:
|
|
|
|
```bash
|
|
ls /var/www/html/ayanova.com/download/old
|
|
```
|
|
|
|
```bash
|
|
rm -r /var/www/html/ayanova.com/download/old/2024-10-23-v8.2.2
|
|
```
|
|
|
|
```bash
|
|
cp /var/www/html/ayanova.com/download/next/* /var/www/html/ayanova.com/download && cp /var/www/html/ayanova.com/download/latest-version.json /var/www/html/ayanova.com/download/previous-latest-version.json
|
|
```
|
|
|
|
Do **not** upload the new `latest-version.json` yet.
|
|
|
|
## 8. Upgrade the subscription droplets
|
|
|
|
On the normal Thursday schedule and times (or out of band for an emergency), as in the
|
|
[weekly update](#weekly-os-update): snapshot, health check, OS update, reboot; then any
|
|
one-time steps for this release, then the [release upgrade commands](#upgrade-commands); then the
|
|
health check and the log entry. Mark each server as upgraded in its Sockeye subscription server record.
|
|
|
|
## 9. Announce
|
|
|
|
Only after the last subscription droplet is upgraded, from PowerShell on the workstation (overwrites
|
|
the old one; from this moment every AyaNova installation shows that the new version is available):
|
|
|
|
```powershell
|
|
scp C:\data\code\raven\dist\latest-version.json root@ayanova.com:/var/www/html/ayanova.com/download/
|
|
```
|
|
|
|
## 10. Check what was published
|
|
|
|
Download every installer and package back from ayanova.com and check its version: Windows
|
|
installers by the .exe's ProductVersion, Linux zips by the `AyaNova.dll` inside. (Found
|
|
2026-10-25: the LAN installer on the website was still 8.2.3, so 8.2.4 had never been uploaded for LAN.)
|
|
|
|
```powershell
|
|
$ProgressPreference = 'SilentlyContinue'; $d = "$env:TEMP\ayanova-published"; Remove-Item $d -Recurse -Force -ErrorAction SilentlyContinue; New-Item -ItemType Directory $d | Out-Null; foreach ($f in 'ayanova-windows-x64-single-setup.exe','ayanova-windows-x64-lan-setup.exe','ayanova-linux-x64-server.zip','ayanova-linux-x64-desktop.zip','ayanova-subscription-linux-x64-server.zip') { Invoke-WebRequest "https://www.ayanova.com/download/$f" -OutFile "$d\$f" -UseBasicParsing; if ($f -like '*.exe') { "$f $((Get-Item "$d\$f").VersionInfo.ProductVersion)" } else { Expand-Archive "$d\$f" "$d\$($f -replace '\.zip$','')" ; "$f $((Get-Item "$d\$($f -replace '\.zip$','')\AyaNova.dll").VersionInfo.ProductVersion)" } }
|
|
```
|
|
|
|
Every line must show the new version.
|
|
|
|
## 11. Tell subscription customers
|
|
|
|
Subscription customers are told after their server was upgraded, not before. Perpetual licence
|
|
customers see the new version in AyaNova itself; tell one only if they are waiting for a
|
|
specific change. Message:
|
|
|
|
> Subject: Your AyaNova service was upgraded
|
|
>
|
|
> Your AyaNova service has been upgraded to the latest version.
|
|
>
|
|
> This release contains these features and improvements:
|
|
> https://ayanova.com/docs/changelog/#ayanova-830
|
|
>
|
|
> Be sure to backup your data regularly:
|
|
> https://ayanova.com/docs/sub-service-intro/
|
|
|
|
## 12. Start the next version
|
|
|
|
Version numbers follow [semantic versioning](https://semver.org/): MAJOR for incompatible API
|
|
changes, MINOR for new backward compatible functionality, PATCH for backward compatible fixes.
|
|
|
|
The version bump is the **first commit** of the next release, `case NNNN - version bump`, one
|
|
commit per repo, made when work on the next release starts. Set the new version in:
|
|
|
|
| Repo | File | What |
|
|
|---|---|---|
|
|
| raven | `server/AyaNova/AyaNova.csproj` | `<Version>` and `<FileVersion>` |
|
|
| raven | `server/AyaNova/util/AyaNovaVersion.cs` | `VersionString` |
|
|
| raven | `dist/install/windows/x64/lan.iss` | `#define MyAppVersion` |
|
|
| raven | `dist/install/windows/x64/standalone.iss` | `#define MyAppVersion` |
|
|
| raven | `dist/latest-version.json` | `latestversion` and the `changelogurl` anchor |
|
|
| raven | `docs/8.0/ayanova/docs/changelog.md` | new version heading with "IN PROGRESS" under it |
|
|
| raven | `docs/8.0/ayanova/docs/ay-about.md` | the example version number |
|
|
| raven-client | `ayanova/package.json`, `ayanova/package-lock.json` | `"version"` (both places in the lock file) |
|
|
| raven-client | `ayanova/src/api/ayanova-version.js` | `version` |
|
|
| raven-launcher | `ayanova-version.cs` | `VersionString` |
|
|
| raven-test | `e2e/cypress.json` | `expectedServerVersion` |
|
|
|
|
Searching each repo for the previous version number finds them all:
|
|
|
|
```powershell
|
|
git -C C:\data\code\raven grep -n -F "8.3.0" -- server dist docs/8.0/ayanova/docs/ay-about.md; git -C C:\data\code\raven-client grep -n -F "8.3.0" -- ayanova/package.json ayanova/src/api; git -C C:\data\code\raven-launcher grep -n -F "8.3.0"
|
|
```
|
|
|
|
QBI (`ravenqbi`) has its own version cycle (`install\qbi.iss`).
|
|
|
|
## Upgrade commands
|
|
|
|
Run on a subscription droplet as root. They stop AyaNova, download the new zip, replace the
|
|
program files (keeping `config.json` and everything in `data`), and start AyaNova again. `curl -f`
|
|
makes a failed download stop the whole chain before anything is deleted; without it a missing
|
|
file would be saved as an error page, the program files deleted and the unzip would fail, leaving
|
|
the server down until the snapshot is restored.
|
|
|
|
**Next version, for testing on devtest** (from the `next` folder):
|
|
|
|
```bash
|
|
cd /var/ayanova && systemctl stop ayanova.service && curl -fO https://www.ayanova.com/download/next/ayanova-subscription-linux-x64-server.zip && rm *.dll && rm -r ./resource && rm -r ./wwwroot && unzip -o ayanova-subscription-linux-x64-server.zip -x "config.json" && systemctl start ayanova.service
|
|
```
|
|
|
|
**Release version, for customer droplets:**
|
|
|
|
```bash
|
|
cd /var/ayanova && systemctl stop ayanova.service && curl -fO https://www.ayanova.com/download/ayanova-subscription-linux-x64-server.zip && rm *.dll && rm -r ./resource && rm -r ./wwwroot && unzip -o ayanova-subscription-linux-x64-server.zip -x "config.json" && systemctl start ayanova.service
|
|
```
|
|
|
|
Then `reboot`, and the health check: `https://<server>.onayanova.com/health`.
|
|
|
|
## One-time steps for 8.3.0
|
|
|
|
Every existing droplet, once, right before its 8.3.0 upgrade. Both are safe to run again.
|
|
|
|
**.NET 10 runtime** (8.3.0 needs it; on Ubuntu 24.04 and 26.04 it comes from Ubuntu's own archive):
|
|
|
|
```bash
|
|
apt-get install -y aspnetcore-runtime-10.0
|
|
```
|
|
|
|
**Hardening** (case 4727; what the setup script now does for new droplets): AyaNova listens only on
|
|
127.0.0.1, the program folder becomes read only for the service account, `config.json` 640, the
|
|
old 8.2.x Chrome folders are removed, and the systemd security settings are added as a drop-in.
|
|
Tested 2026-10-26 on `devtestn10`. Save it on the droplet with `nano /root/harden-830.sh`, paste,
|
|
save, then run `bash /root/harden-830.sh`; it must end with `HARDENING DONE`. It takes effect when
|
|
the upgrade commands start AyaNova.
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# AyaNova 8.3.0: one-time hardening of an existing subscription droplet (case 4727, F36 and F38).
|
|
# Run once per droplet as root, right before the 8.3.0 upgrade commands. Safe to run again.
|
|
set -e
|
|
|
|
# safety copies, kept from the first run only
|
|
[ -e /root/config.json.before-8.3.0 ] || cp /var/ayanova/config.json /root/config.json.before-8.3.0
|
|
[ -e /root/ayanova.service.before-8.3.0 ] || cp /etc/systemd/system/ayanova.service /root/ayanova.service.before-8.3.0
|
|
|
|
# F36: nginx is the only way in: listen on this server only, not on its public address
|
|
sed -i 's#http://[*]:7575#http://127.0.0.1:7575#' /var/ayanova/config.json
|
|
grep -q '"http://127.0.0.1:7575"' /var/ayanova/config.json
|
|
|
|
# F38: program files read only for the service account; it writes to data only
|
|
chmod -R go-w /var/ayanova
|
|
chmod -R g+rw /var/ayanova/data
|
|
# config.json holds the database password: readable by root and the service group only
|
|
chown root:www-data /var/ayanova/config.json
|
|
chmod 640 /var/ayanova/config.json
|
|
# 8.2.x report browser folders; 8.3.0 downloads the browser into data/report-browser
|
|
rm -rf /var/ayanova/Chrome /var/ayanova/ChromeHeadlessShell
|
|
|
|
# F38: systemd sandboxing, as a drop-in so the existing unit file is left as it is
|
|
mkdir -p /etc/systemd/system/ayanova.service.d
|
|
cat > /etc/systemd/system/ayanova.service.d/hardening.conf <<'EOF'
|
|
[Service]
|
|
NoNewPrivileges=true
|
|
ProtectSystem=strict
|
|
ReadWritePaths=/var/ayanova/data
|
|
PrivateTmp=true
|
|
ProtectHome=true
|
|
EOF
|
|
systemctl daemon-reload
|
|
|
|
echo HARDENING DONE
|
|
```
|
|
|
|
After the upgrade, check that AyaNova listens only on 127.0.0.1 (`ss -tln | grep 7575` shows
|
|
`127.0.0.1:7575`) and that the health check passes.
|
|
|
|
## Weekly OS update
|
|
|
|
Every Thursday, Ubuntu updates on every subscription droplet. Customers have been told updates
|
|
happen on Thursday nights after 7 pm their time (downtime under a minute; none has asked for
|
|
another time). All droplets have phased updates turned off, so they get the same packages
|
|
devtest was tested with (`/etc/apt/apt.conf.d/52no-phasing`; the setup script adds it to new droplets).
|
|
|
|
Order: devtest first (it mirrors the customer droplets), then customers in groups by time zone,
|
|
at John's 7 am (Singapore, Australia), 4 pm (US east coast and Europe, most servers) and 7 pm
|
|
(US west coast).
|
|
|
|
Per droplet:
|
|
|
|
1. In the DigitalOcean control panel: delete last week's snapshot, take a new one.
|
|
2. Confirm the server is healthy: `https://<server>.onayanova.com/health`.
|
|
3. SSH in, remove last week's leftovers, update, reboot (always reboot: skipping it caused intermittent problems in the past):
|
|
|
|
```bash
|
|
apt autoremove
|
|
```
|
|
|
|
```bash
|
|
apt-get update && apt-get -V full-upgrade
|
|
```
|
|
|
|
```bash
|
|
reboot
|
|
```
|
|
|
|
4. Copy the session's console output into the week's log file (kept until the next week's updates, to look back at if something comes up).
|
|
5. Mark the date and time of the update in the server's Sockeye subscription server record.
|
|
6. Refresh the server's AyaNova page and confirm the login page appears; open `/health` in place of `/login` and paste the result into the week's log as the last entry for that server.
|