Summary#
Osy# does not keep an old spelling alive beside a new one: when a form is renamed or removed, source that writes the old form is refused, and the refusal names what to write instead. That is the right answer for code you are editing — and the wrong one for an app that is already serving people, which a platform release rebuilds from the source it was deployed with. So every retired form carries two versions:
| what it means | |
|---|---|
| deprecated since | the release that retired it. From here on, compiling SOURCE that writes it is refused. |
| refused from | the release from which even a deployed app's rebuild refuses it — by default the next minor. |
Between the two, a release rebuilds a deployed app that still writes the form exactly as before, and records a warning on
the app's version naming the form and its deadline. Before the deadline, deploy the app again from its source, written the
new way — osy migrate code rewrites every form that has a mechanical rewrite.
Signature#
$ osy migrate code # rewrite this project's retired forms, then validate again
$ osy migrate code --dry-run # say what would be rewritten, write nothing
$ osy migrate code --json # every rewrite, and every form left for you// in the kit's package.osy
deprecated "<Control>(<its parameters as they were declared>)" {
since "<platform version>"; // required
use "<what to write instead>"; // required
why "<one line>"; // optional
refusedFrom "<platform version>"; // optional: the next minor after `since` when left out
}Description#
What happens when I compile a retired form?#
osy validate, osy compile and a deploy refuse it, with the compiler's sentence for that form and the replacement:
model/jobs.osy:3:2 ERROR `[OwnUnitOfWork]` is now `[CommitsAlone]` — write `[CommitsAlone]` on 'Record'. The meaning
is unchanged: its writes commit by themselves when it returns and survive the caller failing. (`osy migrate code` rewrites it.)The last sentence is there when the rewrite is mechanical. A form whose replacement behaves differently in some case is refused without one, and the message says what differs.
What happens to an app that is already deployed?#
A platform release rebuilds every deployed app from the source its version recorded. When that source writes a retired form and the release is before the form's refused from, the rebuild rewrites the form in memory, compiles, and serves the app as it was. The version keeps the source it was deployed with, and records the form on its history:
model/jobs.osy:3 uses [OwnUnitOfWork], refused from 0.17.0 — write [CommitsAlone]The same line is logged as a warning by the release, and listed by the operator's check of every deployed app
(osycloud platform compile-recorded), so the apps still using a retired form are known before the release that refuses
them.
When does a deployed app stop rebuilding?#
From refused from on, the rebuild refuses the form like any other source, and says why:
model/jobs.osy:3 ERROR DEPRECATED_FORM this recorded version uses [OwnUnitOfWork], which a rebuild accepted until 0.17.0
(this platform is 0.17.0) — deploy it again from its source, written [CommitsAlone].Deploying the app again from its project, written the new way, is the whole fix: the new version records the new source.
How do I rewrite my project?#
Run osy migrate code in the project. It validates the project, takes the rewrite each refusal of a retired form carries,
writes it into the file, and validates again — a second round can find forms the first could not see (a call on what a
retired factory returned is refused only once the factory is rewritten). It stops when nothing more can be rewritten, and
lists every retired form it left in place with what to write by hand.
Only the project's own files are written. A retired form inside a kit the project uses is the kit's to retire: the
command names it, and a newer version of the kit (osy update) is how the fix arrives.
After it, the project compiles as the current language writes it:
entity Reading {
[MaxLength(100)] string Text = "";
security { allow read, create when IsAuthenticated; }
}
[CommitsAlone] void Record(string t) { new Reading { Text = t }; }
TimeSpan OffsetNow() { return TimeZoneInfo.FindSystemTimeZoneById("UTC").GetUtcOffset(DateTime.UtcNow); }
DateTime WallClock(DateTime instant) { return TimeZoneInfo.ConvertTimeFromUtc(instant, TimeZoneInfo.FindSystemTimeZoneById("UTC")); }
int ZonesOnOffer() { return TimeZoneInfo.GetSystemTimeZones().ToArray().Length; }Which forms are retired?#
| retired form | write instead | deprecated since | refused from | rewrite |
|---|---|---|---|---|
[OwnUnitOfWork] | [CommitsAlone] | 0.16.0 | 0.17.0 | yes |
Zone (a type) | TimeZoneInfo | 0.16.0 | 0.17.0 | yes |
Zone.Of(id), Zone(id) | TimeZoneInfo.FindSystemTimeZoneById(id) | 0.16.0 | 0.17.0 | yes |
Zone.Declared | TimeZoneInfo.GetSystemTimeZones() (as an array: .ToArray()) | 0.16.0 | 0.17.0 | yes |
instant.InZone(zone) | TimeZoneInfo.ConvertTimeFromUtc(instant, zone) | 0.16.0 | 0.17.0 | yes |
zone.OffsetAt(instant) | zone.GetUtcOffset(instant) | 0.16.0 | 0.17.0 | yes |
zone.IsDst(instant) | zone.IsDaylightSavingTime(instant) | 0.16.0 | 0.17.0 | yes |
zone.Resolve(date, time) | TimeZoneInfo.ConvertTimeToUtc(date.ToDateTime(time), zone) | 0.16.0 | 0.17.0 | yes — kept exact: C# throws on a time inside a spring-forward gap, where Resolve read it forward, so the rewrite asks zone.IsInvalidTime first and reads it forward by the gap's width. If your times never fall in a gap, the plain C# call is the same |
ProgressBar(int percent = 0, Tone tone = Tone.Primary, string label = "") (Osysharp.Ui) | ProgressBar(label, percent, tone) | 0.16.0 | 0.17.0 | yes |
Facts(string label = "") (Osysharp.Ui) | Facts(label) | 0.16.0 | 0.17.0 | yes |
ZonePicker(string label, Binding<Zone> value, bool disabled = false) (Osysharp.Ui) | TimeZonePicker(label, value, disabled) | 0.16.2 | 0.18.0 | yes — the name |
ZoneWords(Zone zone) (Osysharp.Ui) | TimeZoneWords(zone) | 0.16.2 | 0.18.0 | yes — the name |
What does the ProgressBar rewrite write?#
A call that binds a control only as its kit declared it before is rewritten with named arguments, onto the control as it is now. A parameter the control now requires, and the former signature gave a default, takes that default — so a rebuilt app renders exactly what it rendered:
ProgressBar(72, Tone.Success) → ProgressBar(label: "", percent: 72, tone: Tone.Success)osy migrate code says when it wrote a former default. Replace label: "" with what the bar measures: a progress bar
with no name is announced by a screen reader as a percentage of nothing.
How does a kit retire part of its own surface?#
A kit declares the former signature of a control in its own package.osy, one deprecated entry per retired form:
package Acme.Gauges {
version "2.0.0";
minPlatform "0.16.1";
model "*.osy";
deprecated "Gauge(int value = 0, string caption = \"\")" {
since "0.16.1";
use "Gauge(caption, value)";
why "a gauge says what it measures, so `caption` is required and first";
}
}The value is the control's name and parameter list exactly as they were declared. A call that does not bind the control as
it is now, and does bind the former signature, is refused as the retired form — with its rewrite when every former
parameter still exists and every newly required one had a default. since is the platform release the kit's change ships
with, and the window runs from there to refusedFrom like the platform's own.
How does a kit rename a control or a function?#
The same entry, with the new name in use. It works for a component and for a public function:
deprecated "Dial(int value)" {
since "0.17.2";
use "Gauge(value)";
why "every control in the kit is named after what it shows";
}A call to the former name is refused as the retired form, and its rewrite replaces the name and keeps the arguments as
written. A rebuild applies it, and so does osy migrate code.
Does retiring a form need a major version of the kit?#
No. A form with a deprecated entry is still offered: an app that wrote it keeps rebuilding, and osy migrate code
rewrites its source. So osy publish and osy package pack --since don't count it as removed, and the kit's next version
can be a minor. Without an entry, the form is removed, and the kit has to take a new major version. Deleting an entry later
removes the form, so that release needs a major too.
See also#
- Moving runs onto the version you just deployed —
osy migratewithoutcodemoves workflow runs, not source - [CommitsAlone] — a function whose writes land by themselves —
[CommitsAlone], the attribute[OwnUnitOfWork]became - Time zones — TimeZoneInfo —
TimeZoneInfo, the typeZonebecame - package.osy — the rest of
package.osy - minPlatform — the oldest platform an app runs on — the oldest platform an app runs on
- Deploying while workflows are running — what a deployed version is