Test builds go stale after a fortnight
A tester still racing a two-week-old binary reports things that were fixed a week ago, and the afternoon spent chasing them is gone. An expired build now says so and stops: a dialog naming its version and expiry date, pointing at the releases page, and an exit before anything else runs. The log carries the same line, so a report from an expired build identifies itself. $expireDays at the top of stamp-version.ps1 is the shelf life, sitting next to the product line it belongs with. It counts from the day a build was MADE rather than the day the code was written - rebuilding an old commit to chase something should hand back a usable binary, not one born stale. SET IT TO 0 FOR A REAL RELEASE. A shipped build that expires is a catastrophe, and that one line decides it. It is called out in the script, in the generated header and in BUILD.md, because it is the kind of thing that gets noticed exactly once, too late. The date is what makes rpl4build.h differ from one day to the next, so the first build of each day recompiles RPL4.CPP and the rest do not. This is a nudge, not a lock. The date comes from the machine's own clock and anyone determined can wind it back; the point is to stop an honest tester wasting a day, not to stop anybody at all. RP412NOEXPIRY=1 waives it for us and logs that it did, so a waived build is never mistaken for a current one. It is deliberately absent from environ.ini - a bypass every tester can see is a bypass every tester will use, and then it never goes stale for the one person it was meant to stop. Verified all four ways by backdating the shelf life rather than touching the clock, which is what a negative $expireDays is for: a fresh build runs untouched; an expired one raises the dialog, exits 1, and logs "Build expired on 4 August 2026 - refusing to run"; the same expired build with RP412NOEXPIRY=1 runs and logs the waiver; and a build with two days left runs and logs two days left. Two things that only showed up by running it. Negative days first meant "never" rather than "already expired", so the refusal path went untested on the first pass - only 0 means never now. And the days-left count was anchored at midday, reporting one day fewer than the build had; it is anchored at the end of the expiry day, which is the rule the check actually enforces. The packaged README tells testers the build expires, where to get the next one, and that unzipping it over the folder keeps their four files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,24 @@ means the tree had uncommitted changes to tracked files when it was built —
|
||||
useful when a test machine reports something a clean build cannot reproduce.
|
||||
Only the `4.12` product line is set by hand, at the top of the script.
|
||||
|
||||
**Test builds expire.** `$expireDays` at the top of the same script is the
|
||||
shelf life in days (currently **14**, counted from the day it was *built*,
|
||||
not the day the code was written — rebuilding an old commit gives a usable
|
||||
binary rather than one born stale). An expired build says so in a dialog,
|
||||
names its version and expiry date, points at the releases page, and exits
|
||||
without running. It stops a tester spending an afternoon on something that
|
||||
was fixed a week ago.
|
||||
|
||||
> ⚠️ **Set `$expireDays = 0` for a real release.** A shipped build that
|
||||
> expires is a catastrophe, and that one line is what decides it.
|
||||
|
||||
`RP412NOEXPIRY=1` waives the check when an old build has to be run on
|
||||
purpose, and says so in the log so a waived build is never mistaken for a
|
||||
current one. It is deliberately **not** listed in `environ.ini` — a bypass
|
||||
every tester can see is a bypass every tester will use. Negative
|
||||
`$expireDays` backdates the expiry, which is how the refusal gets tested
|
||||
without touching the machine's clock.
|
||||
|
||||
The header is deliberately not committed: the commit that recorded a
|
||||
hardcoded number would itself change the count, so the file would be stale
|
||||
the moment it landed. It is rewritten only when the stamp actually changes,
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <strsafe.h>
|
||||
#include <direct.h>
|
||||
#include <shellapi.h>
|
||||
#include <time.h> // the test-build expiry check
|
||||
|
||||
#define SPOOL_SIZE 0x600000
|
||||
|
||||
@@ -182,6 +183,98 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
//
|
||||
RPL4Environ_Load();
|
||||
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
// Test builds have a shelf life.
|
||||
//
|
||||
// A tester still racing a fortnight-old binary reports things that were
|
||||
// fixed a week ago, and the afternoon spent chasing them is gone. So the
|
||||
// build says plainly that it is out of date and stops, rather than
|
||||
// running on and being quietly wrong about what it is.
|
||||
//
|
||||
// This is a nudge, not a lock: the date comes from the machine's own
|
||||
// clock, so anyone determined can wind it back, and RP412NOEXPIRY=1 is
|
||||
// there for us when an old build has to be run on purpose. It is
|
||||
// deliberately not listed in environ.ini - a bypass every tester can see
|
||||
// is a bypass every tester will use, and then the build never goes stale
|
||||
// for the one person it was meant to stop.
|
||||
//
|
||||
// $expireDays in stamp-version.ps1 is what sets this, and 0 turns it off
|
||||
// for a real release.
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
#if RP412_EXPIRES
|
||||
{
|
||||
const char *no_expiry = getenv("RP412NOEXPIRY");
|
||||
Logical overridden = (no_expiry != NULL && atoi(no_expiry) != 0);
|
||||
|
||||
__time64_t raw_now = _time64(NULL);
|
||||
struct tm today;
|
||||
if (!overridden && _localtime64_s(&today, &raw_now) == 0)
|
||||
{
|
||||
int now_stamp =
|
||||
(today.tm_year + 1900) * 10000 + (today.tm_mon + 1) * 100 + today.tm_mday;
|
||||
int expiry_stamp =
|
||||
RP412_EXPIRY_YEAR * 10000 + RP412_EXPIRY_MONTH * 100 + RP412_EXPIRY_DAY;
|
||||
|
||||
if (now_stamp > expiry_stamp)
|
||||
{
|
||||
DEBUG_STREAM << "Build expired on " << RP412_EXPIRY_TEXT
|
||||
<< " - refusing to run\n" << std::flush;
|
||||
|
||||
char notice[512];
|
||||
sprintf(notice,
|
||||
"This Red Planet test build has expired.\n\n"
|
||||
" Build %s\n"
|
||||
" Expired %s\n\n"
|
||||
"Test builds are good for a fortnight so that nobody spends an "
|
||||
"afternoon chasing something that was fixed a week ago.\n\n"
|
||||
"Grab the current one:\n"
|
||||
"https://gitea.mysticmachines.com/VWE/RP412/releases",
|
||||
RP412_VERSION_LONG, RP412_EXPIRY_TEXT);
|
||||
MessageBoxA(NULL, notice, "Red Planet - test build expired",
|
||||
MB_OK | MB_ICONWARNING | MB_SETFOREGROUND);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// The last few days get a line in the log, so somebody reading a
|
||||
// report can see the build was nearly out rather than wondering.
|
||||
//
|
||||
struct tm expiry_day;
|
||||
memset(&expiry_day, 0, sizeof(expiry_day));
|
||||
expiry_day.tm_year = RP412_EXPIRY_YEAR - 1900;
|
||||
expiry_day.tm_mon = RP412_EXPIRY_MONTH - 1;
|
||||
expiry_day.tm_mday = RP412_EXPIRY_DAY;
|
||||
//
|
||||
// The END of the expiry day, because that is the rule the check
|
||||
// above enforces - the build is good for all of that date and
|
||||
// refuses the morning after. Anchoring at midday instead would
|
||||
// report one day fewer than the build actually has left.
|
||||
//
|
||||
expiry_day.tm_hour = 23;
|
||||
expiry_day.tm_min = 59;
|
||||
expiry_day.tm_sec = 59;
|
||||
expiry_day.tm_isdst = -1;
|
||||
__time64_t expiry_time = _mktime64(&expiry_day);
|
||||
if (expiry_time != (__time64_t) -1)
|
||||
{
|
||||
int days_left = (int)((expiry_time - raw_now) / (24 * 60 * 60));
|
||||
if (days_left <= 3)
|
||||
{
|
||||
DEBUG_STREAM << "Build expires " << RP412_EXPIRY_TEXT << " ("
|
||||
<< days_left << " day(s) left)\n" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (overridden)
|
||||
{
|
||||
DEBUG_STREAM << "Build expiry (" << RP412_EXPIRY_TEXT
|
||||
<< ") waived by RP412NOEXPIRY\n" << std::flush;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
DEBUG_STREAM << "L4CONTROLS=" << getenv("L4CONTROLS") << std::endl << std::flush;
|
||||
|
||||
#ifdef RP412_STEAM
|
||||
|
||||
@@ -282,6 +282,16 @@ through several updates stops being offered new options - rpl4.log names
|
||||
any it has not heard of, and deleting the file brings back the fully
|
||||
documented current one.
|
||||
|
||||
THIS IS A TEST BUILD AND IT GOES STALE. Two weeks after it was built it
|
||||
will tell you so and refuse to start, rather than let you spend an
|
||||
afternoon on something that was fixed the week before. When that happens,
|
||||
grab the current build:
|
||||
|
||||
https://gitea.mysticmachines.com/VWE/RP412/releases
|
||||
|
||||
Unzip it over this folder - your environ.ini, bindings.txt, pilot.cfg and
|
||||
mfd_layout.cfg all survive.
|
||||
|
||||
Known prototype notes: pods race untextured (the player1-8 skins come
|
||||
from the presets system, not shipped data), and text drawn on the plasma
|
||||
glass may appear rotated.
|
||||
|
||||
+47
-2
@@ -18,6 +18,15 @@ $ErrorActionPreference = 'Stop'
|
||||
# number after it looks after itself.
|
||||
$line = '4.12'
|
||||
|
||||
# How many days a build stays good for. Test builds go stale so nobody is
|
||||
# still racing a fortnight-old binary and reporting things that were fixed
|
||||
# a week ago - the expired build says so and stops, rather than quietly
|
||||
# wasting everyone's afternoon.
|
||||
#
|
||||
# SET THIS TO 0 FOR A REAL RELEASE. A shipped build that expires is a
|
||||
# catastrophe, and this is the one line that decides it.
|
||||
$expireDays = 14
|
||||
|
||||
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$header = Join-Path $root 'RP_L4\rpl4build.h'
|
||||
|
||||
@@ -62,6 +71,32 @@ if ($count -eq 0) {
|
||||
$long = "$version ($commit$(if ($dirty) { '+' } else { '' }))"
|
||||
}
|
||||
|
||||
#
|
||||
# Expiry, from the day it was BUILT rather than the day the code was
|
||||
# written: rebuilding an old commit to chase something should give a
|
||||
# usable binary, not one that was born stale.
|
||||
#
|
||||
# The date is what makes this header change from one day to the next, so
|
||||
# the first build of each day recompiles RPL4.CPP and the rest of that
|
||||
# day's builds do not. One file, a couple of seconds.
|
||||
#
|
||||
# Only 0 means "never". A NEGATIVE count backdates the expiry, which is how
|
||||
# the refusal gets tested without touching the machine's clock.
|
||||
if ($expireDays -ne 0) {
|
||||
$expiry = (Get-Date).Date.AddDays($expireDays)
|
||||
$expires = 1
|
||||
$expiryY = $expiry.Year
|
||||
$expiryM = $expiry.Month
|
||||
$expiryD = $expiry.Day
|
||||
$expiryTxt = $expiry.ToString('d MMMM yyyy', [Globalization.CultureInfo]::InvariantCulture)
|
||||
} else {
|
||||
$expires = 0
|
||||
$expiryY = 0
|
||||
$expiryM = 0
|
||||
$expiryD = 0
|
||||
$expiryTxt = 'never'
|
||||
}
|
||||
|
||||
$content = @"
|
||||
//===========================================================================//
|
||||
// File: rpl4build.h GENERATED - do not edit, do not commit //
|
||||
@@ -70,6 +105,9 @@ $content = @"
|
||||
// is the repository's commit count and the hash beside it names the exact //
|
||||
// commit, so a running build always says where it came from. A trailing '+' //
|
||||
// means the tree had uncommitted changes to tracked files when it was built. //
|
||||
// //
|
||||
// EXPIRES is the test-build shelf life - see \$expireDays in the script. A //
|
||||
// release must be built with it at 0. //
|
||||
//===========================================================================//
|
||||
|
||||
#pragma once
|
||||
@@ -79,6 +117,12 @@ $content = @"
|
||||
#define RP412_BUILD_DIRTY $dirty
|
||||
#define RP412_VERSION "$version"
|
||||
#define RP412_VERSION_LONG "$long"
|
||||
|
||||
#define RP412_EXPIRES $expires
|
||||
#define RP412_EXPIRY_YEAR $expiryY
|
||||
#define RP412_EXPIRY_MONTH $expiryM
|
||||
#define RP412_EXPIRY_DAY $expiryD
|
||||
#define RP412_EXPIRY_TEXT "$expiryTxt"
|
||||
"@
|
||||
|
||||
#
|
||||
@@ -87,11 +131,12 @@ $content = @"
|
||||
# recompile each time.
|
||||
#
|
||||
$existing = if (Test-Path $header) { [IO.File]::ReadAllText($header) } else { '' }
|
||||
$note = if ($expires) { "expires $expiryTxt" } else { "no expiry" }
|
||||
if ($existing -ne $content) {
|
||||
[IO.File]::WriteAllText($header, $content, (New-Object System.Text.ASCIIEncoding))
|
||||
Write-Host "stamp-version: $long"
|
||||
Write-Host "stamp-version: $long, $note"
|
||||
} else {
|
||||
Write-Host "stamp-version: $long (unchanged)"
|
||||
Write-Host "stamp-version: $long, $note (unchanged)"
|
||||
}
|
||||
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user