Skip to content
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

Merged
merged 10 commits into from Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file removed source/ios/release/AdaptiveCards.framework.zip
Binary file not shown.
Binary file not shown.
146 changes: 146 additions & 0 deletions source/ios/tools/IOSFeed/IOSFeed.cs
@@ -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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= "source/ios/tools/IOSFeed/ConnectString.txt" [](start = 48, length = 47)

This is sensitive info -- is this assumed to be in clear text? What are connection string best practices again? #Closed

Copy link
Member Author

@jwoo-msft jwoo-msft Aug 14, 2019

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)

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
Copy link
Member

@shalinijoshi19 shalinijoshi19 Aug 1, 2019

Choose a reason for hiding this comment

The 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()
Copy link
Member

@shalinijoshi19 shalinijoshi19 Aug 1, 2019

Choose a reason for hiding this comment

The 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");
Copy link
Member

@shalinijoshi19 shalinijoshi19 Aug 14, 2019

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The 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
};

Copy link
Member

@shalinijoshi19 shalinijoshi19 Aug 14, 2019

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sound great; thanks


In reply to: 314079529 [](ancestors = 314079529)

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");
}
Copy link
Member

Choose a reason for hiding this comment

The 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

Copy link
Member Author

@jwoo-msft jwoo-msft Aug 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, that's good suggestion.


In reply to: 314076636 [](ancestors = 314076636)

}

private static void UpdatePodSpec(string uri)
Copy link
Member

@shalinijoshi19 shalinijoshi19 Aug 1, 2019

Choose a reason for hiding this comment

The 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");
Copy link
Member

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks


In reply to: 314077399 [](ancestors = 314077399)

}

// Open the stream and read it back.
using (StreamReader sr = File.OpenText(sourceFile))
{
string s = "";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string [](start = 16, length = 6)

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks


In reply to: 314078732 [](ancestors = 314078732)

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"))
Copy link
Member

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks


In reply to: 314079012 [](ancestors = 314079012)

{
Copy link
Member

@shalinijoshi19 shalinijoshi19 Aug 1, 2019

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The 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);
}
}
}
}
13 changes: 13 additions & 0 deletions source/ios/tools/IOSFeed/IOSFeed.csproj
@@ -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>
25 changes: 25 additions & 0 deletions source/ios/tools/IOSFeed/IOSFeed.sln
@@ -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