New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adds tool for ios feed #3291
adds tool for ios feed #3291
Changes from 5 commits
1060e75
4800dcc
91cd27f
50f3084
2e68dfb
4714f31
520bffb
a7f249b
8df204d
eda8045
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
using System; | ||
using System.IO; | ||
using System.Threading.Tasks; | ||
using Microsoft.Azure.Storage; | ||
using Microsoft.Azure.Storage.Blob; | ||
|
||
namespace AdaptiveCards.Tools.IOSFeed | ||
{ | ||
public static class Constants | ||
{ | ||
public const string ConnectionStringPath = "source/ios/tools/IOSFeed/ConnectString.txt"; | ||
public const string ContainerId = "adaptivecardsiosblobs"; | ||
public const string FrameworkPath = "source/ios/AdaptiveCards/AdaptiveCards"; | ||
public const string FrameworkName = "AdaptiveCards.framework.zip"; | ||
public const string PodspecPath = "./source/ios/tools/"; | ||
public const string TargetPodspecPath = "./source/ios/"; | ||
public const string PodspecName = "AdaptiveCards.podspec"; | ||
} | ||
|
||
/// <summary> | ||
/// IOSFeed picks up ios artifact, sends to the Azure storage | ||
/// and updates podspec for pod publication | ||
/// </summary> | ||
class IOSFeed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: a quick class summary/description would be handy here) ; is there a readme.md or something we can add as part of this btw? Thanks! #Resolved |
||
{ | ||
public static void Main() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: add msft copyright header! #Resolved |
||
{ | ||
// Run the examples asynchronously, wait for the results before proceeding | ||
ProcessAsync().GetAwaiter().GetResult(); | ||
} | ||
|
||
private static async Task ProcessAsync() | ||
{ | ||
string storageConnectionString; | ||
|
||
FileInfo fi = new FileInfo(Constants.ConnectionStringPath); | ||
if (!fi.Exists) | ||
{ | ||
throw new InvalidOperationException("connection string file does not exist"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Helpful to include the path to what wasnt found for debugging? Where does this tool run again? is it manually invoked or part of CI? #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is part of external feed run by AzureDepOp, but it should be invoked by manual process since it's intended for external release. In reply to: 314076034 [](ancestors = 314076034) |
||
} | ||
|
||
using (StreamReader sr = File.OpenText(Constants.ConnectionStringPath)) | ||
{ | ||
storageConnectionString = sr.ReadToEnd(); | ||
} | ||
|
||
// Check whether the connection string can be parsed. | ||
CloudStorageAccount storageAccount; | ||
if (CloudStorageAccount.TryParse(storageConnectionString, out storageAccount)) | ||
{ | ||
// If the connection string is valid, proceed with operations against Blob | ||
// storage here. | ||
// Create the CloudBlobClient that represents the | ||
// Blob storage endpoint for the storage account. | ||
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient(); | ||
|
||
CloudBlobContainer cloudBlobContainer = | ||
cloudBlobClient.GetContainerReference(Constants.ContainerId); | ||
await cloudBlobContainer.CreateIfNotExistsAsync(); | ||
|
||
// Set the permissions so the blobs are public. | ||
BlobContainerPermissions permissions = new BlobContainerPermissions | ||
{ | ||
PublicAccess = BlobContainerPublicAccessType.Blob | ||
}; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if any of the code fails below -- do you want to delete the blob you created so we dont have orphans laying around? Suggest add a try-catch-finally block #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
await cloudBlobContainer.SetPermissionsAsync(permissions); | ||
|
||
var sourceFile = Path.Combine(Constants.FrameworkPath, Constants.FrameworkName); | ||
var blobGuid = Guid.NewGuid().ToString(); | ||
var cloudFileName = blobGuid + Constants.FrameworkName; | ||
|
||
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(cloudFileName); | ||
|
||
await cloudBlockBlob.UploadFromFileAsync(sourceFile); | ||
|
||
BlobContinuationToken blobContinuationToken = null; | ||
do | ||
{ | ||
var results = await cloudBlobContainer.ListBlobsSegmentedAsync(null, blobContinuationToken); | ||
// Get the value of the continuation token returned by the listing call. | ||
blobContinuationToken = results.ContinuationToken; | ||
foreach (IListBlobItem item in results.Results) | ||
{ | ||
var uriString = item.Uri.ToString(); | ||
if (uriString.Contains(blobGuid)) | ||
{ | ||
UpdatePodSpec(uriString); | ||
} | ||
} | ||
} while (blobContinuationToken != null); | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException("invalid connection string"); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIt Nit: maybe have this be part of the if check upfront for readability and move the bigger logic behind it in the else; Also include the connection string that we are complaining about for debugging. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
private static void UpdatePodSpec(string uri) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general let's add in error handling to these helpers as well. #Resolved |
||
{ | ||
var sourceFile = Path.Combine(Constants.PodspecPath, Constants.PodspecName); | ||
var targetFile = Path.Combine(Constants.TargetPodspecPath, Constants.PodspecName); | ||
|
||
FileInfo fi = new FileInfo(sourceFile); | ||
if (!fi.Exists) | ||
{ | ||
throw new FileNotFoundException("missing podspec"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: same aids in debuggability if you include the filename we are complainng about #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
// Open the stream and read it back. | ||
using (StreamReader sr = File.OpenText(sourceFile)) | ||
{ | ||
string s = ""; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
strings in c# are immutable. That means every time you assign a new value to s a new string ojbect will be created under the covers; that will be grossly inefficient. Prefer StringBuilder here. #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
string output = ""; | ||
while ((s = sr.ReadLine()) != null) | ||
{ | ||
if (s.Length != 0) | ||
{ | ||
var splits = s.Split('='); | ||
if (splits.Length > 0) | ||
{ | ||
if (splits[0].Contains("spec.source")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Note that string.Contains() is a case-sensitive comparison by default - do you want it to be case sensitive or just make it case-insensitive (OrdinalIgnoreCase) #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you not able to use the built in System.Uri or System.UriBuilder class? you shouldnt have to parse the tokens manually. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm not parsing the uri here. i'm attaching uri to podspec file. In reply to: 309843959 [](ancestors = 309843959) |
||
s = splits[0] + "= { :http => " + "'" + uri + "' }"; | ||
} | ||
else if (splits[0].Contains("spec.version")) | ||
{ | ||
var adaptiveVersion = Environment.GetEnvironmentVariable("ADCVERSION"); | ||
s = splits[0] + "= '" + adaptiveVersion + "'" ; | ||
} | ||
} | ||
|
||
output += (s + "\n"); | ||
} else | ||
{ | ||
output += "\n"; | ||
} | ||
} | ||
|
||
File.WriteAllText(targetFile, output); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>netcoreapp2.1</TargetFramework> | ||
<RootNamespace>AdaptiveCards.Toos.IOSFeed</RootNamespace> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Azure.Storage.Blob" Version="10.0.3" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
| ||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio 15 | ||
VisualStudioVersion = 15.0.28307.705 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IOSFeed", "IOSFeed.csproj", "{953218C2-8A1A-44E3-96C6-DFF08D977D7C}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{953218C2-8A1A-44E3-96C6-DFF08D977D7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{953218C2-8A1A-44E3-96C6-DFF08D977D7C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{953218C2-8A1A-44E3-96C6-DFF08D977D7C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{953218C2-8A1A-44E3-96C6-DFF08D977D7C}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {060131CB-8F54-484E-BCDE-6AF526BB9762} | ||
EndGlobalSection | ||
EndGlobal |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is sensitive info -- is this assumed to be in clear text? What are connection string best practices again? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AzureDepOp has secure file download task. The task stores a file securely, then it download the file securely. And the location is referenced by a variable specified by the task.
Then I used AzureDepOp's copy task to copy the file to the ConnectionStringPath location.
unless build machine is hacked during the build process, this practice won't pose security risk.
In reply to: 314079827 [](ancestors = 314079827)