Skip to content

Commit f84e572

Browse files
authored
Add support for ACR when publishing scripts (PowerShell#1573)
1 parent f1b3cf7 commit f84e572

File tree

6 files changed

+152
-97
lines changed

6 files changed

+152
-97
lines changed

src/code/ACRServerAPICalls.cs

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public override FindResults FindName(string packageName, bool includePrerelease,
133133
_cmdletPassedIn.WriteDebug("In ACRServerAPICalls::FindName()");
134134
string accessToken = string.Empty;
135135
string tenantID = string.Empty;
136+
string packageNameLowercase = packageName.ToLower();
136137

137138
// Need to set up secret management vault before hand
138139
var repositoryCredentialInfo = Repository.CredentialInfo;
@@ -167,7 +168,7 @@ public override FindResults FindName(string packageName, bool includePrerelease,
167168
}
168169

169170
_cmdletPassedIn.WriteVerbose("Getting tags");
170-
var foundTags = FindAcrImageTags(registry, packageName, "*", acrAccessToken, out errRecord);
171+
var foundTags = FindAcrImageTags(registry, packageNameLowercase, "*", acrAccessToken, out errRecord);
171172
if (errRecord != null || foundTags == null)
172173
{
173174
return new FindResults(stringResponse: new string[] { }, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
@@ -193,20 +194,29 @@ public override FindResults FindName(string packageName, bool includePrerelease,
193194
return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
194195
}
195196

196-
if (NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion))
197+
if (!NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion))
197198
{
198-
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");
199-
if (!pkgVersion.IsPrerelease || includePrerelease)
200-
{
201-
// Versions are always in descending order i.e 5.0.0, 3.0.0, 1.0.0 so grabbing the first match suffices
202-
latestVersionResponse.Add(GetACRMetadata(registry, packageName, pkgVersion, acrAccessToken, out errRecord));
203-
if (errRecord != null)
204-
{
205-
return new FindResults(stringResponse: new string[] { }, hashtableResponse: latestVersionResponse.ToArray(), responseType: acrFindResponseType);
206-
}
199+
errRecord = new ErrorRecord(
200+
new ArgumentException($"Version {pkgVersionElement.ToString()} to be parsed from metadata is not a valid NuGet version."),
201+
"FindNameFailure",
202+
ErrorCategory.InvalidArgument,
203+
this);
207204

208-
break;
205+
return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
206+
}
207+
208+
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");
209+
if (!pkgVersion.IsPrerelease || includePrerelease)
210+
{
211+
// TODO: ensure versions are in order, fix bug https://github.com/PowerShell/PSResourceGet/issues/1581
212+
Hashtable metadata = GetACRMetadata(registry, packageNameLowercase, pkgVersion, acrAccessToken, out errRecord);
213+
if (errRecord != null || metadata.Count == 0)
214+
{
215+
return new FindResults(stringResponse: new string[] { }, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
209216
}
217+
218+
latestVersionResponse.Add(metadata);
219+
break;
210220
}
211221
}
212222
}
@@ -285,6 +295,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
285295
_cmdletPassedIn.WriteDebug("In ACRServerAPICalls::FindVersionGlobbing()");
286296
string accessToken = string.Empty;
287297
string tenantID = string.Empty;
298+
string packageNameLowercase = packageName.ToLower();
288299

289300
// Need to set up secret management vault beforehand
290301
var repositoryCredentialInfo = Repository.CredentialInfo;
@@ -318,7 +329,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
318329
}
319330

320331
_cmdletPassedIn.WriteVerbose("Getting tags");
321-
var foundTags = FindAcrImageTags(registry, packageName, "*", acrAccessToken, out errRecord);
332+
var foundTags = FindAcrImageTags(registry, packageNameLowercase, "*", acrAccessToken, out errRecord);
322333
if (errRecord != null || foundTags == null)
323334
{
324335
return new FindResults(stringResponse: new string[] { }, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
@@ -355,7 +366,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
355366
continue;
356367
}
357368

358-
latestVersionResponse.Add(GetACRMetadata(registry, packageName, pkgVersion, acrAccessToken, out errRecord));
369+
latestVersionResponse.Add(GetACRMetadata(registry, packageNameLowercase, pkgVersion, acrAccessToken, out errRecord));
359370
if (errRecord != null)
360371
{
361372
return new FindResults(stringResponse: new string[] { }, hashtableResponse: latestVersionResponse.ToArray(), responseType: acrFindResponseType);
@@ -706,7 +717,7 @@ internal Hashtable GetACRMetadata(string registry, string packageName, NuGetVers
706717
if (exception != null)
707718
{
708719
errRecord = new ErrorRecord(exception, "FindNameFailure", ErrorCategory.InvalidResult, this);
709-
720+
710721
return requiredVersionResponse;
711722
}
712723

@@ -727,14 +738,22 @@ internal Hashtable GetACRMetadata(string registry, string packageName, NuGetVers
727738
return requiredVersionResponse;
728739
}
729740

730-
if (NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion))
741+
if (!NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion))
731742
{
732-
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");
743+
errRecord = new ErrorRecord(
744+
new ArgumentException($"Version {pkgVersionElement.ToString()} to be parsed from metadata is not a valid NuGet version."),
745+
"FindNameFailure",
746+
ErrorCategory.InvalidArgument,
747+
this);
733748

734-
if (pkgVersion == requiredVersion)
735-
{
736-
requiredVersionResponse.Add(metadataPkgName, metadata);
737-
}
749+
return requiredVersionResponse;
750+
}
751+
752+
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");
753+
754+
if (pkgVersion == requiredVersion)
755+
{
756+
requiredVersionResponse.Add(metadataPkgName, metadata);
738757
}
739758
}
740759

@@ -1102,7 +1121,7 @@ private static Collection<KeyValuePair<string, string>> GetDefaultHeaders(string
11021121
};
11031122
}
11041123

1105-
internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string pkgName, NuGetVersion pkgVersion, PSRepositoryInfo repository, Hashtable parsedMetadataHash, out ErrorRecord errRecord)
1124+
internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string pkgName, NuGetVersion pkgVersion, PSRepositoryInfo repository, ResourceType resourceType, Hashtable parsedMetadataHash, out ErrorRecord errRecord)
11061125
{
11071126
errRecord = null;
11081127
// Push the nupkg to the appropriate repository
@@ -1188,7 +1207,7 @@ internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string p
11881207

11891208
/* Create manifest layer */
11901209
_cmdletPassedIn.WriteVerbose("Create package version metadata as JSON string");
1191-
string jsonString = CreateMetadataContent(psd1OrPs1File, parsedMetadataHash, out ErrorRecord metadataCreationError);
1210+
string jsonString = CreateMetadataContent(psd1OrPs1File, resourceType, parsedMetadataHash, out ErrorRecord metadataCreationError);
11921211
if (metadataCreationError != null)
11931212
{
11941213
_cmdletPassedIn.ThrowTerminatingError(metadataCreationError);
@@ -1197,7 +1216,7 @@ internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string p
11971216
FileInfo nupkgFile = new FileInfo(fullNupkgFile);
11981217
var fileSize = nupkgFile.Length;
11991218
var fileName = System.IO.Path.GetFileName(fullNupkgFile);
1200-
string fileContent = CreateJsonContent(nupkgDigest, configDigest, fileSize, fileName, pkgName, jsonString);
1219+
string fileContent = CreateJsonContent(nupkgDigest, configDigest, fileSize, fileName, pkgName, resourceType, jsonString);
12011220
File.WriteAllText(configFilePath, fileContent);
12021221

12031222
_cmdletPassedIn.WriteVerbose("Create the manifest layer");
@@ -1207,6 +1226,7 @@ internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string p
12071226
{
12081227
return true;
12091228
}
1229+
12101230
return false;
12111231
}
12121232

@@ -1216,6 +1236,7 @@ private string CreateJsonContent(
12161236
long nupkgFileSize,
12171237
string fileName,
12181238
string packageName,
1239+
ResourceType resourceType,
12191240
string jsonString)
12201241
{
12211242
StringBuilder stringBuilder = new StringBuilder();
@@ -1224,6 +1245,7 @@ private string CreateJsonContent(
12241245

12251246
jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
12261247

1248+
// start of manifest JSON object
12271249
jsonWriter.WriteStartObject();
12281250

12291251
jsonWriter.WritePropertyName("schemaVersion");
@@ -1257,17 +1279,18 @@ private string CreateJsonContent(
12571279
jsonWriter.WriteValue(fileName);
12581280
jsonWriter.WritePropertyName("metadata");
12591281
jsonWriter.WriteValue(jsonString);
1260-
jsonWriter.WriteEndObject();
1261-
jsonWriter.WriteEndObject();
1282+
jsonWriter.WritePropertyName("artifactType");
1283+
jsonWriter.WriteValue(resourceType.ToString());
1284+
jsonWriter.WriteEndObject(); // end of annotations object
12621285

1263-
jsonWriter.WriteEndArray();
1264-
jsonWriter.WriteEndObject();
1286+
jsonWriter.WriteEndObject(); // end of 'layers' entry object
1287+
1288+
jsonWriter.WriteEndArray(); // end of 'layers' array
1289+
jsonWriter.WriteEndObject(); // end of manifest JSON object
12651290

12661291
return stringWriter.ToString();
12671292
}
12681293

1269-
1270-
12711294
// ACR method
12721295
private bool CreateDigest(string fileName, out string digest, out ErrorRecord error)
12731296
{
@@ -1310,45 +1333,40 @@ private bool CreateDigest(string fileName, out string digest, out ErrorRecord er
13101333
return true;
13111334
}
13121335

1313-
private string CreateMetadataContent(string manifestFilePath, Hashtable parsedMetadata, out ErrorRecord metadataCreationError)
1336+
private string CreateMetadataContent(string manifestFilePath, ResourceType resourceType, Hashtable parsedMetadata, out ErrorRecord metadataCreationError)
13141337
{
13151338
metadataCreationError = null;
1316-
Hashtable parsedMetadataHash = null;
13171339
string jsonString = string.Empty;
13181340

1319-
// A script will already have the metadata parsed into the parsedMetadatahash,
1320-
// a module will still need the module manifest to be parsed.
13211341
if (parsedMetadata == null || parsedMetadata.Count == 0)
1322-
{
1323-
// Use the parsed module manifest data as 'parsedMetadataHash' instead of the passed-in data.
1324-
if (!Utils.TryReadManifestFile(
1325-
manifestFilePath: manifestFilePath,
1326-
manifestInfo: out parsedMetadataHash,
1327-
error: out Exception manifestReadError))
1328-
{
1329-
metadataCreationError = new ErrorRecord(
1330-
manifestReadError,
1331-
"ManifestFileReadParseForACRPublishError",
1332-
ErrorCategory.ReadError,
1333-
_cmdletPassedIn);
1334-
1335-
return jsonString;
1336-
}
1337-
}
1338-
1339-
if (parsedMetadataHash == null)
13401342
{
13411343
metadataCreationError = new ErrorRecord(
1342-
new InvalidOperationException("Error parsing package metadata into hashtable."),
1343-
"PackageMetadataHashEmptyError",
1344-
ErrorCategory.InvalidData,
1344+
new ArgumentException("Hashtable created from .ps1 or .psd1 containing package metadata was null or empty"),
1345+
"MetadataHashtableEmptyError",
1346+
ErrorCategory.InvalidArgument,
13451347
_cmdletPassedIn);
13461348

13471349
return jsonString;
13481350
}
13491351

13501352
_cmdletPassedIn.WriteVerbose("Serialize JSON into string.");
1351-
jsonString = System.Text.Json.JsonSerializer.Serialize(parsedMetadataHash);
1353+
1354+
if (parsedMetadata.ContainsKey("Version") && parsedMetadata["Version"] is NuGetVersion pkgNuGetVersion)
1355+
{
1356+
// do not serialize NuGetVersion, this will populate more metadata than is needed and makes it harder to deserialize later
1357+
parsedMetadata.Remove("Version");
1358+
parsedMetadata["Version"] = pkgNuGetVersion.ToString();
1359+
}
1360+
1361+
try
1362+
{
1363+
jsonString = System.Text.Json.JsonSerializer.Serialize(parsedMetadata);
1364+
}
1365+
catch (Exception ex)
1366+
{
1367+
metadataCreationError = new ErrorRecord(ex, "JsonSerializationError", ErrorCategory.InvalidResult, _cmdletPassedIn);
1368+
return jsonString;
1369+
}
13521370

13531371
return jsonString;
13541372
}

src/code/PSResourceInfo.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ public static bool TryConvertFromACRJson(
819819

820820
if (packageMetadata == null)
821821
{
822-
errorMsg = "TryConvertJsonToPSResourceInfo: Invalid json object. Object cannot be null.";
822+
errorMsg = "TryConvertFromACRJson: Invalid json object. Object cannot be null.";
823823
return false;
824824
}
825825

@@ -829,24 +829,34 @@ public static bool TryConvertFromACRJson(
829829
JsonElement rootDom = packageMetadata.RootElement;
830830

831831
// Version
832-
if (rootDom.TryGetProperty("ModuleVersion", out JsonElement versionElement))
832+
if (rootDom.TryGetProperty("ModuleVersion", out JsonElement versionElement) || rootDom.TryGetProperty("Version", out versionElement))
833833
{
834834
string versionValue = versionElement.ToString();
835-
metadata["Version"] = ParseHttpVersion(versionValue, out string prereleaseLabel);
835+
836+
Version pkgVersion = ParseHttpVersion(versionValue, out string prereleaseLabel);
837+
metadata["Version"] = pkgVersion;
836838
metadata["Prerelease"] = prereleaseLabel;
837839
metadata["IsPrerelease"] = !String.IsNullOrEmpty(prereleaseLabel);
838840

839-
if (!NuGetVersion.TryParse(versionValue, out NuGetVersion parsedNormalizedVersion))
841+
if (!NuGetVersion.TryParse(versionValue, out NuGetVersion parsedNormalizedVersion) && pkgVersion == null)
840842
{
841843
errorMsg = string.Format(
842844
CultureInfo.InvariantCulture,
843-
@"TryReadPSGetInfo: Cannot parse NormalizedVersion");
845+
@"TryConvertFromACRJson: Cannot parse NormalizedVersion or System.Version from version in metadata.");
844846

845-
parsedNormalizedVersion = new NuGetVersion("1.0.0.0");
847+
return false;
846848
}
847849

848850
metadata["NormalizedVersion"] = parsedNormalizedVersion;
849851
}
852+
else
853+
{
854+
errorMsg = string.Format(
855+
CultureInfo.InvariantCulture,
856+
@"TryConvertFromACRJson: Neither 'ModuleVersion' nor 'Version' could be found in package metadata");
857+
858+
return false;
859+
}
850860

851861
// License Url
852862
if (rootDom.TryGetProperty("LicenseUrl", out JsonElement licenseUrlElement))
@@ -972,7 +982,7 @@ public static bool TryConvertFromACRJson(
972982
{
973983
errorMsg = string.Format(
974984
CultureInfo.InvariantCulture,
975-
@"TryConvertFromJson: Cannot parse PSResourceInfo from json object with error: {0}",
985+
@"TryConvertFromACRJson: Cannot parse PSResourceInfo from json object with error: {0}",
976986
ex.Message);
977987

978988
return false;

0 commit comments

Comments
 (0)