deployment-tools
deployment-tools copied to clipboard
Productize DOM APIs for release.json
@joeloff is working on this. The APIs can be reviewed here (internal only link, sorry). The APIs are here:
API Propsal
namespace Microsoft.Deployment.DotNet.Releases {
public class AspNetCoreReleaseComponent : ReleaseComponent {
public IReadOnlyCollection<string> AspNetCoreModuleVersions { get; }
public string VisualStudioVersion { get; }
}
public class Cve {
public Cve();
public string Id { get; }
public Uri Url { get; }
public override bool Equals(object obj);
public override int GetHashCode();
}
public class Product {
public Product();
public DateTime? EndOfLifeDate { get; }
public bool IsSecurityUpdate { get; }
public DateTime? LatestReleaseDate { get; }
public ReleaseVersion LatestReleaseVersion { get; }
public ReleaseVersion LatestRuntimeVersion { get; }
public ReleaseVersion LatestSdkVersion { get; }
public string ProductName { get; }
public string ProductVersion { get; }
public Uri ReleasesJson { get; }
public SupportPhase SupportPhase { get; }
public static Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(Uri releasesJsonUrl);
public Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync();
public Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(string releasesIndexJsonPath, bool downloadLatest);
public bool IsOutOfSupport();
}
public sealed class ProductCollection : ReadOnlyCollection<Product> {
public static readonly Uri ReleaseIndexDefaultUrl;
public static Task<ProductCollection> CreateAsync();
public static Task<ProductCollection> CreateAsync(string releasesIndexJsonPath, bool downloadLatest);
public static Task<ProductCollection> CreateAsync(Uri releasesIndexUrl);
public IEnumerable<SupportPhase> GetSupportPhases();
}
public class ProductRelease {
public AspNetCoreReleaseComponent AspNetCoreRuntime { get; }
public IReadOnlyCollection<ReleaseComponent> Components { get; }
public IReadOnlyCollection<Cve> Cves { get; }
public IReadOnlyCollection<ReleaseFile> Files { get; }
public bool IsPreview { get; }
public bool IsSecurityUpdate { get; }
public DateTime ReleaseDate { get; }
public Uri ReleaseNotes { get; }
public RuntimeReleaseComponent Runtime { get; }
public IEnumerable<ReleaseComponent> Runtimes { get; }
public IReadOnlyCollection<SdkReleaseComponent> Sdks { get; }
public ReleaseVersion Version { get; }
public WindowsDesktopReleaseComponent WindowsDesktopRuntime { get; }
}
public abstract class ReleaseComponent {
public ReleaseVersion DisplayVersion { get; }
public IReadOnlyCollection<ReleaseFile> Files { get; }
public string Name { get; protected set; }
public ProductRelease Release { get; }
public ReleaseVersion Version { get; }
}
public class ReleaseFile {
public ReleaseFile();
public string FileName { get; }
public string Hash { get; }
public string Name { get; }
public string Rid { get; }
public Uri Url { get; }
public Task DownloadAsync(string fileName);
public Task DownloadAsync(string fileName, bool verifyHash);
public override bool Equals(object obj);
public override int GetHashCode();
}
public class ReleaseVersion : IComparable, IComparable<ReleaseVersion>, IEquatable<ReleaseVersion>, ICloneable {
public ReleaseVersion(string version);
public ReleaseVersion();
public static readonly string Version2Pattern;
public string BuildMetadata { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
public int Patch { get; set; }
public string Prerelease { get; set; }
public int SdkFeatureBand { get; }
public int SdkPatchLevel { get; }
public static int Compare(ReleaseVersion a, ReleaseVersion b);
public static bool Equals(ReleaseVersion a, ReleaseVersion b);
public static bool operator ==(ReleaseVersion a, ReleaseVersion b);
public static bool operator >(ReleaseVersion a, ReleaseVersion b);
public static bool operator >=(ReleaseVersion a, ReleaseVersion b);
public static bool operator !=(ReleaseVersion a, ReleaseVersion b);
public static bool operator <(ReleaseVersion a, ReleaseVersion b);
public static bool operator <=(ReleaseVersion a, ReleaseVersion b);
public object Clone();
public int ComparePrecedence(ReleaseVersion value);
public int CompareTo(object value);
public int CompareTo(ReleaseVersion value);
public bool Equals(ReleaseVersion obj);
public bool PrecedenceEquals(ReleaseVersion value);
public string ToString(int fieldCount);
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
}
public class ReleaseVersionConverter : JsonConverter<ReleaseVersion> {
public ReleaseVersionConverter();
public override ReleaseVersion ReadJson(JsonReader reader, Type objectType, ReleaseVersion existingValue, bool hasExistingValue, JsonSerializer serializer);
public override void WriteJson(JsonWriter writer, ReleaseVersion value, JsonSerializer serializer);
}
public class RuntimeReleaseComponent : ReleaseComponent {
public string VisualStudioMacVersion { get; }
public string VisualStudioVersion { get; }
}
public class SdkReleaseComponent : ReleaseComponent {
public string CSharpVersion { get; }
public string FSharpVersion { get; }
public ReleaseVersion RuntimeVersion { get; }
public string VisualBasicVersion { get; }
public string VisualStudioMacSupport { get; }
public string VisualStudioMacVersion { get; }
public string VisualStudioSupport { get; }
public string VisualStudioVersion { get; }
}
public class SupportPhaseConverter : StringEnumConverter {
public SupportPhaseConverter();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer);
}
public class WindowsDesktopReleaseComponent : ReleaseComponent {
}
public enum SupportPhase {
Unknown = 0,
[EnumMember]
EndOfLife = 1,
Maintenance = 2,
[EnumMember]
LongTermSupport = 3,
Preview = 4,
RC = 5,
}
}
- Provides a public API for the release.json
- Distributed as a NuGet package
- Depends on
Newtonsoft.Json - Is
Microsoft.Deployment.Releasesthe right namespace? Should we fit into the other namespaces used by the SDK tooling, which AFAIK is in inMicrosoft.DotNet. - Currently Windows-only (because it targets .NET Framework) but it can be fully cross-platform
- General notes:
- Instead of exposing
IEnumerable<T>use collection typesCollection<T>andReadOnlyCollection<T>because they avoid leaking the underlying instance that holds the data. It also allows you to derive from to add custom indexers/methods. - Using
DateTimeoverDateTimeOffsetfor data that has no time component is correct
- Instead of exposing
ReleaseIndex- It's a subset of
ReleaseChannel. It would be better if these types would be merged and the additional data is lazily loaded. For exampleProductwould represent the data that is in the root JSON file andProductDetailswould contain the entirety of the product that comes from another JSON file. They key is to let the user initiate each separate IO request. Should also return data async.
- It's a subset of
ProductVersionReleaseVersion
ReleaseChannelReleaseChannelshould be namedProductSupportPhaseshould be an enum- It's weird to have
LatestReleaseandGetLatestRelease(). Either get rid of the method and make the property returnReleaseor remove the properties. - Having a nullable
LatestReleaseDateis odd - Shouldn't have public constructor
ReleaseRuntimesshould beComponents- Having both
SdkandSdksis odd. We should only exposeSdksbecause that's well defined AspNetCoreRuntimeshould beAspNetCoreComponentWindowsDesktopRuntimeshould beWindowsDesktopComponent
IRelease- It's odd to have a type called
Releasebut not implementingIRelease - Also, we should try to avoid interfaces and use abstract base types
- Should be named
ReleaseComponentand an abstract class
- It's odd to have a type called
SdkRelease- Should be named
SdkReleaseComponent
- Should be named
RuntimeRelease- Should be named
RuntimeReleaseComponent
- Should be named
AspNetCoreRuntimeRelease- Should be named
AspNetCoreReleaseComponent
- Should be named
WindowsDesktopRelease- Should be named
WindowsDesktopReleaseComponent
- Should be named
namespace Microsoft.Deployment.Releases {
public interface IRelease {
IEnumerable<ReleaseFile> Files { get; set; }
string Name { get; }
ReleaseFlags ReleaseKind { get; }
ProductVersion Version { get; set; }
}
public class AspNetCoreRuntimeRelease : IRelease {
public AspNetCoreRuntimeRelease();
public string[] AspNetCoreModuleVersions { get; set; }
public string DisplayVersion { get; set; }
public IEnumerable<ReleaseFile> Files { get; set; }
public string Name { get; }
public ProductVersion Version { get; set; }
public string VisualStudioVersion { get; set; }
}
public class Cve {
public Cve();
public string Id { get; set; }
public Uri Url { get; set; }
public override bool Equals(object obj);
public override int GetHashCode();
}
public class ProductVersion : IComparable, IComparable<ProductVersion>, IEquatable<ProductVersion>, ICloneable {
public ProductVersion(string version);
public ProductVersion();
public static readonly string Version2Pattern;
public string BuildMetadata { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
public int Patch { get; set; }
public string Prerelease { get; set; }
public int SdkFeatureBand { get; }
public int SdkPatchLevel { get; }
public static int Compare(ProductVersion a, ProductVersion b);
public static bool Equals(ProductVersion a, ProductVersion b);
public static bool operator ==(ProductVersion v1, ProductVersion v2);
public static bool operator >(ProductVersion v1, ProductVersion v2);
public static bool operator >=(ProductVersion v1, ProductVersion v2);
public static bool operator !=(ProductVersion v1, ProductVersion v2);
public static bool operator <(ProductVersion v1, ProductVersion v2);
public static bool operator <=(ProductVersion v1, ProductVersion v2);
public object Clone();
public int ComparePrecedence(ProductVersion value);
public int CompareTo(object value);
public int CompareTo(ProductVersion value);
public bool Equals(ProductVersion obj);
public bool PrecedenceEquals(ProductVersion value);
public string ToString(int fieldCount);
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
}
public class ProductVersionConverter : JsonConverter<ProductVersion> {
public ProductVersionConverter();
public override ProductVersion ReadJson(JsonReader reader, Type objectType, ProductVersion existingValue, bool hasExistingValue, JsonSerializer serializer);
public override void WriteJson(JsonWriter writer, ProductVersion value, JsonSerializer serializer);
}
public class Release {
public Release();
public AspNetCoreRuntimeRelease AspNetCoreRuntime { get; set; }
public IEnumerable<Cve> Cves { get; set; }
public IEnumerable<ReleaseFile> Files { get; }
public bool IsPreview { get; }
public bool IsSecurityUpdate { get; set; }
public DateTime ReleaseDate { get; set; }
public Uri ReleaseNotes { get; set; }
public ProductVersion ReleaseVersion { get; set; }
public RuntimeRelease Runtime { get; set; }
public IEnumerable<IRelease> Runtimes { get; }
public SdkRelease Sdk { get; set; }
public IEnumerable<SdkRelease> Sdks { get; set; }
public WindowsDesktopRelease WindowsDesktop { get; set; }
}
public class ReleaseChannel {
public ReleaseChannel();
public string ChannelVersion { get; set; }
public DateTime? EolDate { get; set; }
public bool IsOutOfSupport { get; }
public ProductVersion LatestRelease { get; set; }
public DateTime? LatestReleaseDate { get; set; }
public ProductVersion LatestRuntime { get; set; }
public ProductVersion LatestSdk { get; set; }
public Uri LifeCyclePolicyUrl { get; set; }
public IEnumerable<Release> Releases { get; set; }
public SupportPhase SupportPhase { get; set; }
public Release GetLatestRelease();
public Release GetLatestRelease(bool isSecurityUpdate);
}
public class ReleaseFile {
public ReleaseFile();
public string FileName { get; }
public string Hash { get; }
public string Name { get; }
public string Rid { get; }
public Uri Url { get; }
public void Download(string fileName);
public override bool Equals(object obj);
public override int GetHashCode();
}
public class ReleaseIndex {
public ReleaseIndex();
public string ChannelVersion { get; set; }
public DateTime? EolDate { get; set; }
public bool IsOutOfSupport { get; }
public bool IsSecurity { get; set; }
public ProductVersion LatestRelease { get; set; }
public DateTime? LatestReleaseDate { get; set; }
public ProductVersion LatestRuntime { get; set; }
public ProductVersion LatestSdk { get; set; }
public string Product { get; set; }
public string ReleasesJson { get; set; }
public SupportPhase SupportPhase { get; set; }
}
public class Releases {
public Releases();
public static readonly Uri ReleasesIndexJsonUri;
public int ChannelCount { get; }
public IEnumerable<ReleaseChannel> Channels { get; }
public IEnumerable<string> ChannelVersions { get; }
public IEnumerable<ReleaseIndex> Index { get; set; }
public ReleaseChannel this[string channelVersion] { get; }
public static Releases CreateFromDefaultUrl();
public static Releases CreateFromFile(string releasesIndexPath);
public static Releases CreateFromFile(string releasesIndexPath, bool useLatest);
public static Releases CreateFromUrl(Uri uri);
public bool ContainsChannel(string channelVersion);
public Release GetRelease(ProductVersion releaseVersion);
public IEnumerable<Release> GetReleases(string cveId);
}
public class ReleasesHelpers {
public ReleasesHelpers();
public static T Create<T>(Uri jsonUrl);
public static T CreateFromFile<T>(string path);
}
public class RuntimeRelease : IRelease {
public RuntimeRelease();
public ProductVersion DisplayVersion { get; set; }
public IEnumerable<ReleaseFile> Files { get; set; }
public string Name { get; }
public ProductVersion Version { get; set; }
public string VisualStudioMacVersion { get; set; }
public string VisualStudioVersion { get; set; }
}
public class SdkRelease : IRelease {
public SdkRelease();
public string CSharpVersion { get; set; }
public ProductVersion DisplayVersion { get; set; }
public IEnumerable<ReleaseFile> Files { get; set; }
public string FSharpVersion { get; set; }
public string Name { get; }
public ProductVersion RuntimeVersion { get; set; }
public ProductVersion Version { get; set; }
public string VisualStudioMacSupport { get; set; }
public string VisualStudioMacVersion { get; set; }
public string VisualStudioSupport { get; set; }
public string VisualStudioVersion { get; set; }
}
public class WindowsDesktopRelease : IRelease {
public WindowsDesktopRelease();
public string DisplayVersion { get; set; }
public IEnumerable<ReleaseFile> Files { get; set; }
public string Name { get; }
public ProductVersion Version { get; set; }
}
[Flags]
public enum ReleaseFlags {
None = 0,
Sdk = 1,
Runtime = 2,
AspNetCoreRuntime = 4,
WindowsDesktopRuntime = 8,
}
public enum SupportPhase {
Undefined = 0,
[EnumMember]
EOL = 1,
[EnumMember]
LTS = 2,
[EnumMember]
Maintenance = 3,
[EnumMember]
Preview = 4,
}
}
With regards to the namespace question...I see this as something that the SDK might use, but most applications would be outside the SDK space, e.g. a global tool to query released products, installation managers, etc.
Would Microsoft.Deployment.DotNet.Products perhaps capture the intent better?
- Pick a strategy: either expose
IReadOnlyXxxor useCollection/ReadOnlyCollection<T>types but don't do both - Some types of the object model have (public) constructors while others don't. Pick one model.
- Is it possible that consumers of this API can observe inconsistencies when files are updated independently? Should this be done via a versioning scheme or simply by serving the files from a GitHub repo (so long we make sure that commits are consistent).
ProductCollectionReleaseIndexDefaultUrlshould be a get-only propertyCreateAsyncis odd. How aboutGetAsync()?- The parameters shouldn't use name
UrlorJson. - Don't overload between file paths and URLs (because we often also add string based overloads for URI based APIs)
- Add a string-based overload for URI
- Add
GetFromFilefor the file-based one
ProductIsSecurityUpdate. Should at least be renamedLatestReleaseIsSecurityUpdatebut we should consider removing it because the SDK has feature trains5.0.100and5.0.200that are for different VS releases. In which case only 200 is latest, which produce confusing/misleading results. It also seems the property is prone to errors for folks that want to check whether they are missing a security update (because this property is only true when the very latest release is a security update).- The static
GetReleasesAsync(Uri releasesJsonUrl)method should move toProductRelease
SupportPhase- We should probably not surface serialization attributes
- What does
Maintenancemean? Currentis missing
ProductRelease- Whatever policy you have on the
Product, such as whether something is go-liveSupportPhaseit seems the releases should be able to answer any questions as well, because releases are logically a point in time for a product. For example, we exposeIsPreviewbut notIsGoLive. - Considering a reference back to
Product
- Whatever policy you have on the
ReleaseComponentDisplayVersionshould be astringRuntimesshould be namedAllRuntimesand returnIReadOnlyCollection<T>
ReleaseVersion- This type is mutable, which we probably don't want. We should also remove
Clone()andICloneable - You have
ComparePrecedenceandPrecedenceEquals. It seems more consistent to usePrecedenceCompareToandPrecedenceEquals
- This type is mutable, which we probably don't want. We should also remove
ReleaseFile- We should make sure that hash validation is the default.
- When the validation fails we should delete the file
- We should negate the parameter to
skipHashValidation - Should we consider dropping the option of skipping entirely?
- Consider using
System.IO.InvalidDataException - The
fileNameparameter is a bit ambiguous because the instance also has one. MaybepathordestinationPath? - Should probably implement
IEquatable<>
ReleaseVersionConverterandSupportPhaseConverter- Should probably be internal
namespace Microsoft.Deployment.DotNet.Releases {
public class AspNetCoreReleaseComponent : ReleaseComponent {
public IReadOnlyCollection<string> AspNetCoreModuleVersions { get; }
public string VisualStudioVersion { get; }
}
public class Cve {
public Cve();
public string Id { get; }
public Uri Url { get; }
public override bool Equals(object obj);
public override int GetHashCode();
}
public class Product {
public Product();
public DateTime? EndOfLifeDate { get; }
public bool IsSecurityUpdate { get; }
public DateTime? LatestReleaseDate { get; }
public ReleaseVersion LatestReleaseVersion { get; }
public ReleaseVersion LatestRuntimeVersion { get; }
public ReleaseVersion LatestSdkVersion { get; }
public string ProductName { get; }
public string ProductVersion { get; }
public Uri ReleasesJson { get; }
public SupportPhase SupportPhase { get; }
public static Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(Uri releasesJsonUrl);
public Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync();
public Task<ReadOnlyCollection<ProductRelease>> GetReleasesAsync(string releasesIndexJsonPath, bool downloadLatest);
public bool IsOutOfSupport();
}
public sealed class ProductCollection : ReadOnlyCollection<Product> {
public static readonly Uri ReleaseIndexDefaultUrl;
public static Task<ProductCollection> CreateAsync();
public static Task<ProductCollection> CreateAsync(string releasesIndexJsonPath, bool downloadLatest);
public static Task<ProductCollection> CreateAsync(Uri releasesIndexUrl);
public IEnumerable<SupportPhase> GetSupportPhases();
}
public class ProductRelease {
public AspNetCoreReleaseComponent AspNetCoreRuntime { get; }
public IReadOnlyCollection<ReleaseComponent> Components { get; }
public IReadOnlyCollection<Cve> Cves { get; }
public IReadOnlyCollection<ReleaseFile> Files { get; }
public bool IsPreview { get; }
public bool IsSecurityUpdate { get; }
public DateTime ReleaseDate { get; }
public Uri ReleaseNotes { get; }
public RuntimeReleaseComponent Runtime { get; }
public IEnumerable<ReleaseComponent> Runtimes { get; }
public IReadOnlyCollection<SdkReleaseComponent> Sdks { get; }
public ReleaseVersion Version { get; }
public WindowsDesktopReleaseComponent WindowsDesktopRuntime { get; }
}
public abstract class ReleaseComponent {
public ReleaseVersion DisplayVersion { get; }
public IReadOnlyCollection<ReleaseFile> Files { get; }
public string Name { get; protected set; }
public ProductRelease Release { get; }
public ReleaseVersion Version { get; }
}
public class ReleaseFile {
public ReleaseFile();
public string FileName { get; }
public string Hash { get; }
public string Name { get; }
public string Rid { get; }
public Uri Url { get; }
public Task DownloadAsync(string fileName);
public Task DownloadAsync(string fileName, bool verifyHash);
public override bool Equals(object obj);
public override int GetHashCode();
}
public class ReleaseVersion : IComparable, IComparable<ReleaseVersion>, IEquatable<ReleaseVersion>, ICloneable {
public ReleaseVersion(string version);
public ReleaseVersion();
public static readonly string Version2Pattern;
public string BuildMetadata { get; set; }
public int Major { get; set; }
public int Minor { get; set; }
public int Patch { get; set; }
public string Prerelease { get; set; }
public int SdkFeatureBand { get; }
public int SdkPatchLevel { get; }
public static int Compare(ReleaseVersion a, ReleaseVersion b);
public static bool Equals(ReleaseVersion a, ReleaseVersion b);
public static bool operator ==(ReleaseVersion a, ReleaseVersion b);
public static bool operator >(ReleaseVersion a, ReleaseVersion b);
public static bool operator >=(ReleaseVersion a, ReleaseVersion b);
public static bool operator !=(ReleaseVersion a, ReleaseVersion b);
public static bool operator <(ReleaseVersion a, ReleaseVersion b);
public static bool operator <=(ReleaseVersion a, ReleaseVersion b);
public object Clone();
public int ComparePrecedence(ReleaseVersion value);
public int CompareTo(object value);
public int CompareTo(ReleaseVersion value);
public bool Equals(ReleaseVersion obj);
public bool PrecedenceEquals(ReleaseVersion value);
public string ToString(int fieldCount);
public override bool Equals(object obj);
public override int GetHashCode();
public override string ToString();
}
public class ReleaseVersionConverter : JsonConverter<ReleaseVersion> {
public ReleaseVersionConverter();
public override ReleaseVersion ReadJson(JsonReader reader, Type objectType, ReleaseVersion existingValue, bool hasExistingValue, JsonSerializer serializer);
public override void WriteJson(JsonWriter writer, ReleaseVersion value, JsonSerializer serializer);
}
public class RuntimeReleaseComponent : ReleaseComponent {
public string VisualStudioMacVersion { get; }
public string VisualStudioVersion { get; }
}
public class SdkReleaseComponent : ReleaseComponent {
public string CSharpVersion { get; }
public string FSharpVersion { get; }
public ReleaseVersion RuntimeVersion { get; }
public string VisualBasicVersion { get; }
public string VisualStudioMacSupport { get; }
public string VisualStudioMacVersion { get; }
public string VisualStudioSupport { get; }
public string VisualStudioVersion { get; }
}
public class SupportPhaseConverter : StringEnumConverter {
public SupportPhaseConverter();
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer);
}
public class WindowsDesktopReleaseComponent : ReleaseComponent {
}
public enum SupportPhase {
Unknown = 0,
[EnumMember]
EndOfLife = 1,
Maintenance = 2,
[EnumMember]
LongTermSupport = 3,
Preview = 4,
RC = 5,
}
}