Install-free .NET w/ Google Cloud
Gotta love containers
There are the languages where you can start writing code, build|run the code and you’re off to the races in no time, e.g. Golang, Python, JavaScript.
Then there are the languages where you need to spend a bunch of time remembering all the infrastructure that’s needed *before* you can start building|running code, e.g. Java and .NET.
I wanted to help a developer stumped by some C# code using a Google API Client Library. I was reluctant to try to repro the issue because it feels (worse than it is) to get a .NET runtime.
Containers to the rescue:
docker run \
--rm \
--interactive \
--tty \
--volume=$PWD/dotnet:/dotnet \
--volume=$PWD/images:/images \
--volume=$PWD/secret:/secret \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet watch run
This is super useful! I was able to quite quickly reconstruct a .NET project (I think there’s probably an easier way to do this too), plonk in the Program.cs
and get running.
Setup
Let’s be cute and write “Hello Freddie” in F# (.NET==.NET). Create a directory called fsharp
and create Program.fs
in it:
open System[<EntryPoint>]
let main argv =
printfn "Hello Freddie!"
0 // return an integer exit code
In the same directory, create a solution, called fsharp.fsproj
:
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup><ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup></Project>
NB Too clever… For F# you’ll need the
<ItemGroup>
and<Compile Include...>
values. These aren’t necessary for C#.
Watch-Run It
docker run \
--rm --interactive \
--tty \
--volume=$PWD/fsharp:/dotnet \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet watch runwatch : Polling file watcher is enabled
watch : Started
Hello Freddie!
watch : Exited
watch : Waiting for a file to change before restarting dotnet...
The watch
permits you to change Program.cs
, save it and have the project rebuilt and rerun on-the-fly. Nice!
Stepping Back
I subsequently realized that the dotnet
image will also create project structures for us. For our root directory:
docker run \
--rm \
--interactive \
--tty \
--volume=$PWD:/dotnet \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet new console -lang F# -o fsharp
Or:
docker run \
--rm \
--interactive \
--tty \
--volume=$PWD:/dotnet \
--workdir=/dotnet \
microsoft/dotnet:2.2-sdk \
dotnet new console -lang C# -o csharp
Google API Client Library
To write code that leverages Google API Client Libraries (and any other Nuget packages), we’ll need to add the relevant packages to our project. The dotnet
image supports this too:
... dotnet add package Google.Apis.Auth --version 1.38.0
... dotnet add package Google.Apis.Drive.v3 --version 1.38.0.1491
NB For brevity I’ve omitted the
docker run ...
preamble to these commands. You need only ensure that the correct volume mapping is specified; for the project where you wish the libraries added.
Alternatively, you can manually reference the libraries in the project file. For example, to add Google’s Auth API to the F# project:
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup><ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup><ItemGroup>
<PackageReference Include="Google.Apis.Auth" Version="1.38.0" />
</ItemGroup></Project>
If you read my previous post “Getting Started with .NET on GCP”, you’ll recall that we can search the Nuget repo for all of these libraries, including Google.Apis.Auth
:
The image supports restore
, build
, run
and the watch run
command that I’ve been using above.
OAuth
One small, obscurely (!?) documented trick is that the default OAuth flow launches the browser in order to capture the authentication code. When running .NET in a container, this flow may be broken.
An alternative flow is to prompt the user to visit a URL, copy and paste the code back into the code. This will work and I recommend this approach.
The common flow is:
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore("DriveUploader.ListMyLibrary")
);
The flow that prompts the user to visit the URL and copy the code is a very small change:
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore("DriveUploader.ListMyLibrary"),
new PromptCodeReceiver()
);
Conclusion
If you’re lazy like me and groan at the prospect of installing .NET to develop code, fret not! Containers facilitate software installation and Microsoft has done a good job here in providing an image that saves a bunch of work.
Tidy up
When you’re done with the .NET runtime:
docker image rm microsoft/dotnet:2.2-sdk
NB The tag will change with the SDK version.