Visual Studio Tools for Azure Functions の修正版

Azure Functions 向けの拡張機能を入れたところで、.NET Coreのプロジェクトがだめになるっていうものに対する修正版が出てたようです。

https://blogs.msdn.microsoft.com/webdev/2016/12/01/visual-studio-tools-for-azure-functions/

Update 12-6-16 @5:00 PM: Updated version of the tools are available that fix the ability to open .NET Core projects with Azure Functions tools installed. Install the updated version over your old version to fix the issue, there is no need to uninstall the previous copy.

というわけで、入れてみたところ・・・

2016-12-10

インストール完了。
プロジェクトの新規作成で、ASP.NET Coreのプロジェクトテンプレートから作成したみところ、無事にできました。

2016-12-10-2

良かった良かったー

風邪っぽい・・・?VS2015の拡張機能

Microsoft Azure2 Advent Calendar 2016 の6日目の記事です。
.NET Core Advent Calendarの6日目の記事です。


2016-12-10追記
Visual Studio Tools for Azure Functions の修正版が出たようで、解消しました。

両方空いていたのと、両方を好きな人はこれから私と悲しい気持ちを味わう前に楽しくなってもらえればという願いを込めて書きました。
相変わらず、しょうもないネタなのですが・・・お時間あればお付き合いください。
Visual Studio 2015の話になります。

季節の変わり目で、咳が止まらず目が覚めて何気なく.NET Coreプロジェクトを作ろうとしたら・・・

2016-12-05-1

2016-12-05-2

あ、、、あれれ?コンソールだめなら、Webは・・・?

2016-12-05-3

2016-12-05-4

 

うーむ、VS2017 RCと共存してるからかなぁ・・・とか思いつつ、既存の.NET Coreアプリを開いてみたところ・・・

=====================
2016/12/04 5:47:00
Recoverable
System.Exception: ディスク上のプロジェクトファイルの検出中に次のエラーが発生しました。Could not load file or assembly 'Microsoft.VisualStudio.ProjectSystem.DotNet, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. 指定されたファイルが見つかりません。。
   at Microsoft.VisualStudio.ProjectSystem.DotNet.Common.FileMirroring.SourceItemsInMemoryProject.<InitializeFromDiskAsync>d__54.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.ProjectSystem.DotNet.Common.FileMirroring.SourceItemsInMemoryProject.<Creator>d__52.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.VisualStudio.ProjectSystem.UnconfiguredProjectImpl.AutoLoadMethodStateMachine.<<StartExecution>b__6_0>d.MoveNext()
===================

どうやら風邪がうつったようです

いやいやそんなわけはなく・・・いろいろ調べてみたところ・・・
どうやら、Visual Studio Tools for Azure Funtionsの問題として認識されてるよというところまでは見つけました。

https://blogs.msdn.microsoft.com/webdev/2016/12/01/visual-studio-tools-for-azure-functions/

Warning: We have discovered that installing this Preview breaks the ability to create or open .NET Core projects in Visual Studio 2015.  We are currently investigating, and will make a fix available as soon as it is ready.  In the meantime, do not install this on any machines where you plan to work with .NET Core projects.

.NET Coreプロジェクトを扱うことができなくなってしまうということで、対策をがんばってるそうです。

そう・・・思えばFunctionsへの浮気心が、.NET Coreとの距離を近づけたのかもしれない・・・

そうか、浮気といえば・・・電話番号は二つ・・・つまり、マシンをもう一つAzureで作ればいいじゃない!
この発想には賛否両論あると思いますが…

というわけで、デプロイ!!
で、さりげなく機能紹介。

2016-12-05-5

というわけで、デプロイできた仮想マシンのブレードを開くと、
Automation スクリプトというのがあるのでそこを選択すると出てくるものです。
これは、ご存知の方多いとは思いますが、ARMというテンプレートで、この仮想マシンと同じものを再現するために使える逆起しの設計書みたいな状態です。
ただ、エラーが出てる通り、この仮想マシンに設定した自動シャットダウンと、仮想マシン拡張の診断機能は含まれていません。なので100%ではないですが、このJSONを1から作るよりも、一度作ってみたものをもとにJSON取得して、修正。
ARMとしてテンプレートデプロイをするというシナリオが近道のような気がします。

 

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "virtualMachines_vm20161206_adminPassword": {
            "defaultValue": null,
            "type": "SecureString"
        },
        "virtualMachines_vm20161206_name": {
            "defaultValue": "vm20161206",
            "type": "String"
        },
        "networkInterfaces_vm20161206128_name": {
            "defaultValue": "vm20161206128",
            "type": "String"
        },
        "networkSecurityGroups_vm20161206_nsg_name": {
            "defaultValue": "vm20161206-nsg",
            "type": "String"
        },
        "publicIPAddresses_vm20161206_ip_name": {
            "defaultValue": "vm20161206-ip",
            "type": "String"
        },
        "virtualNetworks_vm20161206_vnet_name": {
            "defaultValue": "vm20161206-vnet",
            "type": "String"
        },
        "storageAccounts_vm20161206diag122_name": {
            "defaultValue": "vm20161206diag122",
            "type": "String"
        },
        "storageAccounts_vm20161206disks246_name": {
            "defaultValue": "vm20161206disks246",
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Compute/virtualMachines/vm20161206' から一般化されました。",
            "type": "Microsoft.Compute/virtualMachines",
            "name": "[parameters('virtualMachines_vm20161206_name')]",
            "apiVersion": "2015-06-15",
            "location": "japaneast",
            "properties": {
                "hardwareProfile": {
                    "vmSize": "Standard_F8s"
                },
                "storageProfile": {
                    "imageReference": {
                        "publisher": "MicrosoftVisualStudio",
                        "offer": "VisualStudio",
                        "sku": "VS-2015-Ent-VSU3-AzureSDK-29-Win10-N",
                        "version": "latest"
                    },
                    "osDisk": {
                        "name": "[parameters('virtualMachines_vm20161206_name')]",
                        "createOption": "FromImage",
                        "vhd": {
                            "uri": "[concat('https', '://', parameters('storageAccounts_vm20161206disks246_name'), '.blob.core.windows.net', concat('/vhds/', parameters('virtualMachines_vm20161206_name'),'20161204060340.vhd'))]"
                        },
                        "caching": "ReadWrite"
                    },
                    "dataDisks": []
                },
                "osProfile": {
                    "computerName": "[parameters('virtualMachines_vm20161206_name')]",
                    "adminUsername": "watashi.kanrisha",
                    "windowsConfiguration": {
                        "provisionVMAgent": true,
                        "enableAutomaticUpdates": true
                    },
                    "secrets": [],
                    "adminPassword": "[parameters('virtualMachines_vm20161206_adminPassword')]"
                },
                "networkProfile": {
                    "networkInterfaces": [
                        {
                            "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaces_vm20161206128_name'))]"
                        }
                    ]
                }
            },
            "resources": [],
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccounts_vm20161206disks246_name'))]",
                "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaces_vm20161206128_name'))]"
            ]
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/networkInterfaces/vm20161206128' から一般化されました。",
            "type": "Microsoft.Network/networkInterfaces",
            "name": "[parameters('networkInterfaces_vm20161206128_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "ipConfigurations": [
                    {
                        "name": "ipconfig1",
                        "properties": {
                            "privateIPAddress": "10.0.0.4",
                            "privateIPAllocationMethod": "Dynamic",
                            "publicIPAddress": {
                                "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_vm20161206_ip_name'))]"
                            },
                            "subnet": {
                                "id": "[concat(resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworks_vm20161206_vnet_name')), '/subnets/default')]"
                            }
                        }
                    }
                ],
                "dnsSettings": {
                    "dnsServers": []
                },
                "enableIPForwarding": false,
                "networkSecurityGroup": {
                    "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroups_vm20161206_nsg_name'))]"
                }
            },
            "resources": [],
            "dependsOn": [
                "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddresses_vm20161206_ip_name'))]",
                "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworks_vm20161206_vnet_name'))]",
                "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroups_vm20161206_nsg_name'))]"
            ]
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/networkSecurityGroups/vm20161206-nsg' から一般化されました。",
            "type": "Microsoft.Network/networkSecurityGroups",
            "name": "[parameters('networkSecurityGroups_vm20161206_nsg_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "securityRules": [
                    {
                        "name": "default-allow-rdp",
                        "properties": {
                            "protocol": "TCP",
                            "sourcePortRange": "*",
                            "destinationPortRange": "3389",
                            "sourceAddressPrefix": "*",
                            "destinationAddressPrefix": "*",
                            "access": "Allow",
                            "priority": 1000,
                            "direction": "Inbound"
                        }
                    }
                ]
            },
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/publicIPAddresses/vm20161206-ip' から一般化されました。",
            "type": "Microsoft.Network/publicIPAddresses",
            "name": "[parameters('publicIPAddresses_vm20161206_ip_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "publicIPAllocationMethod": "Dynamic",
                "idleTimeoutInMinutes": 4
            },
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Network/virtualNetworks/vm20161206-vnet' から一般化されました。",
            "type": "Microsoft.Network/virtualNetworks",
            "name": "[parameters('virtualNetworks_vm20161206_vnet_name')]",
            "apiVersion": "2016-03-30",
            "location": "japaneast",
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "10.0.0.0/24"
                    ]
                },
                "subnets": [
                    {
                        "name": "default",
                        "properties": {
                            "addressPrefix": "10.0.0.0/24"
                        }
                    }
                ]
            },
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Storage/storageAccounts/vm20161206diag122' から一般化されました。",
            "type": "Microsoft.Storage/storageAccounts",
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "kind": "Storage",
            "name": "[parameters('storageAccounts_vm20161206diag122_name')]",
            "apiVersion": "2016-01-01",
            "location": "japaneast",
            "tags": {},
            "properties": {},
            "resources": [],
            "dependsOn": []
        },
        {
            "comments": "リソース '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/vm20161206/providers/Microsoft.Storage/storageAccounts/vm20161206disks246' から一般化されました。",
            "type": "Microsoft.Storage/storageAccounts",
            "sku": {
                "name": "Premium_LRS",
                "tier": "Premium"
            },
            "kind": "Storage",
            "name": "[parameters('storageAccounts_vm20161206disks246_name')]",
            "apiVersion": "2016-01-01",
            "location": "japaneast",
            "tags": {},
            "properties": {},
            "resources": [],
            "dependsOn": []
        }
    ]
}

このテンプレートを見て、何をデプロイしたか分かった人はすごい!

で、話は戻りまして・・・こうしてどちらかの環境用のマシンを分けておくと、何かと便利なので、あまり使ってないサブスクリプションを持ってる人とかはどうでしょうか?

まとめ

こんな状態でもツールをリリースして、たたかれる覚悟でも一歩でも進もうとしていることに私はすごいことなんじゃないかと思っています。

enjoy azure !
enjoy .NET Core !

AppVeyorの生成物を定義するメモ

定義するってなんぞや?というところですが、
基本的にビルドが終了するとすべてのものは消えてしまいます。
ビルドで、dllができたりnupkgができたりとするわけですが
これを定義して、リソースとして扱えるようにするもののようです。

公式のPackaging artifacts

で、yaml形式でもかけるようですが、WebUIを使っていきます。

Settings>Artifacts

ビルドコマンドで生成されたパスと、その名前を定義しておきます。
そして、ビルド実行!
結果にArtifactsが出てくれば成功

以下、BlackJumboDogのDockerImageでも利用している生成物と同じようにpublishで生成したリソースが出てきます。
https://ci.appveyor.com/project/darkcrash/bjd5-p6o91/build/artifacts

さぁ・・・これをどうしてくれようか

そしてDeploymentの道に続く・・・

.NET Core xUnit 出力のメモ

今日はちょっとしたメモ

.NET CoreでVisualStudio上のテストエクスプローラーから実行した場合、それまでのテストツールであれば、出力で、Traceなどの出力を確認することができました。
しかし、新しいxUnit v2.xでは標準でサポートされなくなりましたと。

https://xunit.github.io/docs/capturing-output.html
If you used xUnit.net 1.x, you may have previously been writing output to Console, Debug, or Trace. When xUnit.net v2 shipped with parallelization turned on by default, this output capture mechanism was no longer appropriate; it is impossible to know which of the many tests that could be running in parallel were responsible for writing to those shared resources. Users who are porting code from v1.x to v2.x should use one of the two new methods instead.

要するに、並列実行されるようになって、どこのテストの結果から出たTraceかわからなくなったと。これに責任持てるような手段がないとのこと。

代替として・・・

As you can see in the example above, the WriteLine function on ITestOutputHelper supports formatting arguments, just as you were used to with Console.

テストコードのあるクラスのコンストラクタに、ITestOutputHelperを引数として渡す。それにWriteLineメソッドを呼び出せば標準出力のように動いてくれると。といっても、Traceしたいので、なんとなく作りました。

https://github.com/darkcrash/bjd5/blob/CoreCLR/Bjd.Common.CoreCLR.Test/Logs/TestOutput.cs

使い方は、コンストラクタで、ITestOutputHelper を引数としてインスタンス化。DisposeでDisposeです。とはいえ、これはこれで、出力がただ出てくれるというだけで、根本的な解決ではないと認識しています。

これを作った経緯として、個別のテストでエラー箇所を特定するために出したかったというものです。なので、一括に動作してきれいな形で出てくれるが目的ではないのです。

こんな感じ

appveyorで、.NET Core を試してみた

ご無沙汰しております。
Blog存在半分忘れかけておりました。
書きたくないわけでもなく、書きたい気持ちはありつつもなかなかタイミングがなかったというところで、実は、取り組んでいるものに節目がうまく出ていないのも一つかなというところです。
その場その場のことを書いていけるようにしていけたらいいのかなとか考えているところです。

というところで、本題です。

経緯

実は、.NET Coreでの開発をしていて、ある程度の規模になってきたときテストコードを自分のところで実行したり、dotnet packでnupkgを作るということをやりたいなと。ただ、継続的インテグレーションを構成したいと考えてましたところ、6月よりちょっと前だと、対応したと目立った情報もなく結構厳しいのかなと思ってました。毎回、手動で実行していければいいのですが、忘れたりまた、テストの結果が見えるようにできていないというのも、何か不透明な感じで面白くないなーと。
最近、@jsakamotoさんより、「appveyor」は「dotnetコマンドも使えるかも!?」と教えていただきまして、試してみました。

結論

使えたー!

設定したこと

既定ではslnファイルを読み込んでうまいことやろうとしてくれますが、
これだとテストも自動的に含まれてしまったり、うまく動作しなかったりとしたために、dotnetコマンドに置き換えました。

最初に、github上からAppVeyorのプロジェクトを作成しました。

  1. 左側リストのGeneral.プロジェクト名の設定 – ProjectName
    Bjd.xxxx
  2. 左側リストのGeneral.ビルドバージョンの設定 – Build version format
    1.0.{build} → 1.0.0-{build}
    ※気持ちの問題
  3. 左側リストのEnviromentは特になし
  4. 左側リストのBuildで、MSBUILDとなっているものをCMDに変更
    dotnet restore
    
    dotnet build Bjd.xxxx -c Release

    としました。

  5. 左側リストのTestで、「Script」「CMD」を選び、Buildと同じようにdotnetコマンドをdotnet test Bjd.xxxx -parallel all -maxthreads 3としました。オプション「-parallel all -maxthreads 3」はXunitに対するオプションです。並列化を有効にして最大スレッド数を設定してますが、設定しなくても動きます。BlackJumboDogでは、テスト実行にもSocket操作・サーバー動作を伴うとか、テスト実行時間が長くなりすぎないようにと考えたすえ、最大数を調整しました。
  6. Badgeの設定
    といっても、リンクをどこかに張り付けるだけです。私はGitHubのreadme.mdにはっつけてみました。
 
 なんか、それっぽい!という気持ちと、いまだにBjd.WebServer.CoreCLR.Testがpassingになったことはない!という現状を報告しておきます。

そのほか

これは、おどろいたことです。わかる人にはわかるとは思いますが、実はpathにperl.exeがいるらしく、cgiのテストが動作しました。
テストのタイムアウトは60分。普通のテストではそうそう、問題にならないような気がしますが、開発機のスペックと同等だと思わないほうがよいかと思います。テストを並列実行してる場合、コア数の違いで、実行時間がそのまま倍になったりしました。体感的に手持ちのSurfacePro4と同じくらいでした。
Slackへの通知もできます。ビルドの結果くらい通知しておくと、Commitの重みを感じつつ励みになります。(passingなら)
また、余談ですが、サーバーのスレッドが停止しないためにテストが終わらずに60分のタイムアウトを経験できたことは伏せておきます。
開発する環境以外での動作は思ったより大変な部分もありましたが、GitHubにプッシュするだけで、自動的にビルド、テストと動く環境があることで、リポジトリの状態を見えるようにできたことは、ぱっと見るだけで、状況を把握できて面白いかなーと思った次第です。

感謝

@jsakamotoさん、情報ありがとうございます!