├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── build.cake ├── build.cmd ├── build.ps1 ├── build.sh ├── gitversion.yml └── source ├── Console ├── Console.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs ├── Database ├── AlwaysRun │ └── RemoveKnownReplacementsFromStatistics.cs ├── Database.csproj ├── PostDeploy.ps1 ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── 001-PackageStatistics.sql │ ├── 002-RemoveKnownReplacementPackages.sql │ └── 003-AddNugetResultCache.sql └── appsettings.json ├── ICanHasDotnetCore.sln ├── Magic ├── Investigator │ ├── InvestigationResult.cs │ ├── PackageCompatabilityInvestigator.cs │ └── PackageResult.cs ├── Magic.csproj ├── NugetPackages │ ├── Data │ │ ├── KnownReplacements.json │ │ ├── MoreInformation.json │ │ └── README.md │ ├── INugetResultCache.cs │ ├── KnownReplacementsRepository.cs │ ├── MoreInformation.cs │ ├── MoreInformationRepository.cs │ ├── NugetPackage.cs │ ├── NugetPackageInfoRetriever.cs │ ├── PackageRepositoryWrapper.cs │ ├── PclProfileCompatabilityChecker.cs │ └── SupportType.cs ├── Output │ ├── CypherOutputFormatter.cs │ ├── FlatListingOutputFormatter.cs │ ├── GraphVizOutputFormatter.cs │ └── TreeOutputFormatter.cs ├── Plumbing │ ├── Extensions │ │ └── StringExtensions.cs │ ├── Option.cs │ └── Result.cs ├── Properties │ └── AssemblyInfo.cs └── SourcePackageFileReaders │ ├── CsProj │ └── Project.cs │ ├── CsProjReader.cs │ ├── ISourcePackagesFileReader.cs │ ├── PackagesConfig │ └── DependencyEntry.cs │ ├── PackagesConfigReader.cs │ ├── PaketDependenciesReader.cs │ ├── ProjectJsonReader.cs │ ├── SourcePackageFile.cs │ └── SourcePackageFileReader.cs ├── Tests ├── Magic │ ├── EndToEndTest.cs │ ├── EndToEndTests.EndToEndTest.approved.txt │ ├── NugetPackages │ │ ├── KnownReplacementsRepositoryTests.cs │ │ ├── MoreInformationRepositoryTests.cs │ │ ├── NugetPackageRetrieverTests.CheckTheSupportTypeOfAWholeStackOfPackages.approved.txt │ │ ├── NugetPackageRetrieverTests.cs │ │ └── PclProfileCompatabilityCheckerTests.cs │ └── SourcePackageFileReaders │ │ ├── CsProjReaderTests.cs │ │ ├── PackagesConfigReaderTests.cs │ │ ├── PaketDependenciesReaderTests.cs │ │ ├── ProjectJsonReaderTest.cs │ │ └── ReaderTestsBase.cs ├── Properties │ └── AssemblyInfo.cs ├── Setup.cs ├── Tests.csproj └── Web │ ├── Features │ ├── Result │ │ └── GitHub │ │ │ ├── GitHubScannerTests.cs │ │ │ └── RepositoryIdTests.cs │ └── Statistics │ │ └── RequerySupportTypeForStatisticsPackagesTaskTests.cs │ └── Helpers │ ├── DataUriConverterTest.CanConvert.approved.txt │ ├── DataUriConverterTest.cs │ └── FakeConfigurationRoot.cs └── Web ├── .gitignore ├── Content └── images │ ├── favicon.png │ ├── github-circle.png │ ├── graph.PNG │ ├── octopus.png │ └── twitter.png ├── Features ├── Analytics │ └── AnalyticsController.cs ├── Console │ ├── Console.html │ ├── Console.less │ └── Console.ts ├── FeaturesAutofacModule.cs ├── Knowledge │ ├── Knowledge.html │ ├── Knowledge.less │ ├── Knowledge.ts │ └── KnowledgeController.cs ├── Meta │ └── MetaController.cs ├── Statistics │ ├── PackageStatistic.cs │ ├── PackageStatisticResponse.cs │ ├── RequerySupportTypeForStatisticsPackagesTask.cs │ ├── Statistics.html │ ├── Statistics.less │ ├── Statistics.ts │ ├── StatisticsController.cs │ └── StatisticsRepository.cs ├── Version.ts ├── app.ts ├── faq │ ├── faq.html │ ├── faq.less │ └── faq.ts ├── home │ ├── IndexController.cs │ ├── home.html │ ├── home.less │ └── home.ts ├── layout │ ├── ExternalLink.ts │ ├── MenuHighlight.ts │ ├── NavMenu.html │ ├── NavMenu.ts │ ├── layout.html │ ├── layout.less │ └── layout.ts ├── result │ ├── Cache │ │ └── DbNugetResultCache.cs │ ├── DependencyGraph.ts │ ├── GetGitHubRequest.cs │ ├── GetResultController.cs │ ├── GetResultRequest.cs │ ├── GetResultResponse.cs │ ├── GitHub │ │ └── GitHubScanner.cs │ ├── PackageResultBox.less │ ├── PackageResultBox.ts │ ├── ResultGroup.html │ ├── ResultGroup.less │ ├── ResultGroup.ts │ ├── SupportTypeService.ts │ ├── result.html │ ├── result.less │ └── result.ts └── site.less ├── Helpers └── DataUriConverter.cs ├── Plumbing ├── RedirectHttpMiddleware.cs └── RedirectWwwMiddleware.cs ├── Program.cs ├── Properties ├── .gitignore ├── AssemblyInfo.cs └── launchSettings.json ├── Startup.cs ├── Web.csproj ├── appsettings.json ├── gulpfile.js ├── index.html ├── package.json ├── typings.json └── web.config /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.*scc 2 | *.FileListAbsolute.txt 3 | *.aps 4 | *.bak 5 | *.[Cc]ache 6 | *.clw 7 | *.eto 8 | *.fb6lck 9 | *.fbl6 10 | *.fbpInf 11 | *.ilk 12 | *.lib 13 | *.log 14 | *.ncb 15 | *.nlb 16 | *.obj 17 | *.patch 18 | *.pch 19 | *.pdb 20 | *.plg 21 | *.[Pp]ublish.xml 22 | *.rdl.data 23 | *.sbr 24 | *.scc 25 | *.sig 26 | *.sqlsuo 27 | *.suo 28 | *.svclog 29 | *.tlb 30 | *.tlh 31 | *.tli 32 | *.tmp 33 | *.user 34 | *.vshost.* 35 | *DXCore.Solution 36 | *_i.c 37 | *_p.c 38 | Ankh.Load 39 | Backup*/ 40 | CVS/ 41 | PrecompiledWeb/ 42 | UpgradeLog*.* 43 | [Bb]in/ 44 | [Dd]ebug/ 45 | [Oo]bj/ 46 | [Rr]elease/ 47 | [Tt]humbs.db 48 | _UpgradeReport_Files 49 | _[Rr]e[Ss]harper.*/ 50 | hgignore[.-]* 51 | ignore[.-]* 52 | svnignore[.-]* 53 | lint.db 54 | build 55 | tools/TestResult.xml 56 | *.ReSharper 57 | source/packages 58 | source/OctopusTools.v2.ncrunchsolution 59 | *.orig 60 | *.userprefs 61 | *.lock.json 62 | .vs 63 | .vscode 64 | /tools/ 65 | /artifacts/ 66 | /publish/ 67 | TestResult.xml 68 | *.received.* 69 | TestResults/ -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Octopus Deploy and contributors. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## End of Life 2 | We have decided to close down https://icanhasdot.net/ in May 2020. The amount of traffic dropped significantly with the release of .NET Core 2.0 and to a trickle with 3.1. 3 | 4 | This project and site have served it's purpose, and the ecosystem is has matured beyond the need for this tool. 5 | 6 | Thank you to all those who contributed. 7 | 8 | Feel free to clone, copy or borrow this code or concept and use it how you see fit (within the licence terms). 9 | 10 | ## Compiling 11 | You will need Visual Studio 2017 and dotnet-1.1.1-sdk ([1.1.1 with SDK 1.0.1](https://github.com/dotnet/core/blob/master/release-notes/download-archives/1.1.1-download.md)). 12 | Note that you can install multiple SDKs side by side, it's all controlled through the global.json. 13 | 14 | To build from the command line you will need NodeJS (Tested with 4.1) and Gulp installed. 15 | 16 | To get the web UI to build, in Visual Studio, open the `Task Runner Explorer` and run the watch task. You can alternatively do this from the command line: 17 | ``` 18 | cd source\Web 19 | gulp watch 20 | ``` 21 | 22 | The TypeScript `ng` errors can be ignored 23 | 24 | Run `build.cmd` to do a full rebuild, including tests, before submitting a code-change PR. 25 | 26 | ## Deployment 27 | 28 | Built on TeamCity https://build.octopushq.com/viewType.html?buildTypeId=Community_ICanHasDotnetCore_BuildICanHasDotnetCore 29 | 30 | Deployed by https://deploy.octopushq.com/app#/projects/i-can-has-dotnet-core 31 | 32 | Test website is https://icanhasdotnetcore-test.azurewebsites.net/ 33 | -------------------------------------------------------------------------------- /build.cake: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // TOOLS 3 | ////////////////////////////////////////////////////////////////////// 4 | #tool "nuget:?package=GitVersion.CommandLine&version=4.0.0" 5 | #addin "nuget:?package=SharpCompress&version=0.12.4" 6 | #addin nuget:?package=Cake.Npm&version=0.17.0 7 | #addin "Cake.Gulp" 8 | #addin "Cake.FileHelpers" 9 | 10 | using Path = System.IO.Path; 11 | using IO = System.IO; 12 | using SharpCompress; 13 | using SharpCompress.Common; 14 | using SharpCompress.Writer; 15 | 16 | ////////////////////////////////////////////////////////////////////// 17 | // ARGUMENTS 18 | ////////////////////////////////////////////////////////////////////// 19 | var target = Argument("target", "Default"); 20 | var configuration = Argument("configuration", "Release"); 21 | 22 | /////////////////////////////////////////////////////////////////////////////// 23 | // GLOBAL VARIABLES 24 | /////////////////////////////////////////////////////////////////////////////// 25 | var publishDir = "./publish"; 26 | var artifactsDir = "./artifacts"; 27 | var databaseProject = "./source/Database"; 28 | var webProject = "./source/Web"; 29 | var consoleProject = "./source/Console"; 30 | 31 | GitVersion gitVersionInfo; 32 | string nugetVersion; 33 | /////////////////////////////////////////////////////////////////////////////// 34 | // SETUP / TEARDOWN 35 | /////////////////////////////////////////////////////////////////////////////// 36 | Setup(context => 37 | { 38 | gitVersionInfo = GitVersion(new GitVersionSettings { 39 | OutputType = GitVersionOutput.Json 40 | }); 41 | 42 | if(BuildSystem.IsRunningOnTeamCity) 43 | BuildSystem.TeamCity.SetBuildNumber(gitVersionInfo.NuGetVersion); 44 | 45 | nugetVersion = gitVersionInfo.NuGetVersion; 46 | 47 | Information("Building ICanHasDotnetCore v{0}", nugetVersion); 48 | Information("Informational Version {0}", gitVersionInfo.InformationalVersion); 49 | }); 50 | 51 | Teardown(context => 52 | { 53 | Information("Finished running tasks."); 54 | }); 55 | 56 | ////////////////////////////////////////////////////////////////////// 57 | // PRIVATE TASKS 58 | ////////////////////////////////////////////////////////////////////// 59 | 60 | Task("Clean") 61 | .Does(() => 62 | { 63 | CleanDirectory(artifactsDir); 64 | CleanDirectory(publishDir); 65 | CleanDirectories("./source/**/bin"); 66 | CleanDirectories("./source/**/obj"); 67 | CleanDirectories("./source/**/TestResults"); 68 | }); 69 | 70 | Task("Restore") 71 | .IsDependentOn("Clean") 72 | .Does(() => { 73 | DotNetCoreRestore("source"); 74 | var settings = new NpmInstallSettings(); 75 | settings.WorkingDirectory = webProject; 76 | NpmInstall(settings); 77 | 78 | settings.AddPackage("gulp"); 79 | NpmInstall(settings); 80 | }); 81 | 82 | Task("Build") 83 | .IsDependentOn("Restore") 84 | .IsDependentOn("Clean") 85 | .Does(() => 86 | { 87 | 88 | ReplaceRegexInFiles("./source/Web/Features/Version.ts", "version = \"[^\"]+", "version = \"" + nugetVersion); 89 | 90 | Gulp.Local.Execute(s => { 91 | s.WorkingDirectory = webProject; 92 | }); 93 | 94 | DotNetCoreBuild("./source", new DotNetCoreBuildSettings 95 | { 96 | Configuration = configuration, 97 | ArgumentCustomization = args => args.Append($"/p:Version={nugetVersion}") 98 | }); 99 | }); 100 | 101 | Task("Test") 102 | .IsDependentOn("Build") 103 | .Does(() => 104 | { 105 | DotNetCoreTest("./source/Tests/Tests.csproj", new DotNetCoreTestSettings 106 | { 107 | Configuration = configuration, 108 | NoBuild = true, 109 | ArgumentCustomization = args => args.Append("-l trx") 110 | }); 111 | }); 112 | 113 | Task("DotnetPublish") 114 | .IsDependentOn("Test") 115 | .Does(() => 116 | { 117 | DotNetCorePublish(webProject, new DotNetCorePublishSettings 118 | { 119 | Configuration = configuration, 120 | OutputDirectory = Path.Combine(publishDir, "Web") 121 | }); 122 | 123 | DotNetCorePublish(databaseProject, new DotNetCorePublishSettings 124 | { 125 | Configuration = configuration, 126 | OutputDirectory = Path.Combine(publishDir, "Database") 127 | }); 128 | 129 | DotNetCorePublish(consoleProject, new DotNetCorePublishSettings 130 | { 131 | Configuration = configuration, 132 | OutputDirectory = Path.Combine(publishDir, "Console") 133 | }); 134 | }); 135 | 136 | Task("Zip") 137 | .IsDependentOn("DotnetPublish") 138 | .Does(() => { 139 | var downloadsDir = Path.Combine(publishDir, @"Web\wwwroot\Downloads"); 140 | CreateDirectory(downloadsDir); 141 | Zip(Path.Combine(publishDir, "Console"), Path.Combine(downloadsDir, @"ICanHasDotnetCore.zip")); 142 | Zip(Path.Combine(publishDir, "Web"), Path.Combine(artifactsDir, $"ICanHasDotnetCore.Web.{nugetVersion}.zip")); 143 | Zip(Path.Combine(publishDir, "Database"), Path.Combine(artifactsDir, $"ICanHasDotnetCore.Database.{nugetVersion}.zip")); 144 | }); 145 | 146 | 147 | Task("Publish") 148 | .IsDependentOn("Zip") 149 | .WithCriteria(BuildSystem.IsRunningOnTeamCity) 150 | .Does(() => 151 | { 152 | UploadFile( 153 | $"{EnvironmentVariable("Octopus3ServerUrl")}/api/packages/raw?apiKey={EnvironmentVariable("Octopus3ApiKey")}", 154 | $"{artifactsDir}/ICanHasDotnetCore.Web." + nugetVersion + ".zip" 155 | ); 156 | 157 | UploadFile( 158 | $"{EnvironmentVariable("Octopus3ServerUrl")}/api/packages/raw?apiKey={EnvironmentVariable("Octopus3ApiKey")}", 159 | $"{artifactsDir}/ICanHasDotnetCore.Database." + nugetVersion + ".zip" 160 | ); 161 | }); 162 | 163 | 164 | ////////////////////////////////////////////////////////////////////// 165 | // TASKS 166 | ////////////////////////////////////////////////////////////////////// 167 | Task("Default") 168 | .IsDependentOn("Publish"); 169 | 170 | ////////////////////////////////////////////////////////////////////// 171 | // EXECUTION 172 | ////////////////////////////////////////////////////////////////////// 173 | RunTarget(target); 174 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | REM see http://joshua.poehls.me/powershell-batch-file-wrapper/ 3 | 4 | SET SCRIPTNAME=%~d0%~p0%~n0.ps1 5 | SET ARGS=%* 6 | IF [%ARGS%] NEQ [] GOTO ESCAPE_ARGS 7 | 8 | :POWERSHELL 9 | PowerShell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy Unrestricted -Command "& { $ErrorActionPreference = 'Stop'; & '%SCRIPTNAME%' @args; EXIT $LASTEXITCODE }" %ARGS% 10 | EXIT /B %ERRORLEVEL% 11 | 12 | :ESCAPE_ARGS 13 | SET ARGS=%ARGS:"=\"% 14 | SET ARGS=%ARGS:`=``% 15 | SET ARGS=%ARGS:'=`'% 16 | SET ARGS=%ARGS:$=`$% 17 | SET ARGS=%ARGS:{=`}% 18 | SET ARGS=%ARGS:}=`}% 19 | SET ARGS=%ARGS:(=`(% 20 | SET ARGS=%ARGS:)=`)% 21 | SET ARGS=%ARGS:,=`,% 22 | SET ARGS=%ARGS:^%=% 23 | 24 | GOTO POWERSHELL -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ########################################################################## 4 | # This is the Cake bootstrapper script for Linux and OS X. 5 | # This file was downloaded from https://github.com/cake-build/resources 6 | # Feel free to change this file to fit your needs. 7 | ########################################################################## 8 | 9 | # Define directories. 10 | SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 11 | TOOLS_DIR=$SCRIPT_DIR/tools 12 | NUGET_EXE=$TOOLS_DIR/nuget.exe 13 | CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe 14 | PACKAGES_CONFIG=$TOOLS_DIR/packages.config 15 | PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum 16 | 17 | # Define md5sum or md5 depending on Linux/OSX 18 | MD5_EXE= 19 | if [[ "$(uname -s)" == "Darwin" ]]; then 20 | MD5_EXE="md5 -r" 21 | else 22 | MD5_EXE="md5sum" 23 | fi 24 | 25 | # Define default arguments. 26 | SCRIPT="build.cake" 27 | TARGET="Default" 28 | CONFIGURATION="Release" 29 | VERBOSITY="verbose" 30 | DRYRUN= 31 | SHOW_VERSION=false 32 | SCRIPT_ARGUMENTS=() 33 | 34 | # Parse arguments. 35 | for i in "$@"; do 36 | case $1 in 37 | -s|--script) SCRIPT="$2"; shift ;; 38 | -t|--target) TARGET="$2"; shift ;; 39 | -c|--configuration) CONFIGURATION="$2"; shift ;; 40 | -v|--verbosity) VERBOSITY="$2"; shift ;; 41 | -d|--dryrun) DRYRUN="-dryrun" ;; 42 | --version) SHOW_VERSION=true ;; 43 | --) shift; SCRIPT_ARGUMENTS+=("$@"); break ;; 44 | *) SCRIPT_ARGUMENTS+=("$1") ;; 45 | esac 46 | shift 47 | done 48 | 49 | # Make sure the tools folder exist. 50 | if [ ! -d "$TOOLS_DIR" ]; then 51 | mkdir "$TOOLS_DIR" 52 | fi 53 | 54 | # Make sure that packages.config exist. 55 | if [ ! -f "$TOOLS_DIR/packages.config" ]; then 56 | echo "Downloading packages.config..." 57 | curl -Lsfo "$TOOLS_DIR/packages.config" http://cakebuild.net/download/bootstrapper/packages 58 | if [ $? -ne 0 ]; then 59 | echo "An error occured while downloading packages.config." 60 | exit 1 61 | fi 62 | fi 63 | 64 | # Download NuGet if it does not exist. 65 | if [ ! -f "$NUGET_EXE" ]; then 66 | echo "Downloading NuGet..." 67 | curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe 68 | if [ $? -ne 0 ]; then 69 | echo "An error occured while downloading nuget.exe." 70 | exit 1 71 | fi 72 | fi 73 | 74 | # Restore tools from NuGet. 75 | pushd "$TOOLS_DIR" >/dev/null 76 | if [ ! -f $PACKAGES_CONFIG_MD5 ] || [ "$( cat $PACKAGES_CONFIG_MD5 | sed 's/\r$//' )" != "$( $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' )" ]; then 77 | find . -type d ! -name . | xargs rm -rf 78 | fi 79 | 80 | mono "$NUGET_EXE" install -ExcludeVersion 81 | if [ $? -ne 0 ]; then 82 | echo "Could not restore NuGet packages." 83 | exit 1 84 | fi 85 | 86 | $MD5_EXE $PACKAGES_CONFIG | awk '{ print $1 }' >| $PACKAGES_CONFIG_MD5 87 | 88 | popd >/dev/null 89 | 90 | # Make sure that Cake has been installed. 91 | if [ ! -f "$CAKE_EXE" ]; then 92 | echo "Could not find Cake.exe at '$CAKE_EXE'." 93 | exit 1 94 | fi 95 | 96 | # Start Cake 97 | if $SHOW_VERSION; then 98 | exec mono "$CAKE_EXE" -version 99 | else 100 | exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY -configuration=$CONFIGURATION -target=$TARGET $DRYRUN "${SCRIPT_ARGUMENTS[@]}" 101 | fi -------------------------------------------------------------------------------- /gitversion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDeployment 2 | -------------------------------------------------------------------------------- /source/Console/Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net461 5 | ICanHasDotnetCore 6 | Exe 7 | Console 8 | false 9 | false 10 | false 11 | false 12 | false 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /source/Console/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using ICanHasDotnetCore.Investigator; 6 | using ICanHasDotnetCore.NugetPackages; 7 | using ICanHasDotnetCore.Output; 8 | using ICanHasDotnetCore.SourcePackageFileReaders; 9 | using Serilog; 10 | 11 | namespace ICanHasDotnetCore.Console 12 | { 13 | class Program 14 | { 15 | private static readonly HashSet ExcludeDirectories = new HashSet(StringComparer.CurrentCultureIgnoreCase) 16 | { 17 | "node_modules", 18 | "bower_components", 19 | "packages" 20 | }; 21 | 22 | static int Main(string[] args) 23 | { 24 | Log.Logger = new LoggerConfiguration() 25 | .WriteTo.ColoredConsole() 26 | .MinimumLevel.Warning() 27 | .CreateLogger(); 28 | 29 | try 30 | { 31 | if (args.Length < 2) 32 | { 33 | System.Console.Error.WriteLine("Usage: ICanHasDotnetCore.exe [dir_to_scan_2] ... [dir_to_scan_n]"); 34 | return 1; 35 | } 36 | 37 | var directories = args.Skip(1).Select(Path.GetFullPath).ToArray(); 38 | var packageFiles = FindFiles(directories).ToArray(); 39 | var result = PackageCompatabilityInvestigator.Create(new NoNugetResultCache()) 40 | .Go(packageFiles) 41 | .Result; 42 | 43 | 44 | WriteToOutputFiles(args[0], result); 45 | 46 | return 0; 47 | } 48 | catch (Exception ex) 49 | { 50 | Log.Error(ex, "Something went wrong"); 51 | return 2; 52 | } 53 | } 54 | 55 | private static void WriteToOutputFiles(string outputDirectory, InvestigationResult result) 56 | { 57 | Directory.CreateDirectory(outputDirectory); 58 | File.WriteAllLines(Path.Combine(outputDirectory, "Flat.txt"), new[] { FlatListingOutputFormatter.Format(result) }); 59 | File.WriteAllLines(Path.Combine(outputDirectory, "Tree.txt"), new[] { TreeOutputFormatter.Format(result) }); 60 | File.WriteAllLines(Path.Combine(outputDirectory, "1Level.gv"), new[] { GraphVizOutputFormatter.Format(result, 1) }); 61 | File.WriteAllLines(Path.Combine(outputDirectory, "All.gv"), new[] { GraphVizOutputFormatter.Format(result) }); 62 | File.WriteAllLines(Path.Combine(outputDirectory, "All.cql"), new[] { CypherOutputFormatter.Format(result) }); 63 | 64 | foreach (var package in result.PackageConfigResults) 65 | File.WriteAllLines(Path.Combine(outputDirectory, package.PackageName + ".gv"), new[] { GraphVizOutputFormatter.Format(package) }); 66 | 67 | System.Console.ForegroundColor = ConsoleColor.Magenta; 68 | System.Console.WriteLine($"Output written to {outputDirectory}"); 69 | System.Console.ResetColor(); 70 | } 71 | 72 | 73 | private static IEnumerable FindFiles(IEnumerable directories) 74 | { 75 | foreach (var directory in directories.Where(d => !ExcludeDirectories.Contains(Path.GetFileName(d)))) 76 | { 77 | foreach (var filename in SourcePackageFileReader.SupportedFiles) 78 | { 79 | var file = new FileInfo(Path.Combine(directory, filename)); 80 | if (file.Exists) 81 | yield return new SourcePackageFile(file.DirectoryName, filename, File.ReadAllBytes(file.FullName)); 82 | } 83 | 84 | foreach (var fileExtension in SourcePackageFileReader.SupportedExtensions) 85 | { 86 | foreach (var file in Directory.GetFiles(directory, $"*{fileExtension}")) 87 | { 88 | var f = new FileInfo(Path.Combine(directory, file)); 89 | if(f.Exists) 90 | yield return new SourcePackageFile(f.DirectoryName, Path.GetFileName(file), File.ReadAllBytes(f.FullName)); 91 | } 92 | } 93 | 94 | foreach (var packageFile in FindFiles(Directory.EnumerateDirectories(directory))) 95 | yield return packageFile; 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /source/Console/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("Octopus Deploy")] 10 | [assembly: AssemblyProduct("ICanHasDotnetCore.Console")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyFileVersion("0.0.0.0")] 13 | [assembly: AssemblyVersion("0.0.0.0")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("3dc92468-cf9b-433d-95bc-996799941a0a")] 22 | -------------------------------------------------------------------------------- /source/Database/AlwaysRun/RemoveKnownReplacementsFromStatistics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlClient; 3 | using ICanHasDotnetCore.NugetPackages; 4 | 5 | namespace ICanHasDotnetCore.Database.AlwaysRun 6 | { 7 | public class RemoveKnownReplacementsFromStatistics 8 | { 9 | public static void Run(SqlConnection connection) 10 | { 11 | Console.WriteLine("Removing Known Replacements from Package Statistics"); 12 | foreach (var replacement in KnownReplacementsRepository.All) 13 | { 14 | var sql = replacement.StartsWith 15 | ? "DELETE FROM PackageStatistics WHERE Name like @name + '%'" 16 | : "DELETE FROM PackageStatistics WHERE Name = @name"; 17 | using (var cmd = new SqlCommand(sql, connection)) 18 | { 19 | cmd.Parameters.AddWithValue("@name", replacement.Id); 20 | cmd.ExecuteNonQuery(); 21 | } 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /source/Database/Database.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net461 5 | Database 6 | Exe 7 | Database 8 | false 9 | false 10 | false 11 | false 12 | false 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | PreserveNewest 24 | 25 | 26 | PreserveNewest 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /source/Database/PostDeploy.ps1: -------------------------------------------------------------------------------- 1 | . .\Database.exe 2 | if($LastExitCode -ne 0) 3 | { 4 | throw "Error occured, return code $LastExitCode" 5 | } -------------------------------------------------------------------------------- /source/Database/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlClient; 3 | using System.Diagnostics; 4 | using System.Runtime.CompilerServices; 5 | using DbUp; 6 | using ICanHasDotnetCore.Database.AlwaysRun; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.PlatformAbstractions; 9 | 10 | namespace ICanHasDotnetCore.Database 11 | { 12 | public class Program 13 | { 14 | public static int Main(string[] args) 15 | { 16 | var configuration = new ConfigurationBuilder() 17 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 18 | .Build(); 19 | 20 | var connectionString = configuration["ConnectionString"]; 21 | Console.WriteLine($"Connection String: {connectionString}"); 22 | Console.WriteLine("Ensuring Database"); 23 | EnsureDatabase.For.SqlDatabase(connectionString); 24 | 25 | var upgrader = DeployChanges.To 26 | .SqlDatabase(connectionString) 27 | .WithScriptsEmbeddedInAssembly(typeof(Program).Assembly) 28 | .LogToConsole() 29 | .Build(); 30 | 31 | var result = upgrader.PerformUpgrade(); 32 | 33 | if (!result.Successful) 34 | { 35 | Console.ForegroundColor = ConsoleColor.Red; 36 | Console.WriteLine(result.Error); 37 | Console.ResetColor(); 38 | Debugger.Break(); 39 | return -1; 40 | } 41 | 42 | using (var con = new SqlConnection(connectionString)) 43 | { 44 | con.Open(); 45 | RemoveKnownReplacementsFromStatistics.Run(con); 46 | } 47 | 48 | Console.ForegroundColor = ConsoleColor.Green; 49 | Console.WriteLine("Success!"); 50 | Console.ResetColor(); 51 | return 0; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /source/Database/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("Octopus Deploy")] 10 | [assembly: AssemblyProduct("ICanHasDotnetCore.Database")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyFileVersion("0.0.0.0")] 13 | [assembly: AssemblyVersion("0.0.0.0")] 14 | // Setting ComVisible to false makes the types in this assembly not visible 15 | // to COM components. If you need to access a type in this assembly from 16 | // COM, set the ComVisible attribute to true on that type. 17 | [assembly: ComVisible(false)] 18 | 19 | // The following GUID is for the ID of the typelib if this project is exposed to COM 20 | [assembly: Guid("c0a251e7-06a4-4df0-8847-649b9fedb2da")] 21 | -------------------------------------------------------------------------------- /source/Database/Scripts/001-PackageStatistics.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[PackageStatistics]( 2 | [Name] [nvarchar](400) NOT NULL, 3 | [Count] [int] NOT NULL, 4 | [LatestSupportType] [nvarchar](20) NOT NULL, 5 | CONSTRAINT [PK_PackageStatistics] PRIMARY KEY CLUSTERED 6 | ( 7 | [Name] ASC 8 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 9 | ) ON [PRIMARY] 10 | -------------------------------------------------------------------------------- /source/Database/Scripts/002-RemoveKnownReplacementPackages.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM PackageStatistics WHERE Name like 'Microsoft.AspNet.%' 2 | DELETE FROM PackageStatistics WHERE Name like 'EntityFramework.%' 3 | DELETE FROM PackageStatistics WHERE Name like 'Microsoft.Data.Entity.%' 4 | DELETE FROM PackageStatistics WHERE Name = 'Microsoft.Web.Infrastructure' 5 | DELETE FROM PackageStatistics WHERE Name = 'Owin' 6 | DELETE FROM PackageStatistics WHERE Name = 'Microsoft.Owin' 7 | DELETE FROM PackageStatistics WHERE Name like 'Microsoft.Owin.*' 8 | -------------------------------------------------------------------------------- /source/Database/Scripts/003-AddNugetResultCache.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[NugetResultCache]( 2 | [Id] [nvarchar](300) NOT NULL, 3 | [Version] [nvarchar](150) NOT NULL, 4 | [SupportType] [nvarchar](20) NOT NULL, 5 | [ProjectUrl] [nvarchar](1000) NULL, 6 | [Dependencies] [nvarchar](max) NOT NULL, 7 | [Frameworks] [nvarchar](max) NOT NULL, 8 | CONSTRAINT [PK_NugetResultCache] PRIMARY KEY CLUSTERED 9 | ( 10 | [Id] ASC, 11 | [Version] ASC 12 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 13 | ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 14 | 15 | GO -------------------------------------------------------------------------------- /source/Database/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionString": "Server=localhost;Database=ICanHasDotnetCore;Trusted_Connection=True;" 3 | } 4 | -------------------------------------------------------------------------------- /source/ICanHasDotnetCore.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.4 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E4E66474-C9BA-49CD-B274-F5AB7F865FE0}" 7 | ProjectSection(SolutionItems) = preProject 8 | .gitignore = .gitignore 9 | build.cmd = build.cmd 10 | build.ps1 = build.ps1 11 | EndProjectSection 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "Web\Web.csproj", "{95D6E787-309B-448B-8F1D-46D3F5E64C94}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magic", "Magic\Magic.csproj", "{89CAEA64-C5FE-45F5-8B5D-53AE40A1FC92}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console", "Console\Console.csproj", "{3DC92468-CF9B-433D-95BC-996799941A0A}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj", "{E7A09E85-977A-4852-8F5B-9707525D7104}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Database", "Database\Database.csproj", "{C0A251E7-06A4-4DF0-8847-649B9FEDB2DA}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {95D6E787-309B-448B-8F1D-46D3F5E64C94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {95D6E787-309B-448B-8F1D-46D3F5E64C94}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {95D6E787-309B-448B-8F1D-46D3F5E64C94}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {95D6E787-309B-448B-8F1D-46D3F5E64C94}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {89CAEA64-C5FE-45F5-8B5D-53AE40A1FC92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {89CAEA64-C5FE-45F5-8B5D-53AE40A1FC92}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {89CAEA64-C5FE-45F5-8B5D-53AE40A1FC92}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {89CAEA64-C5FE-45F5-8B5D-53AE40A1FC92}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {3DC92468-CF9B-433D-95BC-996799941A0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {3DC92468-CF9B-433D-95BC-996799941A0A}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {3DC92468-CF9B-433D-95BC-996799941A0A}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {3DC92468-CF9B-433D-95BC-996799941A0A}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {E7A09E85-977A-4852-8F5B-9707525D7104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {E7A09E85-977A-4852-8F5B-9707525D7104}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {E7A09E85-977A-4852-8F5B-9707525D7104}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {E7A09E85-977A-4852-8F5B-9707525D7104}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {C0A251E7-06A4-4DF0-8847-649B9FEDB2DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {C0A251E7-06A4-4DF0-8847-649B9FEDB2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {C0A251E7-06A4-4DF0-8847-649B9FEDB2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {C0A251E7-06A4-4DF0-8847-649B9FEDB2DA}.Release|Any CPU.Build.0 = Release|Any CPU 49 | EndGlobalSection 50 | GlobalSection(SolutionProperties) = preSolution 51 | HideSolutionNode = FALSE 52 | EndGlobalSection 53 | EndGlobal 54 | -------------------------------------------------------------------------------- /source/Magic/Investigator/InvestigationResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace ICanHasDotnetCore.Investigator 6 | { 7 | public class InvestigationResult 8 | { 9 | public InvestigationResult(IReadOnlyList packageConfigResults) 10 | { 11 | PackageConfigResults = packageConfigResults; 12 | } 13 | 14 | public IReadOnlyList PackageConfigResults { get; } 15 | 16 | public IReadOnlyList GetAllDistinctRecursive(int maxLevels = int.MaxValue) 17 | { 18 | return PackageConfigResults 19 | .Concat(PackageConfigResults.SelectMany(r => r.GetDependenciesResursive(maxLevels - 1))) 20 | .GroupBy(p => p.PackageName) 21 | .Select(g => g.First()) 22 | .ToArray(); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /source/Magic/Investigator/PackageResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using ICanHasDotnetCore.NugetPackages; 5 | using ICanHasDotnetCore.Plumbing; 6 | 7 | namespace ICanHasDotnetCore.Investigator 8 | { 9 | public class PackageResult 10 | { 11 | private PackageResult() 12 | { 13 | } 14 | 15 | public string PackageName { get; private set; } 16 | public string ProjectUrl { get; private set; } 17 | public string Error { get; private set; } 18 | public IReadOnlyList Dependencies { get; private set; } 19 | public bool WasSuccessful => SupportType != SupportType.Error; 20 | public SupportType SupportType { get; private set; } 21 | 22 | public Option MoreInformation { get; set; } 23 | 24 | public static PackageResult Failed(string packageName, string error) 25 | { 26 | return new PackageResult() 27 | { 28 | PackageName = packageName, 29 | Error = error, 30 | Dependencies = new PackageResult[0], 31 | SupportType = SupportType.Error, 32 | MoreInformation = Option.ToNone 33 | }; 34 | } 35 | 36 | public static PackageResult InvestigationTarget(string name, IReadOnlyList dependencies) 37 | { 38 | return new PackageResult() 39 | { 40 | PackageName = name, 41 | Dependencies = dependencies, 42 | SupportType = SupportType.InvestigationTarget, 43 | MoreInformation = Option.ToNone 44 | }; 45 | } 46 | 47 | public static PackageResult KnownReplacement(string name, MoreInformation moreInformation) 48 | { 49 | return new PackageResult() 50 | { 51 | PackageName = name, 52 | Dependencies = new PackageResult[0], 53 | SupportType = SupportType.KnownReplacementAvailable, 54 | MoreInformation = moreInformation 55 | }; 56 | } 57 | 58 | 59 | public static PackageResult Success(NugetPackage package, IReadOnlyList dependencies, Option moreInformation) 60 | { 61 | var isAggregationPackage = package.SupportType == SupportType.NoDotNetLibraries && 62 | dependencies.Any() && 63 | dependencies.All(d => d.SupportType == SupportType.PreRelease || 64 | d.SupportType == SupportType.Supported || 65 | d.SupportType == SupportType.KnownReplacementAvailable || 66 | d.SupportType == SupportType.Unsupported 67 | ); 68 | 69 | var isSupportedAggregationPackage = isAggregationPackage && 70 | dependencies.All(d => d.SupportType == SupportType.PreRelease || d.SupportType == SupportType.Supported); 71 | 72 | var supportType = isSupportedAggregationPackage 73 | ? (package.IsPrerelease ? SupportType.Supported : SupportType.PreRelease) 74 | : (isAggregationPackage ? SupportType.Unsupported : package.SupportType); 75 | 76 | return new PackageResult() 77 | { 78 | PackageName = package.Id, 79 | Dependencies = dependencies, 80 | SupportType = supportType, 81 | ProjectUrl = package.ProjectUrl, 82 | MoreInformation = moreInformation 83 | }; 84 | } 85 | 86 | public IEnumerable GetDependenciesResursive(int maxLevels = Int32.MaxValue) 87 | { 88 | if (maxLevels == 0) 89 | return Dependencies; 90 | return Dependencies.Concat(Dependencies.SelectMany(d => d.GetDependenciesResursive(maxLevels - 1))); 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /source/Magic/Magic.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net461 5 | Magic 6 | Magic 7 | false 8 | false 9 | false 10 | false 11 | false 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /source/Magic/NugetPackages/Data/KnownReplacements.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "Microsoft.Bcl", 4 | "message": "This package was introduced to provide async support to pre-.NET 4.5 platforms", 5 | "linkText": "About the package", 6 | "url": "https://blogs.msdn.microsoft.com/bclteam/2012/10/22/using-asyncawait-without-net-framework-4-5/" 7 | }, 8 | { 9 | "id": "Microsoft.Bcl.Async", 10 | "message": "This package was introduced to provide async support to pre-.NET 4.5 platforms", 11 | "linkText": "About the package", 12 | "url": "https://blogs.msdn.microsoft.com/bclteam/2012/10/22/using-asyncawait-without-net-framework-4-5/" 13 | }, 14 | { 15 | "id": "Microsoft.Bcl.Build", 16 | "message": "This package provided build time support for Microsoft.Bcl", 17 | "linkText": "About the package", 18 | "url": "https://blogs.msdn.microsoft.com/bclteam/2012/10/22/using-asyncawait-without-net-framework-4-5/" 19 | }, 20 | { 21 | "id": "Microsoft.Net.Http", 22 | "message": "This package backported HttpClient to pre-.NET 4.5 platforms", 23 | "linkText": "About the package", 24 | "url": "https://blogs.msdn.microsoft.com/bclteam/2013/02/18/portable-httpclient-for-net-framework-and-windows-phone/" 25 | }, 26 | { 27 | "id": "Microsoft.Net.Http", 28 | "message": "This package backported HttpClient to pre-.NET 4.5 platforms", 29 | "linkText": "About the package", 30 | "url": "https://blogs.msdn.microsoft.com/bclteam/2013/02/18/portable-httpclient-for-net-framework-and-windows-phone/" 31 | }, 32 | { 33 | "id": "EntityFramework.MicrosoftSqlServer", 34 | "message": "Replaced by Microsoft.EntityFrameworkCore.SqlServer", 35 | "linkText": "ASP.NET Core Package id Changes", 36 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes" 37 | }, 38 | { 39 | "id": "Microsoft.AspNet.Diagnostics.Entity", 40 | "message": "Replaced by Microsoft.AspNetCore.Dianostics.EntityFrameworkCore", 41 | "linkText": "ASP.NET Core Package id Changes", 42 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes" 43 | }, 44 | { 45 | "id": "Microsoft.AspNet.Identity.EntityFramework", 46 | "message": "Replaced by Microsoft.AspNetCore.Identity.EntityFrameworkCore", 47 | "linkText": "ASP.NET Core Package id Changes", 48 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes" 49 | }, 50 | { 51 | "id": "Microsoft.AspNet.", 52 | "message": "Microsoft.AspNet.* packages have been replaced with Microsoft.AspNetCore.*", 53 | "linkText": "ASP.NET Core Package id Changes", 54 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes", 55 | "startsWith": true 56 | }, 57 | { 58 | "id": "EntityFramework", 59 | "message": "EntityFramework has been replaced with Microsoft.EntityFrameworkCore", 60 | "linkText": "ASP.NET Core Package id Changes", 61 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes" 62 | }, 63 | { 64 | "id": "EntityFramework.", 65 | "message": "EntityFramework.* packages have been replaced with Microsoft.EntityFrameworkCore.*", 66 | "linkText": "ASP.NET Core Package id Changes", 67 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes", 68 | "startsWith": true 69 | }, 70 | { 71 | "id": "Microsoft.Data.Entity.", 72 | "message": "Microsoft.Data.Entity.* packages have been replaced with Microsoft.EntityFrameworkCore.*", 73 | "linkText": "ASP.NET Core Package id Changes", 74 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes", 75 | "startsWith": true 76 | }, 77 | { 78 | "id": "Microsoft.AspNet.Tooling.Razor", 79 | "message": "Replaced by Microsoft.AspNetCore.Razor.Tools", 80 | "linkText": "ASP.NET Core Package id Changes", 81 | "url": "https://docs.asp.net/en/latest/migration/rc1-to-rtm.html#namespace-and-package-id-changes" 82 | }, 83 | { 84 | "id": "Microsoft.Web.Infrastructure", 85 | "message": "This package was used to register modules in the ASP.NET pipeline, the ASP.NET Core architecure changes make this obsolete" 86 | }, 87 | { 88 | "id": "Owin", 89 | "message": "Owin has been replaced by Microsoft.AspNetCore.Owin", 90 | "linkText": "Using OWIN with ASP.NET Core", 91 | "url": "https://docs.asp.net/en/latest/fundamentals/owin.html" 92 | }, 93 | { 94 | "id": "Microsoft.Owin", 95 | "message": "Owin has been replaced by Microsoft.AspNetCore.Owin", 96 | "linkText": "Using OWIN with ASP.NET Core", 97 | "url": "https://docs.asp.net/en/latest/fundamentals/owin.html" 98 | }, 99 | { 100 | "id": "Microsoft.Owin.", 101 | "message": "Owin has been replaced by Microsoft.AspNetCore.Owin", 102 | "linkText": "Using OWIN with ASP.NET Core", 103 | "url": "https://docs.asp.net/en/latest/fundamentals/owin.html", 104 | "startsWith": true 105 | }, 106 | { 107 | "id": "HtmlAgilityPack", 108 | "message": "HtmlAgilityPack has been replaced by HtmlAgilityPack.NetCore", 109 | "linkText": "Nuget", 110 | "url": "https://www.nuget.org/packages/HtmlAgilityPack.NetCore/", 111 | "startsWith": false 112 | }, 113 | { 114 | "id": "iTextSharp", 115 | "message": "iTextSharp has been replaced by iTextSharp.LGPLv2.Core", 116 | "linkText": "Nuget", 117 | "url": "https://www.nuget.org/packages/iTextSharp.LGPLv2.Core/", 118 | "startsWith": false 119 | } 120 | ] 121 | -------------------------------------------------------------------------------- /source/Magic/NugetPackages/Data/MoreInformation.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "Antlr", 4 | "message": "Often this package is not referenced directly, but as a dependency of WebGrease", 5 | "linkText": "GitHub Issue tracking .NET Core support", 6 | "url": "https://github.com/antlr/antlrcs/issues/42" 7 | }, 8 | { 9 | "id": "WebGrease", 10 | "message": "Provides javascript, css and image optimisation and minification. ASP.NET Core defaults to gulp for these tasks.", 11 | "linkText": "Forum question on whether .NET Core will be supported", 12 | "url": "http://webgrease.codeplex.com/discussions/641500" 13 | }, 14 | { 15 | "id": "OctoPack", 16 | "message": "Until the build tooling stabilised (ie going back to msbuild), there is an alternative way of packaging.", 17 | "linkText": "Octopus - Deploying ASP.NET Core Web Applications", 18 | "url": "http://docs.octopusdeploy.com/display/OD/Deploying+ASP.NET+Core+Web+Applications" 19 | }, 20 | { 21 | "id": "NSubstitute", 22 | "message": "An alpha release is available on nuget.org (inlisted, see the GitHub issue)", 23 | "linkText": "https://github.com/nsubstitute/NSubstitute/pull/197", 24 | "url": "GitHub issue tracking support" 25 | } 26 | ] -------------------------------------------------------------------------------- /source/Magic/NugetPackages/Data/README.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | The two `json` files in this directory are the source of the of the data shown on the Website's knowledge page. 4 | The KnownReplacements are also used when building the result graph. 5 | 6 | To contribute, fork this repository, edit these file and send a PR. You can edit them directly in GitHub. 7 | Since there are tests ensuring that these files can be parsed, we will catch any syntax errors at build time. 8 | -------------------------------------------------------------------------------- /source/Magic/NugetPackages/INugetResultCache.cs: -------------------------------------------------------------------------------- 1 | using ICanHasDotnetCore.Plumbing; 2 | using NuGet; 3 | 4 | namespace ICanHasDotnetCore.NugetPackages 5 | { 6 | public interface INugetResultCache 7 | { 8 | Option Get(string id, SemanticVersion version); 9 | void Store(NugetPackage package); 10 | } 11 | 12 | public class NoNugetResultCache : INugetResultCache 13 | { 14 | public Option Get(string id, SemanticVersion version) 15 | { 16 | return Option.ToNone; 17 | } 18 | 19 | public void Store(NugetPackage package) 20 | { 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/KnownReplacementsRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using ICanHasDotnetCore.Plumbing; 5 | using Newtonsoft.Json; 6 | 7 | namespace ICanHasDotnetCore.NugetPackages 8 | { 9 | public class KnownReplacementsRepository 10 | { 11 | static KnownReplacementsRepository() 12 | { 13 | using (var s = typeof(KnownReplacementsRepository).Assembly.GetManifestResourceStream($"Magic.NugetPackages.Data.KnownReplacements.json")) 14 | using (var sr = new StreamReader(s)) 15 | { 16 | All = JsonConvert.DeserializeObject(sr.ReadToEnd()) 17 | .OrderBy(r => r.Id) 18 | .ThenBy(r => r.StartsWith) // false first 19 | .ToArray(); 20 | } 21 | } 22 | 23 | public static readonly IReadOnlyList All; 24 | 25 | public static Option Get(string id) => All.FirstOrNone(k => k.AppliesTo(id)); 26 | } 27 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/MoreInformation.cs: -------------------------------------------------------------------------------- 1 | using ICanHasDotnetCore.Plumbing.Extensions; 2 | 3 | namespace ICanHasDotnetCore.NugetPackages 4 | { 5 | public class MoreInformation 6 | { 7 | public string Id { get; } 8 | public string Message { get; set; } 9 | public string LinkText { get; set; } 10 | public string Url { get; set; } 11 | public bool StartsWith { get; set; } 12 | 13 | public MoreInformation(string id) 14 | { 15 | Id = id; 16 | } 17 | 18 | public bool AppliesTo(string id) => StartsWith ? id.StartsWithOrdinalIgnoreCase(Id) : id.EqualsOrdinalIgnoreCase(Id); 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/MoreInformationRepository.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using ICanHasDotnetCore.Plumbing; 5 | using Newtonsoft.Json; 6 | 7 | namespace ICanHasDotnetCore.NugetPackages 8 | { 9 | public class MoreInformationRepository 10 | { 11 | static MoreInformationRepository() 12 | { 13 | using (var s = typeof(KnownReplacementsRepository).Assembly.GetManifestResourceStream($"Magic.NugetPackages.Data.MoreInformation.json")) 14 | using (var sr = new StreamReader(s)) 15 | { 16 | All = JsonConvert.DeserializeObject(sr.ReadToEnd()) 17 | .OrderBy(r => r.Id) 18 | .ThenBy(r => r.StartsWith) // false first 19 | .ToArray(); 20 | } 21 | } 22 | 23 | public static readonly IReadOnlyList All; 24 | 25 | public static Option Get(string id) => All.FirstOrNone(k => k.AppliesTo(id)); 26 | } 27 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/NugetPackage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Runtime.Versioning; 3 | using ICanHasDotnetCore.Plumbing; 4 | using NuGet; 5 | 6 | namespace ICanHasDotnetCore.NugetPackages 7 | { 8 | public class NugetPackage 9 | { 10 | public NugetPackage(string id, IReadOnlyList dependencies, SupportType supportType, Option version, IReadOnlyList frameworks) 11 | { 12 | Id = id; 13 | Dependencies = dependencies; 14 | SupportType = supportType; 15 | Version = version; 16 | Frameworks = frameworks; 17 | } 18 | 19 | public IReadOnlyList Dependencies { get; } 20 | public string Id { get; } 21 | public SupportType SupportType { get; } 22 | public Option Version { get; } 23 | public string ProjectUrl { get; set; } 24 | public bool IsPrerelease => Version.IfSome(v => string.IsNullOrEmpty(v.SpecialVersion).Some()).ValueOr(false); 25 | public IReadOnlyList Frameworks { get; } 26 | } 27 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/PackageRepositoryWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using NuGet; 4 | 5 | namespace ICanHasDotnetCore.NugetPackages 6 | { 7 | public interface IPackageRepositoryWrapper 8 | { 9 | Task GetLatestPackage(string id, bool prerelease); 10 | Task GetPackage(string id, SemanticVersion version); 11 | } 12 | 13 | public class PackageRepositoryWrapper : IPackageRepositoryWrapper 14 | { 15 | private readonly DataServicePackageRepository _repository = new DataServicePackageRepository(new Uri("https://www.nuget.org/api/v2")); 16 | public Task GetLatestPackage(string id, bool prerelease) 17 | { 18 | return Task.Run(() => _repository.FindPackage(id, (IVersionSpec)null, prerelease, false)); 19 | } 20 | 21 | public Task GetPackage(string id, SemanticVersion version) 22 | { 23 | return Task.Run(() => _repository.FindPackage(id, new VersionSpec(version), true, false)); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/PclProfileCompatabilityChecker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using NuGet; 6 | 7 | namespace ICanHasDotnetCore.NugetPackages 8 | { 9 | public class PclProfileCompatabilityChecker 10 | { 11 | 12 | private static readonly NetPortableProfile[] SupportedPclProfiles = new[] 13 | { 14 | NetPortableProfile.Parse("net45+netcore45"), // Profile7 15 | NetPortableProfile.Parse("win81+wp81"), // Profile31 16 | NetPortableProfile.Parse("win81+wpa81"), // Profile32 17 | NetPortableProfile.Parse("net451+win81"), // Profile44 18 | NetPortableProfile.Parse("net45+wp8"), // Profile49 19 | NetPortableProfile.Parse("net45+win8+wp8"), // Profile78 20 | NetPortableProfile.Parse("wp81+wpa81"), // Profile84 21 | NetPortableProfile.Parse("net45+win8+wpa81"), // Profile111 22 | NetPortableProfile.Parse("net451+win81+wpa81"), // Profile151 23 | NetPortableProfile.Parse("win81+wp81+wpa81"), // Profile157 24 | NetPortableProfile.Parse("net45+win8+wpa81+wp8") // Profile259 25 | }; 26 | 27 | public static bool Check(string profileValue) 28 | { 29 | profileValue = Regex.Replace(profileValue, @"wpa\+", "wpa81+"); 30 | profileValue = Regex.Replace(profileValue, "wpa$", "wpa81"); 31 | 32 | var profile = NetPortableProfile.Parse(profileValue); 33 | return SupportedPclProfiles.Any(p => p.IsCompatibleWith(profile)); 34 | } 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /source/Magic/NugetPackages/SupportType.cs: -------------------------------------------------------------------------------- 1 | namespace ICanHasDotnetCore.NugetPackages 2 | { 3 | public enum SupportType 4 | { 5 | NotFound = 0, 6 | Supported = 1, 7 | PreRelease = 2, 8 | Unsupported = 3, 9 | NoDotNetLibraries = 4, 10 | KnownReplacementAvailable = 5, 11 | InvestigationTarget = 6, 12 | Error = 7 13 | } 14 | } -------------------------------------------------------------------------------- /source/Magic/Output/CypherOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ICanHasDotnetCore.Investigator; 6 | using ICanHasDotnetCore.NugetPackages; 7 | 8 | namespace ICanHasDotnetCore.Output 9 | { 10 | public class CypherOutputFormatter 11 | { 12 | public static string Format(InvestigationResult investigationResult, int levels = int.MaxValue) 13 | { 14 | var allResults = investigationResult.GetAllDistinctRecursive(levels); 15 | return Format(allResults, "All"); 16 | } 17 | 18 | public static string Format(PackageResult result) 19 | { 20 | var allResults = result.GetDependenciesResursive().Distinct().ToArray(); 21 | return Format(allResults, result.PackageName); 22 | } 23 | 24 | private static string Format(IReadOnlyList results, string name) 25 | { 26 | var sb = new StringBuilder(); 27 | sb.AppendLine("// This file can be used with any Cypher based DB or on https://neo4j.com/sandbox-v2/ "); 28 | var identifiersByPackageName = new Dictionary(); 29 | for (var i = 0; i < results.Count; i++) 30 | identifiersByPackageName.Add(results[i].PackageName, $"p{i}"); 31 | 32 | var creates = new List(); 33 | foreach (var result in results) 34 | { 35 | var identifier = identifiersByPackageName[result.PackageName]; 36 | creates.Add($"({identifier}:Package:{SupportTypeToLabel(result.SupportType)} {{Name:'{EscapeText(result.PackageName)}', ProjectUrl:'{EscapeText(result.ProjectUrl)}' }})"); 37 | } 38 | 39 | foreach (var result in results) 40 | { 41 | var fromId = identifiersByPackageName[result.PackageName]; 42 | foreach (var dep in result.Dependencies) 43 | { 44 | var toId = identifiersByPackageName[dep.PackageName]; 45 | creates.Add($"({fromId})-[:DEPENDS_ON]->({toId})"); 46 | } 47 | } 48 | 49 | foreach (var result in results.Where(r => r.SupportType == SupportType.KnownReplacementAvailable && r.MoreInformation.Some)) 50 | { 51 | var id = identifiersByPackageName[result.PackageName]; 52 | var info = result.MoreInformation?.Value; 53 | if(info != null) 54 | creates.Add($"({id})<-[:HAS_KNOWN_REPLACEMENT]-(:Replacement {{Id:'{info.Id}', LinkText:'{EscapeText(info.LinkText)}', Message:'{EscapeText(info.Message)}', Url:'{EscapeText(info.LinkText)}', StartsWith: {info.StartsWith}}})"); 55 | } 56 | 57 | sb.AppendLine("CREATE"); 58 | sb.AppendLine($"\t{string.Join($",{Environment.NewLine}\t", creates)}"); 59 | return sb.ToString(); 60 | } 61 | 62 | private static string SupportTypeToLabel(SupportType supportType) 63 | { 64 | switch (supportType) 65 | { 66 | case SupportType.NotFound: 67 | case SupportType.Supported: 68 | case SupportType.PreRelease: 69 | case SupportType.Error: 70 | case SupportType.NoDotNetLibraries: 71 | case SupportType.InvestigationTarget: 72 | return supportType.ToString(); 73 | case SupportType.Unsupported: 74 | case SupportType.KnownReplacementAvailable: 75 | return "Unsupported"; 76 | default: 77 | throw new ArgumentOutOfRangeException(nameof(supportType), supportType, null); 78 | } 79 | } 80 | 81 | private static string EscapeText(string toEscape) 82 | { 83 | return string.IsNullOrWhiteSpace(toEscape) 84 | ? toEscape 85 | : toEscape.Replace("\\", "\\\\"); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /source/Magic/Output/FlatListingOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using ICanHasDotnetCore.Investigator; 4 | using ICanHasDotnetCore.NugetPackages; 5 | 6 | namespace ICanHasDotnetCore.Output 7 | { 8 | public class FlatListingOutputFormatter 9 | { 10 | public static string Format(InvestigationResult investigationResult) 11 | { 12 | var sb = new StringBuilder(); 13 | foreach (var result in investigationResult.GetAllDistinctRecursive()) 14 | { 15 | Format(sb, result); 16 | } 17 | return sb.ToString(); 18 | } 19 | 20 | internal static void Format(StringBuilder sb, PackageResult result) 21 | { 22 | sb.Append(result.PackageName).Append(" "); 23 | switch (result.SupportType) 24 | { 25 | case SupportType.NotFound: 26 | sb.Append("[Not Found]"); 27 | break; 28 | case SupportType.Supported: 29 | sb.Append("[Supported]"); 30 | break; 31 | case SupportType.PreRelease: 32 | sb.Append("[Supported (Pre-Release)]"); 33 | break; 34 | case SupportType.Unsupported: 35 | sb.Append("[Unsupported]"); 36 | break; 37 | case SupportType.KnownReplacementAvailable: 38 | sb.Append("[Known Replacement Available]"); 39 | break; 40 | case SupportType.InvestigationTarget: 41 | sb.Append("[Your Project]"); 42 | break; 43 | case SupportType.Error: 44 | sb.Append(result.Error); 45 | break; 46 | case SupportType.NoDotNetLibraries: 47 | sb.Append("[Not a .NET Library]"); 48 | break; 49 | default: 50 | throw new ArgumentException(); 51 | } 52 | 53 | if (result.MoreInformation.Some) 54 | { 55 | var info = result.MoreInformation.Value; 56 | 57 | if (info.Message != null) 58 | sb.Append(" ").Append(info.Message); 59 | 60 | if (info.Url != null) 61 | { 62 | sb.Append(" - "); 63 | if (info.LinkText != null) 64 | sb.Append(info.LinkText).Append(": "); 65 | sb.Append(info.Url); 66 | } 67 | } 68 | 69 | sb.AppendLine(); 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /source/Magic/Output/GraphVizOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using ICanHasDotnetCore.Investigator; 6 | using ICanHasDotnetCore.NugetPackages; 7 | 8 | namespace ICanHasDotnetCore.Output 9 | { 10 | public class GraphVizOutputFormatter 11 | { 12 | public static string Format(InvestigationResult investigationResult, int levels = Int32.MaxValue) 13 | { 14 | var allResults = investigationResult.GetAllDistinctRecursive(levels); 15 | return Format(allResults, "All"); 16 | } 17 | 18 | public static string Format(PackageResult result) 19 | { 20 | var allResults = result.GetDependenciesResursive().Distinct().ToArray(); 21 | return Format(allResults, result.PackageName); 22 | } 23 | 24 | private static string Format(IReadOnlyList results, string name) 25 | { 26 | var sb = new StringBuilder(); 27 | sb.AppendLine("# This file can be used with GraphVis or http://www.webgraphviz.com/"); 28 | sb.AppendLine($"digraph \"{name}\" {{"); 29 | sb.AppendLine(" graph[layout = dot];"); 30 | sb.AppendLine(" node[style = filled,shape=box];"); 31 | 32 | foreach (var result in results) 33 | foreach (var dep in result.Dependencies) 34 | sb.AppendLine($@" ""{result.PackageName}"" -> ""{dep.PackageName}"";"); 35 | foreach (var result in results) 36 | sb.AppendLine($@" ""{result.PackageName}"" [color=""{GetResultColour(result)}""];"); 37 | 38 | sb.AppendLine("}"); 39 | return sb.ToString(); 40 | } 41 | 42 | private static string GetResultColour(PackageResult result) 43 | { 44 | switch (result.SupportType) 45 | { 46 | case SupportType.NotFound: 47 | return "#E0E0E0"; // Grey 48 | case SupportType.Supported: 49 | return "#A5D6A7"; // green 50 | case SupportType.PreRelease: 51 | return "#80CBC4"; // teal 52 | case SupportType.Unsupported: 53 | return "#FFCC80"; // orange 54 | case SupportType.KnownReplacementAvailable: 55 | return "#81D4FA"; //blue 56 | case SupportType.InvestigationTarget: 57 | return "#B39DDB"; // purple 58 | case SupportType.NoDotNetLibraries: 59 | return "#B0BEC5"; // blue grey 60 | case SupportType.Error: 61 | return "#ef9a9a"; // red 62 | default: 63 | return "#FAFAFA"; // B&W 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /source/Magic/Output/TreeOutputFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | using ICanHasDotnetCore.Investigator; 4 | 5 | namespace ICanHasDotnetCore.Output 6 | { 7 | public class TreeOutputFormatter 8 | { 9 | public static string Format(InvestigationResult investigationResult) 10 | { 11 | var sb = new StringBuilder(); 12 | Format(sb, investigationResult.PackageConfigResults); 13 | return sb.ToString(); 14 | } 15 | 16 | public static void Format(StringBuilder sb, IEnumerable packages, string indent = "") 17 | { 18 | foreach (var package in packages) 19 | { 20 | sb.Append(indent); 21 | FlatListingOutputFormatter.Format(sb, package); 22 | 23 | if (package.WasSuccessful) 24 | Format(sb, package.Dependencies, indent + " "); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /source/Magic/Plumbing/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ICanHasDotnetCore.Plumbing.Extensions 5 | { 6 | public static class StringExtensions 7 | { 8 | public static bool EqualsOrdinalIgnoreCase(this string str, string value) 9 | => str.Equals(value, StringComparison.OrdinalIgnoreCase); 10 | public static bool StartsWithOrdinalIgnoreCase(this string str, string value) 11 | => str.StartsWith(value, StringComparison.OrdinalIgnoreCase); 12 | 13 | public static string CommaSeperate(this IEnumerable items) => string.Join(", ", items); 14 | } 15 | } -------------------------------------------------------------------------------- /source/Magic/Plumbing/Option.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ICanHasDotnetCore.Plumbing 5 | { 6 | public class Option 7 | { 8 | private readonly T _value; 9 | public static readonly Option ToNone = new Option(false, default(T)); 10 | 11 | public static Option ToSome(T value) => new Option(true, value); 12 | 13 | private Option(bool some, T value) 14 | { 15 | _value = value; 16 | Some = some; 17 | } 18 | 19 | public bool Some { get; } 20 | public bool None => !Some; 21 | 22 | public T Value 23 | { 24 | get 25 | { 26 | if (!Some) 27 | throw new InvalidOperationException("This option doesn't have a value, check HasValue before calling Value"); 28 | return _value; 29 | } 30 | } 31 | 32 | public static implicit operator Option(T value) 33 | { 34 | return ToSome(value); 35 | } 36 | } 37 | 38 | public static class OptionExtensions 39 | { 40 | public static Option Some(this T value) => Option.ToSome(value); 41 | public static Option None(this T value) => Option.ToNone; 42 | 43 | public static Option FirstOrNone(this IEnumerable source) 44 | { 45 | if (source == null) 46 | throw new ArgumentNullException("source"); 47 | 48 | foreach (TSource item in source) 49 | return item.Some(); 50 | return Option.ToNone; 51 | } 52 | public static Option FirstOrNone(this IEnumerable source, Func predicate) 53 | { 54 | if (source == null) 55 | throw new ArgumentNullException("source"); 56 | if (predicate == null) 57 | throw new ArgumentNullException("predicate"); 58 | 59 | foreach (TSource item in source) 60 | { 61 | if (predicate(item)) 62 | return item.Some(); 63 | } 64 | return Option.ToNone; 65 | } 66 | 67 | public static Option IfNone(this Option option, Func> ifNone) 68 | { 69 | return option != null && option.Some ? option : ifNone(); 70 | } 71 | 72 | public static T ValueOr(this Option option, Func ifNone) 73 | { 74 | return option != null && option.Some ? option.Value : ifNone(); 75 | } 76 | 77 | public static T ValueOr(this Option option, T ifNone) 78 | { 79 | return option != null && option.Some ? option.Value : ifNone; 80 | } 81 | 82 | public static Option IfSome(this Option option, Func> ifSome) 83 | { 84 | return option != null && option.Some 85 | ? ifSome(option.Value) 86 | : Option.ToNone; 87 | } 88 | 89 | public static T ValueOrNull(this Option option) where T : class 90 | { 91 | return option != null && option.Some ? option.Value : null; 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /source/Magic/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("Octopus Deploy")] 10 | [assembly: AssemblyProduct("ICanHasDotnetCore.Magic")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyFileVersion("0.0.0.0")] 13 | [assembly: AssemblyVersion("0.0.0.0")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("89caea64-c5fe-45f5-8b5d-53ae40a1fc92")] 22 | -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/CsProj/Project.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml.Serialization; 3 | 4 | namespace ICanHasDotnetCore.SourcePackageFileReaders.CsProj 5 | { 6 | [XmlRoot(ElementName = "Project")] 7 | public class Project 8 | { 9 | [XmlElementAttribute("ItemGroup")] 10 | public List ItemGroups { get; set; } 11 | } 12 | 13 | public class ItemGroup 14 | { 15 | [XmlElementAttribute("PackageReference")] 16 | public List Packages { get; set;} 17 | } 18 | 19 | public class PackageReference 20 | { 21 | [XmlAttribute(AttributeName = "Include")] 22 | public string Id { get; set; } 23 | } 24 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/CsProjReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Xml; 5 | using System.Xml.Serialization; 6 | using ICanHasDotnetCore.SourcePackageFileReaders.CsProj; 7 | using Newtonsoft.Json; 8 | 9 | namespace ICanHasDotnetCore.SourcePackageFileReaders 10 | { 11 | public class CsProjReader : ISourcePackagesFileReader 12 | { 13 | public IReadOnlyList ReadDependencies(byte[] contents) 14 | { 15 | using (var ms = new MemoryStream(contents)) 16 | using (var sr = new StreamReader(ms)) 17 | using(var xr = XmlReader.Create(sr)) 18 | { 19 | var xs = new XmlSerializer(typeof(Project)); 20 | if (!xs.CanDeserialize(xr)) return new string[0]; 21 | 22 | var project = (Project)new XmlSerializer(typeof(Project)).Deserialize(xr); 23 | return project.ItemGroups.Where(g => g.Packages.Any()).SelectMany(g => g.Packages).Select(p => p.Id) 24 | .ToArray(); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/ISourcePackagesFileReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ICanHasDotnetCore.SourcePackageFileReaders 4 | { 5 | public interface ISourcePackagesFileReader 6 | { 7 | IReadOnlyList ReadDependencies(byte[] contents); 8 | } 9 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/PackagesConfig/DependencyEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Xml.Serialization; 3 | 4 | namespace ICanHasDotnetCore.SourcePackageFileReaders.PackagesConfig 5 | { 6 | [XmlRoot(ElementName = "packages")] 7 | public class Packages : List 8 | { 9 | } 10 | 11 | [XmlType("package")] 12 | public class DependencyEntry 13 | { 14 | [XmlAttribute(AttributeName = "id")] 15 | public string Id { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/PackagesConfigReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Xml.Serialization; 5 | using ICanHasDotnetCore.SourcePackageFileReaders.PackagesConfig; 6 | 7 | namespace ICanHasDotnetCore.SourcePackageFileReaders 8 | { 9 | public class PackagesConfigReader : ISourcePackagesFileReader 10 | { 11 | public IReadOnlyList ReadDependencies(byte[] contents) 12 | { 13 | using (var ms = new MemoryStream(contents)) 14 | using (var sr = new StreamReader(ms)) 15 | { 16 | var packages = (Packages) new XmlSerializer(typeof(Packages)).Deserialize(sr); 17 | return packages.Select(p => p.Id).ToArray(); 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/PaketDependenciesReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace ICanHasDotnetCore.SourcePackageFileReaders 9 | { 10 | public class PaketDependenciesReader : ISourcePackagesFileReader 11 | { 12 | public IReadOnlyList ReadDependencies(byte[] contents) 13 | { 14 | using (var ms = new MemoryStream(contents)) 15 | using (var sr = new StreamReader(ms)) 16 | { 17 | var str = sr.ReadToEnd(); 18 | var matches = Regex.Matches(str, @"nuget\s+([^\s]+)"); 19 | return matches.Cast() 20 | .Select(m => m.Groups[1].Value) 21 | .ToArray(); 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/ProjectJsonReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace ICanHasDotnetCore.SourcePackageFileReaders 9 | { 10 | public class ProjectJsonReader : ISourcePackagesFileReader 11 | { 12 | public IReadOnlyList ReadDependencies(byte[] contents) 13 | { 14 | using (var ms = new MemoryStream(contents)) 15 | using (var sr = new StreamReader(ms)) 16 | { 17 | var model = JsonConvert.DeserializeObject(sr.ReadToEnd()); 18 | var value = model["dependencies"]?.Value(); 19 | if (value == null) 20 | return new string[0]; 21 | var dependencies = JsonConvert.DeserializeObject>(value.ToString()); 22 | return dependencies?.Keys.ToArray() ?? new string[0]; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/SourcePackageFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace ICanHasDotnetCore.SourcePackageFileReaders 4 | { 5 | public class SourcePackageFile 6 | { 7 | public SourcePackageFile(string name, string originalFileName, byte[] contents) 8 | { 9 | Name = name; 10 | OriginalFileName = originalFileName; 11 | Contents = contents; 12 | } 13 | 14 | public string Name { get; set; } 15 | public string OriginalFileName { get; } 16 | public string OriginalFileExtension => Path.GetExtension(OriginalFileName); 17 | public byte[] Contents { get; } 18 | } 19 | } -------------------------------------------------------------------------------- /source/Magic/SourcePackageFileReaders/SourcePackageFileReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using ICanHasDotnetCore.Investigator; 6 | using ICanHasDotnetCore.Plumbing; 7 | using ICanHasDotnetCore.Plumbing.Extensions; 8 | using Serilog; 9 | 10 | namespace ICanHasDotnetCore.SourcePackageFileReaders 11 | { 12 | public class SourcePackageFileReader 13 | { 14 | public const string PackagesConfig = "packages.config"; 15 | public const string ProjectJson = "project.json"; 16 | public const string Paket = "paket.dependencies"; 17 | public const string CsProj = ".csproj"; 18 | 19 | private static readonly Dictionary SourcePackagesFileReaders = new Dictionary(StringComparer.OrdinalIgnoreCase) 20 | { 21 | { PackagesConfig, new PackagesConfigReader() }, 22 | { ProjectJson, new ProjectJsonReader() }, 23 | { Paket, new PaketDependenciesReader() }, 24 | { CsProj, new CsProjReader() } 25 | }; 26 | 27 | public static IReadOnlyList SupportedFiles => SourcePackagesFileReaders.Keys.Except(SupportedExtensions).ToArray(); 28 | public static IReadOnlyList SupportedExtensions => new[] {CsProj}; 29 | 30 | public static Result> Read(SourcePackageFile file) 31 | { 32 | if (!SourcePackagesFileReaders.ContainsKey(file.OriginalFileName) && 33 | !SourcePackagesFileReaders.ContainsKey(file.OriginalFileExtension)) 34 | { 35 | Log.Warning("The filename {filename} was not recognised as a supported file format", 36 | file.OriginalFileName); 37 | return Result.Failed>( 38 | $"The filename {file.OriginalFileName} was not recognised as a supported file format. Supported types are {SupportedFiles.CommaSeperate()}."); 39 | } 40 | 41 | var sourcePackageFileReader = SourcePackagesFileReaders.FirstOrDefault(r => r.Key.Equals(file.OriginalFileName) || r.Key.Equals(file.OriginalFileExtension)).Value; 42 | 43 | return sourcePackageFileReader 44 | .ReadDependencies(file.Contents) 45 | .AsSuccess(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /source/Tests/Magic/EndToEndTest.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Text; 3 | using Assent; 4 | using ICanHasDotnetCore.Investigator; 5 | using ICanHasDotnetCore.NugetPackages; 6 | using ICanHasDotnetCore.Output; 7 | using ICanHasDotnetCore.SourcePackageFileReaders; 8 | using Xunit; 9 | 10 | namespace ICanHasDotnetCore.Tests.Magic 11 | { 12 | public class EndToEndTests 13 | { 14 | private const string Paket = @"source https://nuget.org/api/v2 15 | // NuGet packages 16 | nuget DotNetZip >= 1.9 17 | 18 | // HTTP resources 19 | http http://www.fssnip.net/1n decrypt.fs"; 20 | 21 | private const string PackagesConfig = @" 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | "; 32 | 33 | private const string ProjectJson = @"{ 34 | ""version"": ""1.0.0-*"", 35 | 36 | ""dependencies"": { 37 | ""Antlr"": ""3.0.11"", 38 | ""bootstrap"": ""3.0.11"" 39 | }, 40 | 41 | ""frameworks"": { 42 | ""net461"": { 43 | } 44 | } 45 | }"; 46 | 47 | [Fact(Skip = "Brittle, but useful when making changes, so keeping it")] 48 | public void EndToEndTest() 49 | { 50 | 51 | var result = PackageCompatabilityInvestigator.Create(new NoNugetResultCache()) 52 | .Go(new[] 53 | { 54 | new SourcePackageFile("PackagesConfig", SourcePackageFileReader.PackagesConfig, Encoding.UTF8.GetBytes(PackagesConfig)), 55 | new SourcePackageFile("ProjectJson", SourcePackageFileReader.ProjectJson, Encoding.UTF8.GetBytes(ProjectJson)), 56 | new SourcePackageFile("Paket", SourcePackageFileReader.Paket, Encoding.UTF8.GetBytes(Paket)) 57 | }) 58 | .Result; 59 | 60 | this.Assent(TreeOutputFormatter.Format(result)); 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /source/Tests/Magic/EndToEndTests.EndToEndTest.approved.txt: -------------------------------------------------------------------------------- 1 | PackagesConfig [Your Project] 2 | Autofac [Supported] 3 | NLog [Supported (Pre-Release)] 4 | Microsoft.Extensions.PlatformAbstractions [Supported] 5 | Microsoft.Web.Xdt [Unsupported] 6 | Nancy [Supported (Pre-Release)] 7 | Microsoft.CSharp [Supported] 8 | Microsoft.Extensions.PlatformAbstractions [Supported] 9 | Microsoft.Extensions.DependencyModel [Supported] 10 | Microsoft.DotNet.PlatformAbstractions [Supported] 11 | Newtonsoft.Json [Supported] 12 | Microsoft.CSharp [Supported] 13 | Nancy.Bootstrappers.Autofac [Supported (Pre-Release)] 14 | Autofac [Supported] 15 | Nancy [Supported (Pre-Release)] 16 | Microsoft.CSharp [Supported] 17 | Microsoft.Extensions.PlatformAbstractions [Supported] 18 | Microsoft.Extensions.DependencyModel [Supported] 19 | Microsoft.DotNet.PlatformAbstractions [Supported] 20 | Newtonsoft.Json [Supported] 21 | Microsoft.CSharp [Supported] 22 | SharpZipLib [Unsupported] 23 | Sprache [Supported] 24 | SSH.NET [Supported] 25 | Microsoft.CSharp [Supported] 26 | SshNet.Security.Cryptography [Supported] 27 | ProjectJson [Your Project] 28 | Antlr [Unsupported] Often this package is not referenced directly, but as a dependency of WebGrease - GitHub Issue tracking .NET Core support: https://github.com/antlr/antlrcs/issues/42 29 | bootstrap [Not a .NET Library] 30 | jQuery [Not a .NET Library] 31 | Paket [Your Project] 32 | DotNetZip [Unsupported] 33 | -------------------------------------------------------------------------------- /source/Tests/Magic/NugetPackages/KnownReplacementsRepositoryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ICanHasDotnetCore.NugetPackages; 3 | using Xunit; 4 | 5 | namespace ICanHasDotnetCore.Tests.Magic.NugetPackages 6 | { 7 | public class KnownReplacementsRepositoryTests 8 | { 9 | [Fact] 10 | public void EntriesCanBeReadAndAtLeastOneEntryExists() 11 | { 12 | KnownReplacementsRepository.All.Should().NotBeEmpty(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /source/Tests/Magic/NugetPackages/MoreInformationRepositoryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ICanHasDotnetCore.NugetPackages; 3 | using Xunit; 4 | 5 | namespace ICanHasDotnetCore.Tests.Magic.NugetPackages 6 | { 7 | public class MoreInformationRepositoryTests 8 | { 9 | [Fact] 10 | public void EntriesCanBeReadAndAtLeastOneEntryExists() 11 | { 12 | MoreInformationRepository.All.Should().NotBeEmpty(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /source/Tests/Magic/NugetPackages/PclProfileCompatabilityCheckerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentAssertions; 3 | using ICanHasDotnetCore.NugetPackages; 4 | using Xunit; 5 | 6 | namespace ICanHasDotnetCore.Tests.Magic.NugetPackages 7 | { 8 | public class PclProfileCompatabilityCheckerTests 9 | { 10 | 11 | public static IEnumerable TestCases() 12 | { 13 | return new[] 14 | { 15 | CreateTestCase("Profile2", "net40+win8+sl4+wp7", false), 16 | CreateTestCase("Profile3", "net40+sl4", false), 17 | CreateTestCase("Profile4", "net45+sl4+win8+wp7", false), 18 | CreateTestCase("Profile5", "net40+win8", false), 19 | CreateTestCase("Profile6", "net403+win8", false), 20 | CreateTestCase("Profile7", "net45+win8", true), 21 | CreateTestCase("Profile14", "net40+sl5", false), 22 | CreateTestCase("Profile18", "net403+sl4", false), 23 | CreateTestCase("Profile19", "net403+sl5", false), 24 | CreateTestCase("Profile23", "net45+sl4", false), 25 | CreateTestCase("Profile24", "net45+sl5", false), 26 | CreateTestCase("Profile31", "win81+wp81", true), 27 | CreateTestCase("Profile32", "win81+wpa81", true), 28 | CreateTestCase("Profile36", "net40+sl4+win8+wp8", false), 29 | CreateTestCase("Profile37", "net40+sl5+win8", false), 30 | CreateTestCase("Profile41", "net403+sl4+win8", false), 31 | CreateTestCase("Profile42", "net403+sl5+win8", false), 32 | CreateTestCase("Profile44", "net451+win81", true), 33 | CreateTestCase("Profile46", "net45+sl4+win8", false), 34 | CreateTestCase("Profile47", "net45+sl5+win8", false), 35 | CreateTestCase("Profile49", "net45+wp8", true), 36 | CreateTestCase("Profile78", "net45+win8+wp8", true), 37 | CreateTestCase("Profile84", "wp81+wpa81", true), 38 | CreateTestCase("Profile88", "net40+sl4+win8+wp75", false), 39 | CreateTestCase("Profile92", "net40+win8+wpa81", false), 40 | CreateTestCase("Profile95", "net403+sl4+win8+wp7", false), 41 | CreateTestCase("Profile96", "net403+sl4+win8+wp75", false), 42 | CreateTestCase("Profile102", "net403+win8+wpa81", false), 43 | CreateTestCase("Profile104", "net45+sl4+win8+wp75", false), 44 | CreateTestCase("Profile111", "net45+win8+wpa81", true), 45 | CreateTestCase("Profile136", "net40+sl5+win8+wp8", false), 46 | CreateTestCase("Profile143", "net403+sl4+win8+wp8", false), 47 | CreateTestCase("Profile147", "net403+sl5+win8+wp8", false), 48 | CreateTestCase("Profile151", "net451+win81+wpa81", true), 49 | CreateTestCase("Profile154", "net45+sl4+win8+wp8", false), 50 | CreateTestCase("Profile157", "win81+wp81+wpa81", true), 51 | CreateTestCase("Profile158", "net45+sl5+win8+wp8", false), 52 | CreateTestCase("Profile225", "net40+sl5+win8+wpa81", false), 53 | CreateTestCase("Profile240", "net403+sl5+win8+wpa81", false), 54 | CreateTestCase("Profile255", "net45+sl5+win8+wpa81", false), 55 | CreateTestCase("Profile259", "net45+win8+wpa81+wp8", true), 56 | CreateTestCase("Profile328", "net40+sl5+win8+wpa81+wp8", false), 57 | CreateTestCase("Profile336", "net403+sl5+win8+wpa81+wp8", false), 58 | CreateTestCase("Profile344", "net45+sl5+win8+wpa81+wp8", false), 59 | CreateTestCase("Profile259 - win alias", "net45+win+wpa81+wp8", true), 60 | CreateTestCase("Profile259 - wpa alias", "net45+win8+wpa+wp8", true), 61 | CreateTestCase("Profile259 - different order", "net45+wp8+win8+wpa81", true), 62 | }; 63 | } 64 | 65 | private static object[] CreateTestCase(string name, string profile, bool expectedResult) 66 | => new object[] {name, profile, expectedResult}; 67 | 68 | [Theory] 69 | [MemberData(nameof(TestCases))] 70 | public void PclCheckReturnsTheRightValue(string name, string profile, bool expected) 71 | { 72 | PclProfileCompatabilityChecker.Check(profile).Should().Be(expected); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /source/Tests/Magic/SourcePackageFileReaders/CsProjReaderTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ICanHasDotnetCore.SourcePackageFileReaders; 3 | 4 | namespace ICanHasDotnetCore.Tests.Magic.SourcePackageFileReaders 5 | { 6 | public class CsProjReaderTests : ReaderTestsBase 7 | { 8 | protected override string Contents => @" 9 | 10 | 11 | net461 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | "; 30 | 31 | protected override void Execute(byte[] encodedFile) 32 | { 33 | var result = new CsProjReader().ReadDependencies(encodedFile); 34 | result.Count.Should().Be(2); 35 | result.Should().BeEquivalentTo("Antlr", "bootstrap"); 36 | } 37 | 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /source/Tests/Magic/SourcePackageFileReaders/PackagesConfigReaderTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ICanHasDotnetCore.SourcePackageFileReaders; 3 | 4 | namespace ICanHasDotnetCore.Tests.Magic.SourcePackageFileReaders 5 | { 6 | public class PackagesConfigReaderTests : ReaderTestsBase 7 | { 8 | protected override string Contents => @" 9 | 10 | 11 | 12 | "; 13 | 14 | 15 | 16 | protected override void Execute(byte[] encodedFile) 17 | { 18 | var result = new PackagesConfigReader().ReadDependencies(encodedFile); 19 | result.Count.Should().Be(2); 20 | result.Should().BeEquivalentTo("Antlr", "bootstrap"); 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Tests/Magic/SourcePackageFileReaders/PaketDependenciesReaderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using FluentAssertions; 4 | using ICanHasDotnetCore.SourcePackageFileReaders; 5 | using Xunit; 6 | 7 | namespace ICanHasDotnetCore.Tests.Magic.SourcePackageFileReaders 8 | { 9 | public class PaketDependenciesReaderTests : ReaderTestsBase 10 | { 11 | protected override string Contents => @"source https://nuget.org/api/v2 12 | 13 | // NuGet packages 14 | nuget NUnit ~> 2.6.3 15 | nuget 16 | FAKE 17 | ~> 18 | 3.4 19 | nuget DotNetZip >= 1.9 20 | nuget SourceLink.Fake 21 | 22 | // Files from GitHub repositories 23 | github forki/FsUnit FsUnit.fs 24 | 25 | // Gist files 26 | gist Thorium/1972349 timestamp.fs 27 | 28 | // HTTP resources 29 | http http://www.fssnip.net/1n decrypt.fs"; 30 | 31 | 32 | protected override void Execute(byte[] encodedFile) 33 | { 34 | var result = new PaketDependenciesReader().ReadDependencies(encodedFile); 35 | result.Should().BeEquivalentTo("NUnit", "FAKE", "DotNetZip", "SourceLink.Fake"); 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /source/Tests/Magic/SourcePackageFileReaders/ProjectJsonReaderTest.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using FluentAssertions; 4 | using ICanHasDotnetCore.SourcePackageFileReaders; 5 | using Xunit; 6 | 7 | namespace ICanHasDotnetCore.Tests.Magic.SourcePackageFileReaders 8 | { 9 | public class ProjectJsonReaderTest : ReaderTestsBase 10 | { 11 | protected override string Contents => @"{ 12 | ""version"": ""1.0.0-*"", 13 | 14 | ""dependencies"": { 15 | ""Antlr"": ""3.0.11"", 16 | ""bootstrap"": ""3.0.11"" 17 | }, 18 | 19 | ""frameworks"": { 20 | ""net461"": { 21 | } 22 | } 23 | }"; 24 | 25 | 26 | protected override void Execute(byte[] encodedFile) 27 | { 28 | var result = new ProjectJsonReader().ReadDependencies(encodedFile); 29 | result.Count.Should().Be(2); 30 | result.Should().BeEquivalentTo("Antlr", "bootstrap"); 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /source/Tests/Magic/SourcePackageFileReaders/ReaderTestsBase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using Xunit; 4 | 5 | namespace ICanHasDotnetCore.Tests.Magic.SourcePackageFileReaders 6 | { 7 | public abstract class ReaderTestsBase 8 | { 9 | protected abstract string Contents { get; } 10 | 11 | [Fact] 12 | public void Utf8FileCanBeRead() 13 | { 14 | var encodedFile = Encoding.UTF8.GetPreamble().Concat(Encoding.UTF8.GetBytes(Contents)).ToArray(); 15 | Execute(encodedFile); 16 | } 17 | 18 | [Fact] 19 | public void Utf16FileCanBeRead() 20 | { 21 | var encodedFile = Encoding.Unicode.GetPreamble().Concat(Encoding.Unicode.GetBytes(Contents)).ToArray(); 22 | Execute(encodedFile); 23 | } 24 | 25 | [Fact] 26 | public void Utf32FileCanBeRead() 27 | { 28 | var encodedFile = Encoding.UTF32.GetPreamble().Concat(Encoding.UTF32.GetBytes(Contents)).ToArray(); 29 | Execute(encodedFile); 30 | } 31 | 32 | [Fact] 33 | public void FileCanBeRead() 34 | { 35 | var encodedFile = Encoding.UTF8.GetBytes(Contents); 36 | Execute(encodedFile); 37 | } 38 | 39 | protected abstract void Execute(byte[] encodedFile); 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /source/Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("Octopus Deploy")] 10 | [assembly: AssemblyProduct("ICanHasDotnetCore.Tests")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyFileVersion("0.0.0.0")] 13 | [assembly: AssemblyVersion("0.0.0.0")] 14 | // Setting ComVisible to false makes the types in this assembly not visible 15 | // to COM components. If you need to access a type in this assembly from 16 | // COM, set the ComVisible attribute to true on that type. 17 | [assembly: ComVisible(false)] 18 | 19 | // The following GUID is for the ID of the typelib if this project is exposed to COM 20 | [assembly: Guid("e7a09e85-977a-4852-8f5b-9707525d7104")] 21 | -------------------------------------------------------------------------------- /source/Tests/Setup.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | using Serilog; 3 | 4 | namespace ICanHasDotnetCore.Tests 5 | { 6 | public class Setup 7 | { 8 | public Setup() 9 | { 10 | Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole().CreateLogger(); 11 | } 12 | } 13 | 14 | 15 | [CollectionDefinition("")] 16 | public class GlobalCollection : ICollectionFixture 17 | { 18 | 19 | } 20 | } -------------------------------------------------------------------------------- /source/Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net461 5 | Tests 6 | Tests 7 | true 8 | false 9 | false 10 | false 11 | false 12 | false 13 | true 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /source/Tests/Web/Features/Result/GitHub/GitHubScannerTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using System.Linq; 4 | using ICanHasDotnetCore.Tests.Web.Helpers; 5 | using ICanHasDotnetCore.Web.Features.result.GitHub; 6 | using Microsoft.Extensions.Configuration; 7 | using Xunit; 8 | using Serilog; 9 | 10 | namespace ICanHasDotnetCore.Tests.Web.Features.Result.GitHub 11 | { 12 | public class GitHubScannerTests 13 | { 14 | 15 | [Fact()] 16 | public async Task ICanHasDotnetRepository() 17 | { 18 | var result = await CreateScanner().Scan("/OctopusDeploy/ICanHasDotnetCore\\"); 19 | result.WasSuccessful.Should().BeTrue(result.ErrorString); 20 | var names = result.Value.Select(p => p.Name).ToArray(); 21 | names.ShouldAllBeEquivalentTo(new[] { "source/Magic", "source/Database", "source/Console", "source/Tests", "source/Web" }); 22 | } 23 | 24 | 25 | [Fact] 26 | public async Task InvalidId_InvalidName() 27 | { 28 | var result = await CreateScanner().Scan("OctopusDeploy/Foo/Bar"); 29 | result.WasSuccessful.Should().BeFalse(); 30 | result.ErrorString.Should().Be("OctopusDeploy/Foo/Bar is not recognised as a GitHub repository name"); 31 | } 32 | 33 | [Fact] 34 | public async Task RepoDoesNotExist() 35 | { 36 | var result = await CreateScanner().Scan("OctopusDeploy/DoesNotExist"); 37 | result.WasSuccessful.Should().BeFalse(); 38 | result.ErrorString.Should().Be("OctopusDeploy/DoesNotExist does not exist or is not publically accessible"); 39 | } 40 | 41 | 42 | 43 | private GitHubScanner CreateScanner() 44 | { 45 | return new GitHubScanner( 46 | new FakeConfigurationRoot() 47 | { 48 | {"GitHubToken", null } 49 | } 50 | ); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /source/Tests/Web/Features/Result/GitHub/RepositoryIdTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ICanHasDotnetCore.Web.Features.result.GitHub; 3 | using Xunit; 4 | 5 | namespace ICanHasDotnetCore.Tests.Web.Features.Result.GitHub 6 | { 7 | public class RepositoryIdTests 8 | { 9 | [Theory] 10 | [InlineData("OctopusDeploy/Foo")] 11 | [InlineData("/OctopusDeploy/Foo/")] 12 | [InlineData("/OctopusDeploy/Foo")] 13 | [InlineData("/Octopus_Deploy/Fo_o/")] 14 | [InlineData("/Octopus-Deploy/Fo-o/")] 15 | [InlineData("/Octopus.Deploy/Fo.o/")] 16 | [InlineData("Oct_opus.Depl-oy/F_o.o-")] 17 | public void ValidNames(string name) 18 | { 19 | GitHubScanner.RepositoryId.Parse(name) 20 | .Some.Should().BeTrue(); 21 | } 22 | 23 | [Theory] 24 | [InlineData("OctopusDeploy/Foo/Bar")] 25 | [InlineData("Octopus$Deploy/Foo")] 26 | [InlineData("Octopus\\Deploy/Foo")] 27 | [InlineData("Octopus=Deploy/Foo")] 28 | [InlineData("OctopusDeploy/F\\oo")] 29 | [InlineData("Octo:pusDeploy/Foo")] 30 | [InlineData("OctopusDeploy/F:oo")] 31 | public void InvalidNames(string name) 32 | { 33 | GitHubScanner.RepositoryId.Parse(name) 34 | .None.Should().BeTrue(); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /source/Tests/Web/Features/Statistics/RequerySupportTypeForStatisticsPackagesTaskTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using ICanHasDotnetCore.Investigator; 4 | using ICanHasDotnetCore.NugetPackages; 5 | using ICanHasDotnetCore.Web.Features.Statistics; 6 | using Xunit; 7 | using FluentAssertions; 8 | using System.Linq; 9 | 10 | namespace ICanHasDotnetCore.Tests.Web.Features.Statistics 11 | { 12 | public class RequerySupportTypeForStatisticsPackagesTaskTests 13 | { 14 | 15 | [Fact] 16 | public void TestRequery() 17 | { 18 | var repo = new TestRepository(); 19 | var task = new RequerySupportTypeForStatisticsPackagesTask(repo); 20 | task.Run().Wait(); 21 | repo.Updates.Keys.ShouldAllBeEquivalentTo(TestRepository.PackageNames); 22 | repo.Updates["JQuery"].Should().Be(SupportType.NoDotNetLibraries, "JQuery is a no dotnet result"); 23 | repo.Updates["Xunit.Core"].Should().Be(SupportType.Supported, "Forwarding packages are Supported"); 24 | } 25 | 26 | private class TestRepository : IStatisticsRepository 27 | { 28 | public readonly Dictionary Updates = new Dictionary(); 29 | 30 | public static readonly string[] PackageNames = { "Autofac", "JQuery", "Xunit.Core" }; 31 | 32 | public Task AddStatisticsForResult(InvestigationResult result) 33 | { 34 | throw new System.NotImplementedException(); 35 | } 36 | 37 | public IReadOnlyList GetAllPackageStatistics() 38 | { 39 | return PackageNames 40 | .Select(n => new PackageStatistic() { Name = n }) 41 | .ToArray(); 42 | } 43 | 44 | public void UpdateSupportTypeFor(PackageStatistic stat, SupportType supportType) 45 | { 46 | Updates[stat.Name] = supportType; 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /source/Tests/Web/Helpers/DataUriConverterTest.CanConvert.approved.txt: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /source/Tests/Web/Helpers/DataUriConverterTest.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Text; 3 | using Assent; 4 | using ICanHasDotnetCore.Web.Helpers; 5 | using Xunit; 6 | 7 | namespace ICanHasDotnetCore.Tests.Web.Helpers 8 | { 9 | public class DataUriConverterTest 10 | { 11 | [Fact] 12 | [MethodImpl(MethodImplOptions.NoInlining)] 13 | public void CanConvert() 14 | { 15 | var result = DataUriConverter.ConvertFrom( 16 | "data:application/xml;base64,77u/PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjxwYWNrYWdlcz4NCiAgPHBhY2thZ2UgaWQ9IlNlcmlsb2ciIHZlcnNpb249IjEuNS4xNCIgdGFyZ2V0RnJhbWV3b3JrPSJuZXQ0NjEiIC8+DQo8L3BhY2thZ2VzPg=="); 17 | this.Assent(Encoding.UTF8.GetString(result)); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /source/Tests/Web/Helpers/FakeConfigurationRoot.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Primitives; 4 | 5 | namespace ICanHasDotnetCore.Tests.Web.Helpers 6 | { 7 | public class FakeConfigurationRoot : Dictionary, IConfigurationRoot 8 | { 9 | 10 | public IConfigurationSection GetSection(string key) 11 | { 12 | throw new System.NotImplementedException(); 13 | } 14 | 15 | public IEnumerable GetChildren() 16 | { 17 | throw new System.NotImplementedException(); 18 | } 19 | 20 | public IChangeToken GetReloadToken() 21 | { 22 | throw new System.NotImplementedException(); 23 | } 24 | 25 | public void Reload() 26 | { 27 | throw new System.NotImplementedException(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /source/Web/.gitignore: -------------------------------------------------------------------------------- 1 | typings/ 2 | wwwroot/ 3 | node_modules/ 4 | Features/**/*.js 5 | -------------------------------------------------------------------------------- /source/Web/Content/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctopusDeploy/ICanHasDotnetCore/95fab8441da80a766f1eb234dc64b56bf8b95e33/source/Web/Content/images/favicon.png -------------------------------------------------------------------------------- /source/Web/Content/images/github-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctopusDeploy/ICanHasDotnetCore/95fab8441da80a766f1eb234dc64b56bf8b95e33/source/Web/Content/images/github-circle.png -------------------------------------------------------------------------------- /source/Web/Content/images/graph.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctopusDeploy/ICanHasDotnetCore/95fab8441da80a766f1eb234dc64b56bf8b95e33/source/Web/Content/images/graph.PNG -------------------------------------------------------------------------------- /source/Web/Content/images/octopus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctopusDeploy/ICanHasDotnetCore/95fab8441da80a766f1eb234dc64b56bf8b95e33/source/Web/Content/images/octopus.png -------------------------------------------------------------------------------- /source/Web/Content/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OctopusDeploy/ICanHasDotnetCore/95fab8441da80a766f1eb234dc64b56bf8b95e33/source/Web/Content/images/twitter.png -------------------------------------------------------------------------------- /source/Web/Features/Analytics/AnalyticsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace ICanHasDotnetCore.Web.Features.Analytics 5 | { 6 | public class AnalyticsController : Controller 7 | { 8 | private readonly IConfigurationRoot _configurationRoot; 9 | 10 | public AnalyticsController(IConfigurationRoot configurationRoot) 11 | { 12 | _configurationRoot = configurationRoot; 13 | } 14 | [HttpGet("api/Analytics")] 15 | public string Get() 16 | { 17 | return _configurationRoot["GoogleAnalytics"]; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /source/Web/Features/Console/Console.html: -------------------------------------------------------------------------------- 1 |
2 |

Command Line Tool

3 |

4 | As an alternative to this website, you can download this console application that will scan your source and output 5 | textual and graph data. 6 |

7 | 10 |

Usage

11 |

12 | Specify the output directory and one or more directories to scan. The application will recursively scan the speciifed 13 | directories looking for packages.config, project.json and paket.dependencies files. 14 |

15 |

16 | ICanHasDotnetCore.exe <output_directory> <dir_to_scan_1> [dir_to_scan_2] ... [dir_to_scan_n] 17 |

18 |

For example, to scan your whole source tree

19 |

20 | ICanHasDotnetCore.exe c:\temp\output c:\source\MyApplicaion 21 |

22 |

or only scan specific projects: (Current directory being the source root)

23 |

24 | ICanHasDotnetCore.exe c:\temp\output MyApplication.Data MyApplication.Web Tests\Tests Tests\Tests.UI 25 |

26 |

Output

27 |

28 | Each found packages file is tagged with the name of the directory it was found in as the 'Project' name. 29 | The application will output: 30 |

    31 |
  • Flat.txt - A flat list of the dependencies
  • 32 |
  • Tree.txt - A tree of the dependencies
  • 33 |
  • All.gv - A GraphVis file of all dependencies of all projects
  • 34 |
  • 1Level.gv - A GraphVis file of direct dependencies of all projects
  • 35 |
  • <Project>.gv - A GraphVis file of dependencies for <Project>
  • 36 |
  • All.cql - A Cypher file of all the dependencies of all the projects
  • 37 |
38 |

39 | 40 |

Result Statuses and Colours

41 |

42 | The textual output will use a textual status (eg [Supported]), while the GraphVis data uses colour. 43 | The possible statuses are: 44 |

45 |
46 |
47 | 48 |
Your input projects.config files
49 |
50 |
51 | 52 |
The latest release version of the package supports .NET Standard
53 |
54 |
55 | 56 |
The latest release version of the package does not support .NET Standard, but the latest pre-release version does
57 |
58 |
59 | 60 |
We have identified that the packages as no longer being required on .NET Standard, or there is a a replacement package with a different name
61 |
62 |
63 | 64 |
The latest release and pre-release versions of the package do not support .NET Standard
65 |
66 |
67 | 68 |
The package does not contain .NET assemblies and is most likely HTML/JS files, a development tool or aggregates other packages
69 |
70 |
71 | 72 |
This package could not be found on nuget.org, it is most likely from an internal nuget feed
73 |
74 |
75 | 76 |
Something went wrong when inspecting the package
77 |
78 |
79 |

GraphVis

80 |

81 | The .gv file is a GraphVis dot file. It can be used with the GraphVis application, online at WebGraphViz 82 | or with many other web and non-web based network graphing applications. 83 |

84 |
85 | 86 | -------------------------------------------------------------------------------- /source/Web/Features/Console/Console.less: -------------------------------------------------------------------------------- 1 | .console { 2 | a.md-button.md-raised.download { 3 | color: white; 4 | background-color: #4CAF50; 5 | padding: 5px 30px; 6 | } 7 | 8 | a.md-button.md-raised.download:hover { 9 | background-color: #2E7D32; 10 | } 11 | 12 | .statuses { 13 | div 14 | { 15 | padding: 3px 7px; 16 | } 17 | package-result-box { 18 | width: 250px; 19 | margin-right: 10px; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /source/Web/Features/Console/Console.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Console { 2 | 3 | export const state = "layout.console"; 4 | 5 | class ViewModel { 6 | } 7 | 8 | addAngularState(state, "/Console", ViewModel, "Console/Console.html", 9 | { 10 | title: "Command Line Tool", 11 | description: "Scan a local source tree and generate a report and graph of nuget dependencies and their .NET Standard support" 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /source/Web/Features/FeaturesAutofacModule.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | using ICanHasDotnetCore.NugetPackages; 3 | using ICanHasDotnetCore.Web.Features.result.Cache; 4 | using ICanHasDotnetCore.Web.Features.result.GitHub; 5 | using ICanHasDotnetCore.Web.Features.Statistics; 6 | 7 | namespace ICanHasDotnetCore.Web.Features 8 | { 9 | public class FeaturesAutofacModule : Module 10 | { 11 | protected override void Load(ContainerBuilder builder) 12 | { 13 | builder.RegisterType().As().SingleInstance(); 14 | builder.RegisterType().AsSelf().SingleInstance(); 15 | builder.RegisterType().As().SingleInstance(); 16 | builder.RegisterType().As().SingleInstance(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /source/Web/Features/Knowledge/Knowledge.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Knowledge

4 |

5 | Below is some further information on the current plans for .NET Standard support for some popular packages, as well as 6 | a list of packages that have replacements available. Find out how to contribute. 7 |

8 | 9 | 10 |

11 | {{info.id}}*
12 | {{info.message}}
13 | {{ info.linkText || 'More Information'}} 14 |

15 |
16 | 17 |

18 | {{info.id}}*
19 | {{info.message}}
20 | {{ info.linkText || 'Details'}} 21 |

22 |
23 |
24 |
25 |
26 | 27 | -------------------------------------------------------------------------------- /source/Web/Features/Knowledge/Knowledge.less: -------------------------------------------------------------------------------- 1 | @import (reference) "../site"; 2 | 3 | .knowledge { 4 | .more-information 5 | { 6 | a { 7 | font-size: 14px; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/Web/Features/Knowledge/Knowledge.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Knowledge { 2 | 3 | export var state = "layout.knowledge"; 4 | 5 | class ViewModel { 6 | replacements: any[]; 7 | moreInformation: any[]; 8 | 9 | constructor($http: ng.IHttpService) { 10 | $http.get("/api/Knowledge/KnownReplacements") 11 | .then(response => this.replacements = response.data); 12 | $http.get("/api/Knowledge/MoreInformation") 13 | .then(response => this.moreInformation = response.data); 14 | } 15 | 16 | } 17 | 18 | redirect("layout.knownReplacements", "/Replacements", state); 19 | addAngularState(state, "/Knowledge", ViewModel, "Knowledge/Knowledge.html", 20 | { 21 | title: "Knowledge", 22 | description: "List of direct substitues and information on .NET Standard support plans for popular packages" 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /source/Web/Features/Knowledge/KnowledgeController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ICanHasDotnetCore.NugetPackages; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace ICanHasDotnetCore.Web.Features.Knowledge 6 | { 7 | public class KnowledgeController : Controller 8 | { 9 | 10 | [HttpGet("/api/Knowledge/KnownReplacements")] 11 | public IReadOnlyList GetKnownReplacements() 12 | { 13 | return KnownReplacementsRepository.All; 14 | } 15 | 16 | [HttpGet("/api/Knowledge/MoreInformation")] 17 | public IReadOnlyList GetMoreInformation() 18 | { 19 | return MoreInformationRepository.All; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /source/Web/Features/Meta/MetaController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace ICanHasDotnetCore.Web.Features.Meta 6 | { 7 | public class MetaController : Controller 8 | { 9 | 10 | private string BaseUrl => Request.Scheme + Uri.SchemeDelimiter + Request.Host; 11 | 12 | [HttpGet("robots.txt")] 13 | public ContentResult RobotsTxt() 14 | { 15 | return Content($@"User-agent:* 16 | Sitemap: {BaseUrl}/sitemap.xml", "text/plain", Encoding.UTF8); 17 | } 18 | 19 | [HttpGet("sitemap.xml")] 20 | public ContentResult SiteMap() 21 | { 22 | var sitemap = $@" 23 | 24 | {GetSiteMapPortion("", "0.9")} 25 | {GetSiteMapPortion("Result/Demo", "0.6")} 26 | {GetSiteMapPortion("Console")} 27 | {GetSiteMapPortion("Faq", "0.3")} 28 | {GetSiteMapPortion("Replacements", "0.3")} 29 | {GetSiteMapPortion("Stats", changeFreq: "hourly")} 30 | "; 31 | 32 | return Content(sitemap, "application/xml", Encoding.UTF8); 33 | } 34 | 35 | private string GetSiteMapPortion(string path, string priority = "0.5", string changeFreq = "daily") 36 | { 37 | return $@" 38 | {BaseUrl}/{path} 39 | {changeFreq} 40 | {priority} 41 | "; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /source/Web/Features/Statistics/PackageStatistic.cs: -------------------------------------------------------------------------------- 1 | using ICanHasDotnetCore.NugetPackages; 2 | 3 | namespace ICanHasDotnetCore.Web.Features.Statistics 4 | { 5 | public class PackageStatistic 6 | { 7 | public string Name { get; set; } 8 | public int Count { get; set; } 9 | public SupportType LatestSupportType { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /source/Web/Features/Statistics/PackageStatisticResponse.cs: -------------------------------------------------------------------------------- 1 | using ICanHasDotnetCore.NugetPackages; 2 | 3 | namespace ICanHasDotnetCore.Web.Features.Statistics 4 | { 5 | public class PackageStatisticResponse 6 | { 7 | public PackageStatistic Statistic { get; set; } 8 | public MoreInformation MoreInformation { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /source/Web/Features/Statistics/RequerySupportTypeForStatisticsPackagesTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Autofac; 6 | using ICanHasDotnetCore.Investigator; 7 | using ICanHasDotnetCore.NugetPackages; 8 | using ICanHasDotnetCore.SourcePackageFileReaders; 9 | using Microsoft.Extensions.Configuration; 10 | using Serilog; 11 | using System.Linq; 12 | using ICanHasDotnetCore.Plumbing.Extensions; 13 | 14 | namespace ICanHasDotnetCore.Web.Features.Statistics 15 | { 16 | public class RequerySupportTypeForStatisticsPackagesTask : IStartable, IDisposable 17 | { 18 | private readonly IStatisticsRepository _statisticsRepository; 19 | private Timer _timer; 20 | 21 | public RequerySupportTypeForStatisticsPackagesTask(IStatisticsRepository statisticsRepository) 22 | { 23 | _statisticsRepository = statisticsRepository; 24 | } 25 | 26 | public void Start() 27 | { 28 | Log.Information("Starting Requery Statistics Package Support Task timer"); 29 | _timer = new Timer(_ => Run().Wait(), null, TimeSpan.FromMinutes(10), TimeSpan.FromDays(1)); 30 | } 31 | 32 | 33 | public void Dispose() 34 | { 35 | _timer?.Dispose(); 36 | } 37 | 38 | public async Task Run() 39 | { 40 | var sw = Stopwatch.StartNew(); 41 | try 42 | { 43 | Log.Information("Requery Statistics Package Support Task Started"); 44 | var stats = _statisticsRepository.GetAllPackageStatistics(); 45 | var packageNames = stats.Select(s => s.Name).ToArray(); 46 | 47 | var result = await PackageCompatabilityInvestigator.Create(new NoNugetResultCache()) 48 | .Process("Requery", packageNames); 49 | 50 | foreach (var stat in stats) 51 | { 52 | var packageResult = result.Dependencies.FirstOrDefault(f => f.PackageName.EqualsOrdinalIgnoreCase(stat.Name)); 53 | UpdatePackage(packageResult, stat); 54 | } 55 | } 56 | catch (Exception ex) 57 | { 58 | Log.Error(ex, "Requery Statistics Package Support Task failed"); 59 | } 60 | Log.Information("Requery Statistics Package Support Task Finished in {time}", sw.Elapsed); 61 | } 62 | 63 | private void UpdatePackage(PackageResult packageResult, PackageStatistic stat) 64 | { 65 | if (packageResult == null) 66 | { 67 | Log.Information("No result returned for {package}", stat.Name); 68 | } 69 | else if (!packageResult.WasSuccessful) 70 | { 71 | Log.Information("Error occured for retrieving {package}: {error}", stat.Name, packageResult.Error); 72 | } 73 | else if (stat.LatestSupportType != packageResult.SupportType && packageResult.SupportType != SupportType.Error) 74 | { 75 | Log.Information("Updating support type for {package} from {from} to {to}", stat.Name, stat.LatestSupportType, 76 | packageResult.SupportType); 77 | _statisticsRepository.UpdateSupportTypeFor(stat, packageResult.SupportType); 78 | } 79 | } 80 | 81 | } 82 | } -------------------------------------------------------------------------------- /source/Web/Features/Statistics/Statistics.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Statistics

4 |

5 | We keep statistics on how many times each package shows up as a dependency. Only public packages found 6 | on nuget.org are included. The statistics are grouped by detected .NET Standard support result the 7 | last time the package was queried. 8 |

9 |

10 | See exactly how we collect statistics. 11 |

12 |
13 | 14 |
15 |
16 | 17 |
18 |
19 |
{{item.statistic.count | number}}
20 | {{item.statistic.name}} 21 | search 22 | 23 |
24 | {{item.moreInformation.message}} 25 | [{{ info.linkText || 'More Information'}}] 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | -------------------------------------------------------------------------------- /source/Web/Features/Statistics/Statistics.less: -------------------------------------------------------------------------------- 1 | @import (reference) "../site"; 2 | 3 | .statistics { 4 | 5 | .count-label { 6 | .label; 7 | margin-right: 5px; 8 | width: 40px; 9 | } 10 | 11 | .group { 12 | font-size: 13px; 13 | margin: 25px; 14 | max-width:400px; 15 | } 16 | 17 | .statistic { 18 | margin: 15px 20px; 19 | } 20 | 21 | package-result-box { 22 | width: 250px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/Web/Features/Statistics/Statistics.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Statistics { 2 | import SupportType = ICanHasDotnetCore.Result.SupportType; 3 | export const state = "layout.statistics"; 4 | 5 | interface IResponse { 6 | statistic: IPackageStatistic; 7 | moreInformation:any; 8 | } 9 | 10 | interface IPackageStatistic { 11 | name: string; 12 | count: number; 13 | latestSupportType: SupportType; 14 | } 15 | 16 | class ViewModel { 17 | statistics: IResponse[]; 18 | typeGroups = [ SupportType.Unsupported, SupportType.PreRelease, SupportType.Supported ]; 19 | 20 | constructor($http: ng.IHttpService, private supportTypeService: Result.SupportTypeService.IService) { 21 | $http.get("/api/Statistics") 22 | .then(response => { 23 | this.statistics = response.data; 24 | }); 25 | } 26 | 27 | getSupportTypeName(statistic) { 28 | return this.supportTypeService.getDisplayName(statistic.latestSupportType); 29 | } 30 | 31 | statsFor(type: SupportType) { 32 | return this.statistics.filter(s => s.statistic.latestSupportType === type); 33 | } 34 | } 35 | 36 | addAngularState(state, "/Stats", ViewModel, "Statistics/Statistics.html", 37 | { 38 | title: "Statistics", 39 | description: "The most popular packages submitted and their support status" 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /source/Web/Features/Statistics/StatisticsController.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Linq; 4 | using ICanHasDotnetCore.NugetPackages; 5 | using ICanHasDotnetCore.Plumbing; 6 | 7 | namespace ICanHasDotnetCore.Web.Features.Statistics 8 | { 9 | public class StatisticsController : Controller 10 | { 11 | private readonly IStatisticsRepository _statisticsRepository; 12 | 13 | public StatisticsController(IStatisticsRepository statisticsRepository) 14 | { 15 | _statisticsRepository = statisticsRepository; 16 | } 17 | 18 | [HttpGet("api/Statistics")] 19 | public IReadOnlyList Get() 20 | { 21 | return _statisticsRepository.GetAllPackageStatistics() 22 | .OrderByDescending(p => p.Count) 23 | .ThenBy(p => p.Name) 24 | .Select(p => new PackageStatisticResponse() 25 | { 26 | Statistic = p, 27 | MoreInformation = MoreInformationRepository.Get(p.Name).ValueOrNull() 28 | }) 29 | .ToArray(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /source/Web/Features/Statistics/StatisticsRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Data.SqlClient; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using ICanHasDotnetCore.Investigator; 9 | using ICanHasDotnetCore.NugetPackages; 10 | using Microsoft.Extensions.Configuration; 11 | using Serilog; 12 | 13 | namespace ICanHasDotnetCore.Web.Features.Statistics 14 | { 15 | public interface IStatisticsRepository 16 | { 17 | Task AddStatisticsForResult(InvestigationResult result); 18 | IReadOnlyList GetAllPackageStatistics(); 19 | void UpdateSupportTypeFor(PackageStatistic stat, SupportType supportType); 20 | } 21 | 22 | public class StatisticsRepository : IStatisticsRepository 23 | { 24 | // Only log packages found on Nuget.org 25 | private static readonly SupportType[] AddStatisticsFor = { SupportType.Unsupported, SupportType.Supported, SupportType.PreRelease }; 26 | 27 | private readonly string _connectionString; 28 | 29 | public StatisticsRepository(IConfigurationRoot configuration) 30 | { 31 | _connectionString = configuration["ConnectionString"]; 32 | if (string.IsNullOrWhiteSpace(_connectionString)) 33 | throw new Exception("The configuration setting 'ConnectionString' is not set"); 34 | } 35 | 36 | public async Task AddStatisticsForResult(InvestigationResult result) 37 | { 38 | try 39 | { 40 | using (var con = new SqlConnection(_connectionString)) 41 | { 42 | await con.OpenAsync(); 43 | var tasks = result.GetAllDistinctRecursive() 44 | .Where(p => AddStatisticsFor.Contains(p.SupportType)) 45 | .Select(p => AddStatistic(con, p)) 46 | .ToArray(); 47 | 48 | await Task.WhenAll(tasks); 49 | } 50 | } 51 | catch (Exception ex) 52 | { 53 | Log.Logger.Error(ex, "Exception writing statistics"); 54 | } 55 | } 56 | 57 | private async Task AddStatistic(SqlConnection con, PackageResult package) 58 | { 59 | const string sql = @"MERGE dbo.PackageStatistics AS t 60 | USING 61 | ( 62 | SELECT @name as 'Name', @latestSupportType as 'LatestSupportType' 63 | ) AS s 64 | ON t.Name = s.Name 65 | 66 | WHEN MATCHED THEN 67 | UPDATE SET LatestSupportType = s.LatestSupportType, [Count] = [Count] + 1 68 | 69 | WHEN NOT MATCHED THEN 70 | INSERT (Name, LatestSupportType, Count) 71 | VALUES (s.Name, s.LatestSupportType, 1);"; 72 | 73 | try 74 | { 75 | using (var cmd = new SqlCommand(sql, con)) 76 | { 77 | cmd.Parameters.AddWithValue("@name", package.PackageName); 78 | cmd.Parameters.AddWithValue("@latestSupportType", package.SupportType.ToString()); 79 | await cmd.ExecuteNonQueryAsync(); 80 | } 81 | } 82 | catch (Exception ex) 83 | { 84 | Log.Logger.Error(ex, "Exception writing statistic {stat}", package.PackageName); 85 | } 86 | } 87 | 88 | public IReadOnlyList GetAllPackageStatistics() 89 | { 90 | const string sql = "SELECT Name, [Count], LatestSupportType FROM dbo.[PackageStatistics] WITH (NOLOCK)"; 91 | var stats = new List(); 92 | using (var con = new SqlConnection(_connectionString)) 93 | { 94 | con.Open(); 95 | using (var cmd = new SqlCommand(sql, con)) 96 | { 97 | var reader = cmd.ExecuteReader(); 98 | while (reader.Read()) 99 | stats.Add(new PackageStatistic() 100 | { 101 | Name = (string)reader["Name"], 102 | Count = (int)reader["Count"], 103 | LatestSupportType = 104 | (SupportType)Enum.Parse(typeof(SupportType), (string)reader["LatestSupportType"]) 105 | }); 106 | } 107 | } 108 | return stats; 109 | } 110 | 111 | public void UpdateSupportTypeFor(PackageStatistic stat, SupportType supportType) 112 | { 113 | const string sql = "UPDATE dbo.[PackageStatistics] SET LatestSupportType = @LatestSupportType WHERE Name = @Name"; 114 | using (var con = new SqlConnection(_connectionString)) 115 | { 116 | con.Open(); 117 | using (var cmd = new SqlCommand(sql, con)) 118 | { 119 | cmd.Parameters.AddWithValue("Name", stat.Name); 120 | cmd.Parameters.AddWithValue("LatestSupportType", supportType.ToString()); 121 | cmd.ExecuteNonQuery(); 122 | } 123 | } 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /source/Web/Features/Version.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore { 2 | export const version = "0.0.0.0"; 3 | } 4 | -------------------------------------------------------------------------------- /source/Web/Features/app.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare var vis; 3 | module ICanHasDotnetCore { 4 | 5 | interface IRouteState extends angular.ui.IState { 6 | data: IRouteData 7 | } 8 | 9 | interface IRouteData { 10 | title?: string; 11 | description?: string; 12 | } 13 | 14 | function httpErrorHandler($httpProvider: ng.IHttpProvider) { 15 | var interceptor = ($q: ng.IQService) => { 16 | return { 17 | 'requestError': response => { 18 | toastr.error(response.message || response.statusText || "An error occured"); 19 | return $q.reject(response); 20 | }, 21 | 'responseError': response => { 22 | var error = (response.data && (response.data.message || response.data.Message)) || response.statusText || "An error occured"; 23 | 24 | toastr.error(error); 25 | return $q.reject(response); 26 | } 27 | }; 28 | }; 29 | $httpProvider.interceptors.push(interceptor); 30 | }; 31 | 32 | export var app = angular.module("app", [ 33 | "ng", 34 | "ui.router", 35 | "ngMaterial", 36 | "ng-file-model" 37 | ]) 38 | .config(httpErrorHandler) 39 | .config([ 40 | "$httpProvider", $httpProvider => { 41 | //initialize get if not there 42 | if (!$httpProvider.defaults.headers.get) { 43 | $httpProvider.defaults.headers.get = {}; 44 | } 45 | //disable IE ajax request caching 46 | $httpProvider.defaults.headers.get["If-Modified-Since"] = "0"; 47 | } 48 | ]) 49 | .config(($locationProvider: ng.ILocationProvider) => $locationProvider.html5Mode(true)) 50 | .config(($urlRouterProvider: angular.ui.IUrlRouterProvider) => { 51 | $urlRouterProvider.when("", "/") 52 | .otherwise(() => "/"); 53 | }) 54 | .config(($urlMatcherFactoryProvider) => { 55 | $urlMatcherFactoryProvider.caseInsensitive(true); 56 | $urlMatcherFactoryProvider.strictMode(false); 57 | }) 58 | .config([ 59 | "$compileProvider", $compileProvider => { 60 | $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|local):/); 61 | }] 62 | ); 63 | 64 | app.run(($window, $rootScope: ng.IRootScopeService, $location: ng.ILocationService) => { 65 | $rootScope.$on('$stateChangeSuccess', 66 | (evt, toState) => { 67 | var title = "I Can Has .NET Core"; 68 | var description = "Analyse your nuget dependencies to determine whether they support .NET Standard"; 69 | if (toState.data) { 70 | if (toState.data.title) { 71 | title = toState.data.title + " - " + title; 72 | } 73 | if (toState.data.description) { 74 | description = toState.data.description; 75 | } 76 | }; 77 | $window.document.title = title; 78 | $("meta[name='description']").attr("content", description); 79 | $("meta[property='og:title']").attr("content", title); 80 | $("meta[property='og:description']").attr("content", description); 81 | }); 82 | }); 83 | 84 | app.config($mdThemingProvider => { 85 | $mdThemingProvider.theme('default') 86 | .primaryPalette('blue') 87 | .accentPalette('orange'); 88 | }); 89 | 90 | app.config($mdIconProvider => { 91 | var rootURL = "ui/images/"; 92 | 93 | // Register the user `avatar` icons 94 | $mdIconProvider 95 | .icon("menu", rootURL + "menu.svg", 24); 96 | }); 97 | 98 | app.run(($window, $rootScope: ng.IRootScopeService, $location: ng.ILocationService, $http: ng.IHttpService) => { 99 | $http.get("/api/Analytics") 100 | .then(result => { 101 | var sendPageView = () => $window.ga('send', 'pageview', $location.path()); 102 | 103 | if (result.data) { 104 | $window.ga('create', result.data, 'auto'); 105 | $rootScope.$on('$stateChangeSuccess', sendPageView); 106 | sendPageView(); 107 | } 108 | }); 109 | }); 110 | 111 | 112 | export function addAngularState(id: string, url: string, controller: Function, template: string, data: IRouteData, params?: any) { 113 | var stateConfig: IRouteState = { 114 | url: url, 115 | templateUrl: "app/" + template, 116 | controller: controller, 117 | controllerAs: "vm", 118 | data: data, 119 | params: params 120 | }; 121 | app.config(($stateProvider: angular.ui.IStateProvider) => 122 | $stateProvider.state(id, stateConfig)); 123 | } 124 | 125 | export function redirect(id: string, url: string, redirectToState: string) { 126 | app.config(($stateProvider: angular.ui.IStateProvider) => 127 | $stateProvider.state(id, { 128 | url: url, 129 | onEnter: ($state) => $state.go(redirectToState) 130 | }) 131 | ); 132 | } 133 | } -------------------------------------------------------------------------------- /source/Web/Features/faq/faq.html: -------------------------------------------------------------------------------- 1 |
2 |

The new tool stack

3 |

4 | The new .NET tool stack includes the dotnet command line tool, xproj project structure and JSON configuration. 5 |

6 |

7 | It is possible to take most existing applications, targeting .NET 4.5.1 or later and convert them to use this new structure. This is done by creating a new .NET Core project, editing the project.json file to change the 8 | frameworks section to target net451 (example). 9 |

10 |

11 | This website (source) uses the new tooling, but targets .NET 4.6.1 as not all our dependencies have been ported to .NET Standard. 12 | The new tooling supports targeting multiple platforms, so each project can be gradually be converted to also target .NET Standard when appropriate. 13 |

14 |

.NET Standard

15 |

16 | .NET Standard Library is a set of APIs that are intended to be available on all .NET runtimes. Applications targeting .NET Standard should run on all platforms that implement that standard. 17 | See the refer to the .NET Standard Library documentation. 18 |

19 |

How does this work?

20 |

21 | We read your packages.config, project.json and paket.dependencies files and recursively lookup up those packages and their dependencies on 22 | nuget.org. We then look at whether the package targets a compatible framework (.NET Standard or PCL) 23 |

24 |

Do you keep any uploaded information?

25 |

26 | For requests using the package file upload method, we keep some statistics in a non-identifying way. We keep a count of how many time a packages 27 | found on nuget.org is appears in a result set. We do not track the names of packages that were not recognised, 28 | as these would typically be from internal nuget feeds. 29 |

30 |

31 | For requests using the GitHub repository scanning method, we keep more information about the end result. The assumption is made that the 32 | contents of these public GitHub repositories is not private. 33 |

34 |

35 | See exactly how we collect statistics 36 |

37 |

38 | We also use google analytics to track page views and time on the site. 39 |

40 | 41 |
42 | 43 | -------------------------------------------------------------------------------- /source/Web/Features/faq/faq.less: -------------------------------------------------------------------------------- 1 | .faq { 2 | } 3 | -------------------------------------------------------------------------------- /source/Web/Features/faq/faq.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Faq { 2 | 3 | export const state = "layout.faq"; 4 | 5 | class ViewModel { 6 | } 7 | 8 | addAngularState(state, "/faq", ViewModel, "faq/faq.html", 9 | { 10 | title: "FAQ", 11 | description: "Frequently Asked Questions" 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /source/Web/Features/home/IndexController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace ICanHasDotnetCore.Web.Features.home 6 | { 7 | [Route("{*url}")] 8 | public class IndexController : Controller 9 | { 10 | private static readonly string[] NotFoundPaths = new[] { "api", "images", "app", "Downloads" }; 11 | public ActionResult Get(string url) 12 | { 13 | if (url != null && NotFoundPaths.Any(p => url.StartsWith(p, StringComparison.OrdinalIgnoreCase))) 14 | return NotFound(); 15 | 16 | return File("~/index.html", "text/html"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /source/Web/Features/home/home.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Can I port my application to .NET Core?

5 | 6 | 7 |

8 | Upload your project's packages.config, project.json, paket.dependencies and *.csproj 9 | files for analysis and we will build a visualisation of the packages and whether 10 | .NET Standard versions are available on nuget.org 11 |

12 | 13 |
14 |
15 | 16 | 17 | 18 |
19 | 20 | 21 |
22 | {{ item.file.name}} 23 | 24 | delete 25 | 26 |
27 |
28 |
29 |
30 |
31 | 32 |

33 | Enter the name or URL of a public GitHub repository and we will scan it for package files and build a 34 | visualisation of the packages and whether .NET Standard versions are available on nuget.org 35 |

36 |
37 | 38 | 39 |
e.g. OctopusDeploy/ICanHasDotnetCore or https://github.com/OctopusDeploy/ICanHasDotnetCore
40 |
41 | {{vm.getGitHubRepositoryName()}} 42 |
43 |
44 | 45 |
46 | 47 | 50 |
51 |
52 |
53 | 54 | directions_run 55 | Visualise Dependencies 56 | 57 | or See Demo 58 |
59 |
60 |
61 |
62 |
63 |

64 | .NET Core is an overloaded term, but is most commonly used to refer to the .NET Standard Library (.NET Standard). 65 | However it may also refer to the new tool stack. For more information, check out the FAQ. 66 |

67 |

68 | There are two main aspects to determining whether you project can be ported to .NET Standard: 69 |

    70 |
  1. 71 | Are any .NET BCL types and methods used that are not part of the standard. Michael Whelan has written an excellent 72 | guide showing how to analyse an existing project and port it. 73 |
  2. 74 |
  3. 75 | Have your other dependencies been ported to .NET Standard. To do that, either upload your project's packages files above 76 | or download the command line tool. 77 |
  4. 78 | 79 |
80 | 81 |

82 |
83 |
84 |
85 | -------------------------------------------------------------------------------- /source/Web/Features/home/home.less: -------------------------------------------------------------------------------- 1 | .home { 2 | .choose-file { 3 | text-transform: none; 4 | } 5 | 6 | md-input-container { 7 | margin-bottom: 0; 8 | } 9 | 10 | .package-file-name { 11 | margin-left: 5px; 12 | text-overflow: ellipsis; 13 | white-space: nowrap; 14 | } 15 | 16 | .package-list 17 | { 18 | margin-top: 20px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/Web/Features/home/home.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Home { 2 | 3 | export const state = "layout.home"; 4 | export interface IPackageFile { 5 | name?: string; 6 | file?: any; 7 | } 8 | 9 | enum Tabs { 10 | UploadPackageFiles = 0, 11 | ScanAGitHubRepository = 1 12 | } 13 | 14 | class ViewModel { 15 | 16 | packageFiles: IPackageFile[]; 17 | selectedTab: Tabs; 18 | gitHubRepository: string; 19 | 20 | constructor($scope: ng.IScope, private $state: ng.ui.IStateService) { 21 | this.packageFiles = [{}]; 22 | 23 | $scope.$watch("vm.packageFiles", () => this.addPackageFileIfNeeded(), true); 24 | } 25 | 26 | private addPackageFileIfNeeded() { 27 | if (this.packageFiles.filter(p => !p.file).length === 0) 28 | this.packageFiles.push({}); 29 | } 30 | 31 | deletePackageFile(packageFile: IPackageFile) { 32 | this.packageFiles = _.without(this.packageFiles, packageFile); 33 | } 34 | 35 | visualiseDependencies() { 36 | switch (this.selectedTab) { 37 | case Tabs.UploadPackageFiles: 38 | this.$state.go(Result.state, 39 | { 40 | data: this.packageFiles.filter(p => !!p.file) 41 | }); 42 | return; 43 | case Tabs.ScanAGitHubRepository: 44 | this.$state.go(Result.state, 45 | { 46 | github: this.getGitHubRepositoryName() 47 | }); 48 | return; 49 | } 50 | 51 | } 52 | 53 | canSubmit() { 54 | switch (this.selectedTab) { 55 | case Tabs.UploadPackageFiles: 56 | return this.packageFiles.length > 1; 57 | case Tabs.ScanAGitHubRepository: 58 | return !!this.getGitHubRepositoryName(); 59 | } 60 | return false; 61 | } 62 | 63 | isGitHubRepositoryValid() { 64 | var repo = this.getGitHubRepositoryName(); 65 | return !!repo && 66 | repo.indexOf("/") > 0 && 67 | repo.indexOf("/") === repo.lastIndexOf("/"); 68 | } 69 | 70 | getGitHubRepositoryName() { 71 | var repo = this.gitHubRepository ? this.gitHubRepository.replace("https://github.com/", "") : ""; 72 | return repo.trim().replace(/^\//, "").replace(/\/$/, ""); 73 | } 74 | } 75 | 76 | addAngularState(state, "/", ViewModel, "home/home.html", 77 | { 78 | title: "Home", 79 | description: "Can you port your application to .NET Core? Submit your GitHub repository or package files to see whether your nuget dependencies support .NET Standard" 80 | }); 81 | } 82 | -------------------------------------------------------------------------------- /source/Web/Features/layout/ExternalLink.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result.PackageResultBox { 2 | app.directive("externalLink", ($state, $rootScope: ng.IRootScopeService) => { 3 | 4 | return { 5 | restrict: "E", 6 | template: "launch", 7 | replace: true, 8 | scope: { 9 | href: "@" 10 | }, 11 | transclude: true, 12 | link: (scope: ng.IScope, element, attr, ctrl, transclude) => { 13 | transclude(clone => element.prepend(clone)); 14 | } 15 | } 16 | } 17 | ); 18 | 19 | } -------------------------------------------------------------------------------- /source/Web/Features/layout/MenuHighlight.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result.PackageResultBox { 2 | app.directive("menuHighlight", ($state, $rootScope: ng.IRootScopeService) => { 3 | 4 | return { 5 | restrict: "A", 6 | link: (scope: ng.IScope, element, attr) => { 7 | var setActive = () => { 8 | if ($state.current.name === attr["uiSref"]) { 9 | element.addClass("active"); 10 | } else { 11 | element.removeClass("active"); 12 | } 13 | }; 14 | $rootScope.$on('$stateChangeSuccess', setActive); 15 | setActive(); 16 | } 17 | } 18 | } 19 | ); 20 | 21 | } -------------------------------------------------------------------------------- /source/Web/Features/layout/NavMenu.html: -------------------------------------------------------------------------------- 1 | homeHome 2 | whatshotDemo 3 | chevron_rightConsole 4 | question_answer FAQ 5 | visibilityKnowledge 6 | pie_chartStats 7 | codeSource 8 | -------------------------------------------------------------------------------- /source/Web/Features/layout/NavMenu.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Menu { 2 | app.directive("navMenu", () => { 3 | return { 4 | restrict: "E", 5 | templateUrl: "app/layout/NavMenu.html" 6 | } 7 | } 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /source/Web/Features/layout/layout.html: -------------------------------------------------------------------------------- 1 | 9 |
10 | 11 |
12 | 13 | menu 14 | 15 | 16 | track_changes 17 | I Can Has .NET Core 18 | 19 | 20 | 21 |
22 |
23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 49 |
-------------------------------------------------------------------------------- /source/Web/Features/layout/layout.less: -------------------------------------------------------------------------------- 1 | @import (reference) "../site"; 2 | 3 | md-toolbar:not(.md-menu-toolbar) { 4 | background-color: @block-colour; 5 | } 6 | 7 | .md-toolbar-tools { 8 | margin: auto; 9 | 10 | md-icon { 11 | vertical-align: bottom; 12 | 13 | &.external-link { 14 | vertical-align: super; 15 | } 16 | } 17 | 18 | nav-menu { 19 | font-size: 14px; 20 | 21 | a { 22 | padding-bottom: 3px; 23 | padding-left: 5px; 24 | padding-right: 5px; 25 | 26 | &.active { 27 | border-bottom-style: solid; 28 | border-width: 2px; 29 | border-color: @accent; 30 | } 31 | 32 | > md-icon:first-child { 33 | display: none; 34 | } 35 | } 36 | } 37 | } 38 | 39 | md-sidenav { 40 | padding: 0px; 41 | min-width: 200px; 42 | max-width: 200px; 43 | 44 | nav-menu { 45 | a { 46 | display: block; 47 | padding: 15px 30px; 48 | border-bottom-style: solid; 49 | border-width: 1px; 50 | border-color: #eee; 51 | color: @block-colour; 52 | text-decoration: none; 53 | 54 | md-icon { 55 | color: @block-colour; 56 | } 57 | 58 | > md-icon:first-child { 59 | margin-right: 10px; 60 | vertical-align: bottom; 61 | } 62 | 63 | &.active { 64 | background-color: @block-colour; 65 | color: white; 66 | border-color: @block-colour; 67 | 68 | md-icon { 69 | color: white; 70 | } 71 | } 72 | 73 | &:hover { 74 | background-color: @accent; 75 | color: white; 76 | border-color: @accent; 77 | 78 | md-icon { 79 | color: white; 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | footer { 87 | background-color: @block-colour; 88 | padding-top: 10px; 89 | border-top: 1px solid #ddd; 90 | text-align: center; 91 | color: white; 92 | font-size: 12px; 93 | 94 | img { 95 | height: 1.5em; 96 | margin-right: 5px; 97 | vertical-align: middle; 98 | } 99 | 100 | a { 101 | color: white; 102 | } 103 | 104 | .footer-continuation { 105 | width: 100%; 106 | height: 1000px; 107 | background-color: @block-colour; 108 | position: fixed; 109 | margin-top: -5px; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /source/Web/Features/layout/layout.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Layout { 2 | 3 | class ViewModel { 4 | version = ICanHasDotnetCore.version; 5 | 6 | constructor(private $mdSidenav) { 7 | } 8 | 9 | toggleSidebar() { 10 | this.$mdSidenav('left').toggle(); 11 | } 12 | 13 | } 14 | 15 | addAngularState("layout", null, ViewModel, "layout/layout.html", {}); 16 | } 17 | -------------------------------------------------------------------------------- /source/Web/Features/result/Cache/DbNugetResultCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlClient; 3 | using ICanHasDotnetCore.NugetPackages; 4 | using ICanHasDotnetCore.Plumbing; 5 | using ICanHasDotnetCore.Web.Features.Statistics; 6 | using Microsoft.Extensions.Configuration; 7 | using NuGet; 8 | using Serilog; 9 | using System.Linq; 10 | using System.Runtime.Versioning; 11 | 12 | namespace ICanHasDotnetCore.Web.Features.result.Cache 13 | { 14 | 15 | public class DbNugetResultCache : INugetResultCache 16 | { 17 | private readonly string _connectionString; 18 | 19 | public DbNugetResultCache(IConfigurationRoot configuration) 20 | { 21 | _connectionString = configuration["ConnectionString"]; 22 | if (string.IsNullOrWhiteSpace(_connectionString)) 23 | throw new Exception("The configuration setting 'ConnectionString' is not set"); 24 | } 25 | 26 | public Option Get(string id, SemanticVersion version) 27 | { 28 | try 29 | { 30 | 31 | 32 | const string sql = "SELECT * FROM dbo.NugetResultCache WHERE Id = @Id AND Version = @Version"; 33 | using (var con = new SqlConnection(_connectionString)) 34 | { 35 | con.Open(); 36 | using (var cmd = new SqlCommand(sql, con)) 37 | { 38 | cmd.Parameters.AddWithValue("Id", id); 39 | cmd.Parameters.AddWithValue("Version", version.ToNormalizedString()); 40 | var reader = cmd.ExecuteReader(); 41 | if (reader.Read()) 42 | { 43 | var dependencies = ((string)reader["Dependencies"]) 44 | .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); 45 | 46 | var frameworks = ((string)reader["Frameworks"]) 47 | .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) 48 | .Select(n => new FrameworkName(n)).ToArray(); 49 | 50 | var supportType = (SupportType)Enum.Parse(typeof(SupportType), (string)reader["SupportType"]); 51 | 52 | return new NugetPackage( 53 | (string)reader["Id"], 54 | dependencies, 55 | supportType, 56 | SemanticVersion.Parse((string)reader["Version"]), 57 | frameworks 58 | ) 59 | { 60 | ProjectUrl = reader["ProjectUrl"] as string 61 | }; 62 | } 63 | else 64 | { 65 | return Option.ToNone; 66 | } 67 | } 68 | } 69 | } 70 | catch (Exception ex) 71 | { 72 | Log.Warning(ex, "Could not retrieve {id} {version} in Nuget Package Cache", id, version); 73 | return Option.ToNone; 74 | } 75 | } 76 | 77 | public void Store(NugetPackage package) 78 | { 79 | if (package.Version.None) 80 | return; 81 | try 82 | { 83 | const string sql = "INSERT INTO dbo.NugetResultCache (Id, SupportType, Version, ProjectUrl, Dependencies, Frameworks) VALUES (@Id, @SupportType, @Version, @ProjectUrl, @Dependencies, @Frameworks)"; 84 | using (var con = new SqlConnection(_connectionString)) 85 | { 86 | con.Open(); 87 | using (var cmd = new SqlCommand(sql, con)) 88 | { 89 | cmd.Parameters.AddWithValue("Id", package.Id); 90 | cmd.Parameters.AddWithValue("SupportType", package.SupportType.ToString()); 91 | cmd.Parameters.AddWithValue("Version", package.Version.Value.ToNormalizedString()); 92 | cmd.Parameters.AddWithValue("ProjectUrl", (object)package.ProjectUrl ?? DBNull.Value); 93 | cmd.Parameters.AddWithValue("Dependencies", string.Join("|", package.Dependencies)); 94 | cmd.Parameters.AddWithValue("Frameworks", string.Join("|", package.Frameworks.Select(f => f.FullName))); 95 | cmd.ExecuteNonQuery(); 96 | } 97 | } 98 | } 99 | catch (Exception ex) 100 | { 101 | Log.Warning(ex, "Could not store {id} {version} in Nuget Package Cache", package.Id, package.Version.Value); 102 | } 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /source/Web/Features/result/DependencyGraph.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result.DependencyGraph { 2 | app.directive("dependencyGraph", (supportTypeService: SupportTypeService.IService) => { 3 | 4 | var network; 5 | var options = { 6 | nodes: { 7 | shape: "box", 8 | size: 25, 9 | shadow: { enabled: true } 10 | }, 11 | edges: { 12 | arrows: { to: true }, 13 | color: { 14 | inherit: "to" 15 | }, 16 | shadow: { enabled: true } 17 | } 18 | }; 19 | 20 | var getUniqueName = (result: IPackageResult) => { 21 | if (result.supportType === SupportType.InvestigationTarget) { 22 | return "Target-" + result.packageName; 23 | } else { 24 | return result.packageName; 25 | } 26 | } 27 | 28 | var createNode = (result: IPackageResult) => ( 29 | { 30 | id: getUniqueName(result), 31 | label: result.packageName, 32 | color: supportTypeService.getColours(result.supportType), 33 | } 34 | ); 35 | 36 | var createEdge = (from: IPackageResult, to: string) => ( 37 | { 38 | from: getUniqueName(from), 39 | to: to 40 | } 41 | ); 42 | 43 | var setData = (results: IPackageResult[]) => { 44 | if (!results) { 45 | network.setData({}); 46 | return; 47 | } 48 | 49 | var nodes = results.map(createNode); 50 | var edges = _.flatMap( 51 | results.filter(f => !!f.dependencies), 52 | (from) => from.dependencies.map(to => createEdge(from, to)) 53 | ); 54 | 55 | network.setData({ 56 | nodes: new vis.DataSet(nodes), 57 | edges: new vis.DataSet(edges) 58 | }); 59 | } 60 | 61 | var link = (scope: ng.IScope, element) => { 62 | scope.$watch("packageResults", setData); 63 | network = new vis.Network(element[0], {}, options); 64 | }; 65 | 66 | return { 67 | restrict: "E", 68 | scope: { 69 | packageResults: "=" 70 | }, 71 | link: link 72 | } 73 | } 74 | ); 75 | 76 | } -------------------------------------------------------------------------------- /source/Web/Features/result/GetGitHubRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ICanHasDotnetCore.Web.Features.result 2 | { 3 | public class GetGitHubRequest 4 | { 5 | public string Id { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /source/Web/Features/result/GetResultRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ICanHasDotnetCore.Web.Features.result 2 | { 3 | public class GetResultRequest 4 | { 5 | public PackageFile[] PackageFiles { get; set; } 6 | } 7 | 8 | public class PackageFile 9 | { 10 | public string Name { get; set; } 11 | public string Contents { get; set; } 12 | public string OriginalFileName { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /source/Web/Features/result/GetResultResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using ICanHasDotnetCore.NugetPackages; 3 | 4 | namespace ICanHasDotnetCore.Web.Features.result 5 | { 6 | public class GetResultResponse 7 | { 8 | public PackageResult[] Result { get; set; } 9 | public string GraphViz { get; set; } 10 | public string Cypher { get; set; } 11 | } 12 | 13 | public class PackageResult 14 | { 15 | public string PackageName { get; set; } 16 | public string Error { get; set; } 17 | public SupportType SupportType { get; set; } 18 | public string[] Dependencies { get; set; } 19 | public string ProjectUrl { get; set; } 20 | public MoreInformation MoreInformation { get; set; } 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /source/Web/Features/result/GitHub/GitHubScanner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Threading.Tasks; 8 | using ICanHasDotnetCore.Plumbing; 9 | using ICanHasDotnetCore.SourcePackageFileReaders; 10 | using Microsoft.Extensions.Configuration; 11 | using Octokit; 12 | using Octokit.Internal; 13 | using Serilog; 14 | 15 | namespace ICanHasDotnetCore.Web.Features.result.GitHub 16 | { 17 | public class GitHubScanner 18 | { 19 | private static readonly string AssemblyVersion = typeof(GitHubScanner).Assembly.GetName().Version.ToString(); 20 | private static string _token; 21 | 22 | public GitHubScanner(IConfigurationRoot configuration) 23 | { 24 | _token = configuration["GitHubToken"]; 25 | ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; 26 | } 27 | 28 | public async Task> Scan(string repoId) 29 | { 30 | try 31 | { 32 | var repo = RepositoryId.Parse(repoId); 33 | if (repo.None) 34 | return Result.Failed($"{repoId} is not recognised as a GitHub repository name"); 35 | 36 | return await Scan(repo.Value); 37 | } 38 | catch (Exception ex) 39 | { 40 | Log.Error(ex, "Exception scanning repository {name}", repoId); 41 | return Result.Failed($"Something didn't go quite right. The error has been logged."); 42 | } 43 | } 44 | 45 | private async Task> Scan(RepositoryId repo) 46 | { 47 | try 48 | { 49 | var client = GetClient(); 50 | var contents = await GetContentsRecursive(client, repo); 51 | 52 | return contents 53 | .Select(c => new SourcePackageFile(c.Path.Contains("/") ? c.Path.Substring(0, c.Path.LastIndexOf("/")) : "", c.Name, Encoding.UTF8.GetBytes(c.Content))) 54 | .ToArray(); 55 | } 56 | catch (NotFoundException nfe) when (nfe.Message == $"repos/{repo}/commits was not found.") 57 | { 58 | return Result.Failed($"{repo} does not exist or is not publically accessible"); 59 | } 60 | } 61 | 62 | 63 | 64 | private GitHubClient GetClient() 65 | { 66 | return new GitHubClient( 67 | new ProductHeaderValue("ICanHasDot.net", AssemblyVersion), 68 | new InMemoryCredentialStore(string.IsNullOrEmpty(_token) ? Credentials.Anonymous : new Credentials(_token) 69 | ) 70 | ); 71 | } 72 | 73 | private async Task> GetContentsRecursive(GitHubClient client, RepositoryId repo) 74 | { 75 | var commits = await client.Repository.Commit.GetAll(repo.Owner, repo.Name); 76 | var head = commits.First(); 77 | var treeResponse = await client.Git.Tree.GetRecursive(repo.Owner, repo.Name, head.Sha); 78 | 79 | if (treeResponse.Truncated) 80 | Log.Warning("Result truncated for {repo}", repo); 81 | 82 | var getFileTasks = treeResponse.Tree 83 | .Where(t => t.Type == TreeType.Blob) 84 | .Where(t => 85 | SourcePackageFileReader.SupportedFiles.Any(f => 86 | t.Path.Equals(f, StringComparison.OrdinalIgnoreCase) || 87 | t.Path.EndsWith($"/{f}", StringComparison.OrdinalIgnoreCase) 88 | ) || 89 | SourcePackageFileReader.SupportedExtensions.Any(e => 90 | t.Path.EndsWith(e, StringComparison.OrdinalIgnoreCase) 91 | ) 92 | ) 93 | .Select(t => client.Repository.Content.GetAllContents(repo.Owner, repo.Name, t.Path)) 94 | .ToArray(); 95 | 96 | return (await Task.WhenAll(getFileTasks)) 97 | .SelectMany(r => r) 98 | .ToArray(); 99 | 100 | } 101 | 102 | public class RepositoryId 103 | { 104 | 105 | public RepositoryId(string owner, string name) 106 | { 107 | Owner = owner; 108 | Name = name; 109 | } 110 | public static Option Parse(string repoId) 111 | { 112 | var match = Regex.Match(repoId, @"^\W*([\w\-_\.]+)/([\w\-_\.]+)\W*$"); 113 | if (match.Success) 114 | return new RepositoryId(match.Groups[1].Value, match.Groups[2].Value); 115 | return Option.ToNone; 116 | } 117 | 118 | public string Owner { get; } 119 | public string Name { get; } 120 | 121 | public override string ToString() 122 | { 123 | return $"{Owner}/{Name}"; 124 | } 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /source/Web/Features/result/PackageResultBox.less: -------------------------------------------------------------------------------- 1 | package-result-box { 2 | margin-top: 5px; 3 | margin-bottom: 5px; 4 | border-radius: 5px; 5 | border-width: 1px; 6 | border-style: solid; 7 | padding: 2px 5px; 8 | display: block; 9 | font-size: 14px; 10 | } 11 | -------------------------------------------------------------------------------- /source/Web/Features/result/PackageResultBox.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result.PackageResultBox { 2 | app.directive("packageResultBox", (supportTypeService: SupportTypeService.IService) => { 3 | 4 | return { 5 | restrict: "E", 6 | scope: { 7 | type: "=", 8 | text: "=" 9 | }, 10 | link: (scope: ng.IScope, element) => { 11 | 12 | 13 | var type = isNaN(Number(scope["type"])) ? SupportType[scope["type"]] : scope["type"]; 14 | var colours = supportTypeService.getColours(type); 15 | 16 | var el = $(element); 17 | el.css("background-color", colours.background); 18 | el.css("border-color", colours.border); 19 | el.html(scope["text"] || supportTypeService.getDisplayName(type)); 20 | } 21 | } 22 | } 23 | ); 24 | 25 | } -------------------------------------------------------------------------------- /source/Web/Features/result/ResultGroup.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |

{{vm.description}}

4 |
5 |
6 |
7 | {{package.packageName}} 8 | search 9 | web 10 |
11 |
12 | 13 |
14 |
{{package.error}}
15 |
16 | {{package.moreInformation.message}} 17 | [{{ package.linkText || 'More Information'}}] 18 |
19 |
20 |
21 |
-------------------------------------------------------------------------------- /source/Web/Features/result/ResultGroup.less: -------------------------------------------------------------------------------- 1 | result-group { 2 | display: block; 3 | 4 | 5 | 6 | package-result-box { 7 | width: 250px; 8 | font-size: 14px; 9 | } 10 | 11 | .list-item { 12 | font-size: 13px; 13 | padding: 3px 10px; 14 | 15 | .name { 16 | margin-bottom: 2px; 17 | margin-top: 2px; 18 | } 19 | 20 | package-result-box { 21 | font-size: 11px; 22 | width: auto; 23 | display: inline-block; 24 | margin-left: 5px; 25 | margin-bottom: 2px; 26 | margin-top: 2px; 27 | } 28 | 29 | .error { 30 | color: red; 31 | } 32 | 33 | .more-information{ 34 | 35 | } 36 | } 37 | 38 | .list-item:nth-child(even) { 39 | background: #eee; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /source/Web/Features/result/ResultGroup.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result.ResultGroup { 2 | 3 | class ViewModel { 4 | 5 | private foundOnNuget = [ 6 | SupportType.Supported, 7 | SupportType.PreRelease, 8 | SupportType.Unsupported 9 | ]; 10 | 11 | type: string; 12 | allPackages: Result.IPackageResult[]; 13 | packages: Result.IPackageResult[]; 14 | showNugetLink: boolean; 15 | 16 | constructor($scope: ng.IScope) { 17 | $scope.$watch('vm.allPackages', () => this.updatePackages()); 18 | this.showNugetLink = this.foundOnNuget.indexOf(SupportType[this.type]) >= 0; 19 | } 20 | 21 | updatePackages() { 22 | var type = SupportType[this.type]; 23 | this.packages = (this.allPackages || []).filter(p => p.supportType === type); 24 | } 25 | 26 | 27 | getDependencies(pkg: Result.IPackageResult) { 28 | return this.allPackages 29 | .filter(p => p.supportType !== SupportType.InvestigationTarget) 30 | .filter(p => pkg.dependencies.filter(d => p.packageName === d).length > 0); 31 | } 32 | } 33 | 34 | app.directive("resultGroup", () => { 35 | return { 36 | restrict: "E", 37 | scope: { 38 | type: "@", 39 | allPackages: "=", 40 | description: "@" 41 | }, 42 | controller: ViewModel, 43 | controllerAs: "vm", 44 | bindToController: true, 45 | templateUrl: "app/result/ResultGroup.html" 46 | } 47 | } 48 | ); 49 | 50 | } -------------------------------------------------------------------------------- /source/Web/Features/result/SupportTypeService.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result.SupportTypeService { 2 | 3 | export interface IColours { 4 | border: string; 5 | background: string; 6 | highlight: { 7 | border: string; 8 | background: string; 9 | } 10 | } 11 | 12 | export interface IService { 13 | getColours(type: SupportType): IColours; 14 | getDisplayName(type: SupportType): string; 15 | } 16 | 17 | class Service implements IService { 18 | getColours(type: SupportType): IColours { 19 | switch (type) { 20 | case SupportType.NotFound: //Grey 21 | return { border: "#616161", background: "#E0E0E0", highlight: { border: "#616161", background: "#F5F5F5" } }; 22 | case SupportType.KnownReplacementAvailable: // blue 23 | return { border: "#0277BD", background: "#81D4FA", highlight: { border: "#0277BD", background: "#B3E5FC" } }; 24 | case SupportType.InvestigationTarget: // purple 25 | return { border: "#673AB7", background: "#B39DDB", highlight: { border: "#673AB7", background: "#D1C4E9" } }; 26 | case SupportType.Supported: // green 27 | return { border: "#43A047", background: "#A5D6A7", highlight: { border: "#43A047", background: "#C8E6C9" } }; 28 | case SupportType.PreRelease: // teal 29 | return { border: "#00897B", background: "#80CBC4", highlight: { border: "#00897B", background: "#B2DFDB" } }; 30 | case SupportType.Unsupported: //orange 31 | return { border: "#FF9800", background: "#FFCC80", highlight: { border: "#FF9800", background: "#FFE0B2" } }; 32 | case SupportType.NoDotNetLibraries: //blue grey 33 | return { border: "#78909C", background: "#B0BEC5", highlight: { border: "#78909C", background: "#CFD8DC" } }; 34 | case SupportType.Error: // red 35 | return { border: "#b71c1c", background: "#ef9a9a", highlight: { border: "#b71c1c", background: "#ffcdd2" } }; 36 | default: // B&W 37 | return { border: "#212121", background: "#FAFAFA", highlight: { border: "#212121", background: "#FAFAFA" } }; 38 | } 39 | }; 40 | 41 | getDisplayName(type: SupportType): string { 42 | switch (type) { 43 | case SupportType.NotFound: 44 | return "Not Found"; 45 | case SupportType.KnownReplacementAvailable: 46 | return "Known Replacement Available"; 47 | case SupportType.InvestigationTarget: 48 | return "Your Project"; 49 | case SupportType.Supported: 50 | return "Supported"; 51 | case SupportType.PreRelease: 52 | return "Supported (Pre-release)"; 53 | case SupportType.NoDotNetLibraries: 54 | return "Not a .NET Library"; 55 | case SupportType.Unsupported: 56 | return "Unsupported"; 57 | case SupportType.Error: 58 | return "Error"; 59 | default: 60 | } 61 | }; 62 | } 63 | 64 | app.service("supportTypeService", Service); 65 | } -------------------------------------------------------------------------------- /source/Web/Features/result/result.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
{{vm.loadingMessage}}...
5 |
6 | sentiment_very_dissatisfied 7 |

Oh no! Something went very wrong. The error has been logged, and we will try and fix it, so come back tomorrow and try again.

8 |

9 | In the meantime, this is a thing. 10 |

11 |
12 |
13 | sentiment_dissatisfied 14 |

Oops! Something went wrong. The error was:

15 |

{{vm.errorMessage}}

16 |

17 | Hopefully that helps. If not, please submit an issue on our 18 | GitHub. 19 |

20 |
21 |
22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 | expand_more Expand 36 | expand_less Collapse 37 |
38 |
39 |
40 |

Results

41 | This is a list of all the packages scanned found, along with the packages each of them depends on. 42 | 44 | 46 | 49 | 52 | 55 | 57 | 60 | 62 |
63 |
64 |

GraphVis data

65 |

66 | The data below is in GraphVis dot format can be imported into many 67 | network graphing tools, including webgraphviz.com. 68 |

69 |
{{vm.response.graphViz}}
70 |
71 |
72 |

Cypher data

73 |

74 | The data below is in the Cypher format and can be imported into any 75 | database that accepts CQL, you can also try it in a sandbox environment at neo4j.com/sandbox-v2. 76 |

77 |
{{vm.response.cypher}}
78 |
79 |
80 |
81 |
82 | -------------------------------------------------------------------------------- /source/Web/Features/result/result.less: -------------------------------------------------------------------------------- 1 | @import (reference) "../site"; 2 | .result { 3 | padding-bottom: 20px; 4 | 5 | dependency-graph { 6 | height: 300px; 7 | display: block; 8 | border: 1px solid grey; 9 | } 10 | 11 | dependency-graph.expanded { 12 | height: 800px; 13 | } 14 | 15 | .loading-message { 16 | text-align: center; 17 | margin-top: 10px; 18 | opacity: .5; 19 | height: 400px; 20 | } 21 | 22 | .legend { 23 | position: absolute; 24 | top: 1px; 25 | right: 0; 26 | padding: 10px; 27 | border-color: #ddd; 28 | border-width: 1px; 29 | border-style: solid; 30 | 31 | package-result-box { 32 | font-size: 12px; 33 | } 34 | } 35 | 36 | 37 | 38 | result-group { 39 | margin-top: 15px; 40 | } 41 | 42 | .graphvis { 43 | white-space: pre; 44 | border-color: #ddd; 45 | border-width: 1px; 46 | border-style: solid; 47 | background: #eee; 48 | font-family: Consolas,monospace; 49 | font-size: 12px; 50 | max-height: 400px; 51 | overflow: auto; 52 | padding: 10px; 53 | } 54 | 55 | .server-error{ 56 | max-width: 600px; 57 | .error-icon { 58 | color: red; 59 | } 60 | 61 | .error-message { 62 | color: red; 63 | } 64 | } 65 | 66 | 67 | .bad-request{ 68 | .error-icon { 69 | color: @accent; 70 | } 71 | 72 | .error-message { 73 | color: @accent; 74 | } 75 | } 76 | 77 | .server-error, .bad-request { 78 | margin-top: 30px; 79 | text-align: center; 80 | font-size: 18px; 81 | 82 | .error-icon { 83 | width: 100px; 84 | height: 100px; 85 | font-size: 80px; 86 | display: block; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /source/Web/Features/result/result.ts: -------------------------------------------------------------------------------- 1 | module ICanHasDotnetCore.Result { 2 | 3 | export const state = "layout.result"; 4 | export const demoState = "layout.resultDemo"; 5 | 6 | 7 | export enum SupportType { 8 | NotFound = 0, 9 | Supported = 1, 10 | PreRelease = 2, 11 | Unsupported = 3, 12 | NoDotNetLibraries = 4, 13 | KnownReplacementAvailable = 5, 14 | InvestigationTarget = 6, 15 | Error = 7 16 | } 17 | 18 | export interface IGetResultRequest { 19 | packageFiles: Home.IPackageFile[] 20 | } 21 | 22 | export interface IGetResultResponse { 23 | graphViz: string; 24 | result: IPackageResult[]; 25 | } 26 | 27 | export interface IPackageResult { 28 | packageName: string; 29 | error?: string; 30 | supportType: SupportType; 31 | dependencies: string[]; 32 | } 33 | 34 | const packageFilesStateKey = "packageFiles"; 35 | 36 | 37 | class ViewModel { 38 | 39 | response: IGetResultResponse; 40 | loadingMessage; 41 | error: boolean; 42 | errorMessage: string; 43 | 44 | private loadingMessages = [ 45 | "Reticulating Splines", 46 | "Unpacking Nuggets", 47 | "Building Network", 48 | "Analysing Big Data", 49 | "Consulting the Internet of Things" 50 | ]; 51 | 52 | constructor(private $http: ng.IHttpService, $state: ng.ui.IStateService, private $timeout: ng.ITimeoutService, $location: ng.ILocationService) { 53 | this.setLoadingMessage(); 54 | 55 | var request: angular.IHttpPromise; 56 | 57 | if ($state.current.name === demoState) { 58 | request = $http.post("/api/GetResult/Demo", {}); 59 | } else if ($state.params["github"]) { 60 | var data = { 61 | id: $state.params["github"] 62 | }; 63 | request = $http.post("/api/GetResult/GitHub", data); 64 | } else if ($state.params["data"]) { 65 | var packageFiles = $state.params["data"]; 66 | 67 | packageFiles = packageFiles.map(f => ({ name: f.name, contents: f.file.data, originalFileName: f.file.name })); 68 | request = $http.post("/api/GetResult", { packageFiles: packageFiles }); 69 | } else { 70 | $state.go(Home.state); 71 | return; 72 | } 73 | request.then( 74 | response => this.response = response.data, 75 | response => { 76 | this.error = true; 77 | if (response.status === 400) { 78 | this.errorMessage = response.data; 79 | } 80 | }); 81 | } 82 | 83 | setLoadingMessage() { 84 | if (this.response) 85 | return; 86 | 87 | var index = Math.floor(Math.random() * this.loadingMessages.length); 88 | this.loadingMessage = this.loadingMessages[index]; 89 | 90 | this.$timeout(() => this.setLoadingMessage(), 3000); 91 | } 92 | 93 | hasPackages(typeStr: string) { 94 | return this.getPackages(typeStr).length > 0; 95 | } 96 | 97 | getPackages(typeStr: string) { 98 | if (!this.response) 99 | return []; 100 | var type = SupportType[typeStr]; 101 | return this.response.result.filter(p => p.supportType === type); 102 | } 103 | 104 | } 105 | 106 | addAngularState(state, "/result?github", ViewModel, "result/result.html", 107 | { 108 | title: "Result", 109 | description: "The result of your query", 110 | }, 111 | { data: null }); 112 | addAngularState(demoState, "/result/demo", ViewModel, "result/result.html", 113 | { 114 | title: "Demo", 115 | description: "Demonstration of the information output after submitting your GitHub repository or package files" 116 | }); 117 | } 118 | -------------------------------------------------------------------------------- /source/Web/Features/site.less: -------------------------------------------------------------------------------- 1 | @code-colour: #AD1457; 2 | @primary-hue: #2196f3; 3 | @hue1: #1976D2; 4 | @hue2: #1565c0; 5 | @accent: #FF8C00; 6 | @link-colour: @primary-hue; 7 | @block-colour: #0f2535; 8 | @shaded-colour: #f0f0f0; 9 | 10 | * { 11 | box-sizing: border-box; 12 | } 13 | 14 | body { 15 | letter-spacing: 0.01em; 16 | } 17 | 18 | p, li { 19 | line-height: 1.6em; 20 | } 21 | 22 | .accent { 23 | color: @accent; 24 | } 25 | 26 | .max { 27 | max-width: 960px; 28 | margin: 10px auto; 29 | padding: 0px 10px; 30 | color: inherit; 31 | } 32 | 33 | code { 34 | color: @code-colour; 35 | border-width: 1px; 36 | border-style: solid; 37 | border-color: #ccc; 38 | border-radius: 3px; 39 | background: @shaded-colour; 40 | padding: 2px 3px; 41 | display: inline; 42 | vertical-align: baseline; 43 | } 44 | 45 | code.block { 46 | border-color: @code-colour; 47 | border-style: dotted; 48 | padding: 15px; 49 | display: block; 50 | color: inherit; 51 | white-space: nowrap; 52 | overflow: auto; 53 | } 54 | 55 | md-icon.external-link { 56 | font-size: 10px; 57 | min-width: 10px; 58 | min-height: 10px; 59 | width: 10px; 60 | height: 10px; 61 | vertical-align: super; 62 | } 63 | 64 | .label { 65 | display: inline-block; 66 | background: @primary-hue; 67 | color: white; 68 | border-radius: 3px; 69 | font-size: 12px; 70 | padding: 2px 5px; 71 | vertical-align: bottom; 72 | text-align: center; 73 | } 74 | 75 | .zebra-stripe:nth-child(even) { 76 | background: #eee; 77 | } 78 | 79 | .white-stripe { 80 | padding: 20px 0px; 81 | } 82 | 83 | .grey-stripe { 84 | background-color: @shaded-colour; 85 | padding: 20px 0px; 86 | } 87 | 88 | 89 | 90 | a { 91 | color: @link-colour; 92 | 93 | md-icon { 94 | color: @link-colour; 95 | } 96 | 97 | white-space: nowrap; 98 | } 99 | 100 | .hint { 101 | position: absolute; 102 | left: 2px; 103 | right: auto; 104 | bottom: 7px; 105 | font-size: 12px; 106 | line-height: 14px; 107 | color: grey; 108 | 109 | &.valid { 110 | color: green; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /source/Web/Helpers/DataUriConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | namespace ICanHasDotnetCore.Web.Helpers 5 | { 6 | public static class DataUriConverter 7 | { 8 | public static byte[] ConvertFrom(string str) 9 | { 10 | return Convert.FromBase64String(str.Split(',')[1]); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /source/Web/Plumbing/RedirectHttpMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using ICanHasDotnetCore.Plumbing.Extensions; 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace ICanHasDotnetCore.Web.Plumbing 7 | { 8 | public class RedirectHttpMiddleware 9 | { 10 | readonly RequestDelegate _next; 11 | 12 | public RedirectHttpMiddleware(RequestDelegate next) 13 | { 14 | _next = next; 15 | } 16 | 17 | public async Task Invoke(HttpContext context) 18 | { 19 | if (context.Request.IsHttps || context.Request.Host.Host.EqualsOrdinalIgnoreCase("localhost")) 20 | { 21 | await _next(context); 22 | } 23 | else 24 | { 25 | var withHttps = Uri.UriSchemeHttps + Uri.SchemeDelimiter + context.Request.Host.Host + context.Request.Path; 26 | context.Response.Redirect(withHttps); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /source/Web/Plumbing/RedirectWwwMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Http; 5 | 6 | namespace ICanHasDotnetCore.Web.Plumbing 7 | { 8 | public class RedirectWwwMiddleware 9 | { 10 | readonly RequestDelegate _next; 11 | 12 | public RedirectWwwMiddleware(RequestDelegate next) 13 | { 14 | _next = next; 15 | } 16 | 17 | public async Task Invoke(HttpContext context) 18 | { 19 | if (context.Request.Host.Host.StartsWith("www.", StringComparison.OrdinalIgnoreCase)) 20 | { 21 | var withoutWww = Uri.UriSchemeHttps + Uri.SchemeDelimiter + context.Request.Host.Host.Substring(4) + context.Request.Path; 22 | context.Response.Redirect(withoutWww); 23 | } 24 | else 25 | { 26 | await _next(context); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /source/Web/Program.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.AspNetCore.Hosting; 3 | 4 | namespace ICanHasDotnetCore.Web 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | var host = new WebHostBuilder() 11 | .UseKestrel() 12 | .UseContentRoot(Directory.GetCurrentDirectory()) 13 | .UseIISIntegration() 14 | .UseStartup() 15 | .Build(); 16 | 17 | host.Run(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /source/Web/Properties/.gitignore: -------------------------------------------------------------------------------- 1 | PublishProfiles -------------------------------------------------------------------------------- /source/Web/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("Octopus Deploy")] 10 | [assembly: AssemblyProduct("ICanHasDotnetCore.Web")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyFileVersion("0.0.0.0")] 13 | [assembly: AssemblyVersion("0.0.0.0")] 14 | 15 | // Setting ComVisible to false makes the types in this assembly not visible 16 | // to COM components. If you need to access a type in this assembly from 17 | // COM, set the ComVisible attribute to true on that type. 18 | [assembly: ComVisible(false)] 19 | 20 | // The following GUID is for the ID of the typelib if this project is exposed to COM 21 | [assembly: Guid("e7a09e85-977a-4852-8f5b-9707525d7104")] 22 | -------------------------------------------------------------------------------- /source/Web/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:50000", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "environmentVariables": { 14 | "ASPNETCORE_ENVIRONMENT": "Development" 15 | } 16 | }, 17 | "Web": { 18 | "commandName": "Project", 19 | "launchBrowser": true, 20 | "launchUrl": "http://localhost:5000", 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /source/Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Autofac; 3 | using Autofac.Extensions.DependencyInjection; 4 | using ICanHasDotnetCore.Web.Plumbing; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Routing; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Logging; 11 | using Microsoft.Extensions.PlatformAbstractions; 12 | using Serilog; 13 | using Serilog.Core; 14 | using Serilog.Events; 15 | 16 | namespace ICanHasDotnetCore.Web 17 | { 18 | public class Startup 19 | { 20 | public Startup(IHostingEnvironment env) 21 | { 22 | var builder = new ConfigurationBuilder() 23 | .SetBasePath(env.ContentRootPath) 24 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 25 | .AddEnvironmentVariables(); 26 | Configuration = builder.Build(); 27 | 28 | Log.Logger = new LoggerConfiguration() 29 | .MinimumLevel.Debug() 30 | .Enrich.FromLogContext() 31 | .WriteTo.LiterateConsole(LogEventLevel.Information) 32 | .WriteTo.Seq(Configuration["Seq:Url"], apiKey: Configuration["Seq:ApiKey"]) 33 | .Enrich.WithProperty("Application", "ICanHasDotnetCore") 34 | .Enrich.WithProperty("Environment", env.EnvironmentName) 35 | .CreateLogger(); 36 | } 37 | 38 | public IConfigurationRoot Configuration { get; } 39 | 40 | // This method gets called by the runtime. Use this method to add services to the container. 41 | public IServiceProvider ConfigureServices(IServiceCollection services) 42 | { 43 | // Add framework services. 44 | services.AddMvc(); 45 | 46 | // Create the container builder. 47 | var builder = new ContainerBuilder(); 48 | 49 | // Register dependencies, populate the services from 50 | // the collection, and build the container. 51 | builder.RegisterInstance(Configuration); 52 | builder.RegisterAssemblyModules(typeof(Startup).Assembly); 53 | builder.Populate(services); 54 | var container = builder.Build(); 55 | 56 | // Return the IServiceProvider resolved from the container. 57 | return container.Resolve(); 58 | } 59 | 60 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 61 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 62 | { 63 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 64 | loggerFactory.AddDebug(); 65 | loggerFactory.AddSerilog(); 66 | 67 | app.UseMiddleware() 68 | .UseMiddleware() 69 | .UseStaticFiles() 70 | .UseMvc(); 71 | 72 | if (env.IsDevelopment()) 73 | app.UseDeveloperExceptionPage(); 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/Web/Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0.1.0-ci0166 5 | net461 6 | Web 7 | Exe 8 | Web 9 | false 10 | false 11 | false 12 | false 13 | false 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /source/Web/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "Seq": { 11 | "Url": "http://localhost:5341/", 12 | "ApiKey": "" 13 | }, 14 | "ConnectionString": "Server=localhost;Database=ICanHasDotnetCore;Trusted_Connection=True;MultipleActiveResultSets=True", 15 | "GoogleAnalytics": null, 16 | "GitHubToken": null 17 | } 18 | -------------------------------------------------------------------------------- /source/Web/gulpfile.js: -------------------------------------------------------------------------------- 1 | /// 2 | var gulp = require("gulp"); 3 | var $ = require("gulp-load-plugins")({pattern: ["gulp-*", "gulp.*", "del*", "path*"], lazy: true}); 4 | 5 | var filenames = { 6 | appJs: "app.js", 7 | appCss: "app.css", 8 | vendorJs: "vendor.js", 9 | vendorCss: "vendor.css" 10 | }; 11 | var outputDir = "wwwroot"; 12 | var paths = { 13 | src: { 14 | appIndex: "index.html", 15 | appTs: "Features/**/*.ts", 16 | appLess: ["Features/**/*.less"], 17 | appNgTemplates: "Features/**/*.html", 18 | appImages: ["content/images/**"], 19 | appFonts: [], 20 | appTsOrder: [ 21 | "**/Features.ts", 22 | "**/*" 23 | ], 24 | dtos: ["Features/**/*Request.cs","Features/**/*Response.cs","../Magic/NugetPackages/SupportType.cs"], 25 | vendorJs: [ 26 | "node_modules/jquery/dist/jquery.js", 27 | "node_modules/angular/angular.js", 28 | "node_modules/angular-ui-router/release/angular-ui-router.js", 29 | "node_modules/angular-animate/angular-animate.js", 30 | "node_modules/angular-aria/angular-aria.js", 31 | "node_modules/angular-material/angular-material.js", 32 | "node_modules/toastr/build/toastr.min.js", 33 | "node_modules/lodash/lodash.js", 34 | "node_modules/ng-file-model/ng-file-model.js", 35 | "node_modules/vis/dist/vis.js" 36 | ], 37 | vendorJsOrder: [ 38 | "**/jquery.js", 39 | "**/angular.js", 40 | "**/*" 41 | ], 42 | vendorCss: [ 43 | "node_modules/angular-material/angular-material.css", 44 | "node_modules/toastr/build/toastr.css", 45 | "node_modules/vis/dist/vis.css" 46 | ] 47 | } 48 | }; 49 | 50 | 51 | gulp.task("clean", function (cb) { 52 | $.del.sync([outputDir], { 53 | force: true 54 | }); 55 | cb(); 56 | }); 57 | 58 | 59 | gulp.task("vendorScripts", function () { 60 | return gulp 61 | .src(paths.src.vendorJs) 62 | .pipe($.plumber()) 63 | .pipe($.order(paths.src.vendorJsOrder)) 64 | .pipe($.uglify()) 65 | .pipe($.concat(filenames.vendorJs)) 66 | .pipe(gulp.dest(outputDir)); 67 | }); 68 | 69 | gulp.task("appScripts", function () { 70 | return gulp 71 | .src(paths.src.appTs) 72 | .pipe($.sourcemaps.init()) 73 | .pipe($.plumber()) 74 | //.pipe($.order(paths.src.appTsOrder)) 75 | .pipe($.typescript({ 76 | out: filenames.appJs 77 | })) 78 | .pipe($.sourcemaps.write()) 79 | .pipe(gulp.dest(outputDir)) 80 | .pipe($.livereload()); 81 | 82 | }); 83 | 84 | gulp.task("vendorStyles", function () { 85 | return gulp 86 | .src(paths.src.vendorCss) 87 | .pipe($.plumber()) 88 | .pipe($.minifyCss()) 89 | .pipe($.flatten()) 90 | .pipe($.concat(filenames.vendorCss)) 91 | .pipe(gulp.dest(outputDir)); 92 | }); 93 | 94 | gulp.task("appStyles", function () { 95 | return gulp 96 | .src(paths.src.appLess) 97 | .pipe($.less()) 98 | .pipe($.plumber()) 99 | .pipe($.flatten()) 100 | .pipe($.concat(filenames.appCss)) 101 | .pipe(gulp.dest(outputDir)) 102 | .pipe($.livereload()); 103 | 104 | }); 105 | 106 | gulp.task("ngTemplates", function () { 107 | return gulp 108 | .src(paths.src.appNgTemplates) 109 | .pipe($.plumber()) 110 | .pipe($.bytediff.start()) 111 | .pipe($.htmlmin({ 112 | collapseWhitespace: true, 113 | conservativeCollapse: true, 114 | preserveLineBreaks: true 115 | })) 116 | .pipe($.bytediff.stop()) 117 | .pipe($.angularTemplatecache({ 118 | root: "app", 119 | module: "app" 120 | })) 121 | .pipe(gulp.dest(outputDir)) 122 | .pipe($.livereload()); 123 | 124 | }); 125 | 126 | gulp.task("images", function () { 127 | return gulp 128 | .src(paths.src.appImages) 129 | .pipe($.plumber()) 130 | .pipe(gulp.dest(outputDir + "/images")); 131 | }); 132 | 133 | gulp.task("fonts", function () { 134 | return gulp 135 | .src(paths.src.appFonts) 136 | .pipe($.plumber()) 137 | .pipe($.flatten()) 138 | .pipe(gulp.dest(outputDir + "/fonts")); 139 | }); 140 | 141 | 142 | gulp.task("app", function (cb) { 143 | $.runSequence("clean", ["vendorScripts", "appScripts", "vendorStyles", "appStyles", "ngTemplates", "images", "fonts"], cb); 144 | }); 145 | 146 | 147 | gulp.task("debug", ["app"], function () { 148 | var sources = gulp 149 | .src([outputDir + "/*.*"]) 150 | .pipe($.order([ 151 | "vendor.js", 152 | "angular.js", 153 | "vendor.css", 154 | "**/*.*", 155 | "**/*"], {base: outputDir})); 156 | 157 | var timestamp = new Date().getTime(); 158 | 159 | return gulp 160 | .src("index.html") 161 | .pipe($.plumber()) 162 | .pipe($.inject(sources, { 163 | addRootSlash: true, 164 | ignorePath: [outputDir], 165 | addSuffix: "?d=" + timestamp 166 | })) 167 | .pipe(gulp.dest(outputDir)); 168 | }); 169 | 170 | gulp.task("release", ["debug"]); 171 | 172 | gulp.task("watch", ["debug"], function () { 173 | $.livereload.listen(); 174 | gulp.watch(paths.src.vendorJs, ['vendorScripts']); 175 | gulp.watch(paths.src.appTs, ['appScripts']); 176 | gulp.watch(paths.src.vendorCss, ['vendorStyles']); 177 | gulp.watch(paths.src.appLess, ['appStyles']); 178 | gulp.watch(paths.src.appNgTemplates, ['ngTemplates']); 179 | gulp.watch(paths.src.appImages, ['images']); 180 | }); 181 | gulp.task("default", ["debug"]); 182 | -------------------------------------------------------------------------------- /source/Web/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | I Can Has .NET Core 24 | 25 | 26 | 27 | 28 | 29 | 30 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /source/Web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "private": true, 4 | "devDependencies": { 5 | "bower": "^1.7.7", 6 | "del": "^2.2.0", 7 | "gulp": "^3.9.1", 8 | "gulp-angular-filesort": "^1.1.1", 9 | "gulp-angular-templatecache": "^1.8.0", 10 | "gulp-bytediff": "^1.0.0", 11 | "gulp-concat": "^2.6.0", 12 | "gulp-embedlr": "^0.5.2", 13 | "gulp-flatten": "^0.2.0", 14 | "gulp-htmlmin": "^1.3.0", 15 | "gulp-inject": "^3.0.0", 16 | "gulp-less": "^3.0.5", 17 | "gulp-livereload": "^3.8.1", 18 | "gulp-load-plugins": "1.2.0", 19 | "gulp-minify-css": "^1.2.2", 20 | "gulp-ng-annotate": "^1.1.0", 21 | "gulp-order": "^1.1.1", 22 | "gulp-plumber": "^1.0.1", 23 | "gulp-print": "^2.0.1", 24 | "gulp-rename": "^1.2.2", 25 | "gulp-rev": "^6.0.1", 26 | "gulp-run-sequence": "^0.3.2", 27 | "gulp-sourcemaps": "^1.6.0", 28 | "gulp-streamify": "^1.0.2", 29 | "gulp-task-listing": "^1.0.1", 30 | "gulp-typescript": "^2.12.0", 31 | "gulp-typescript-cs-poco": "^1.9.0", 32 | "gulp-uglify": "^1.5.1", 33 | "load-gulp-tasks": "^0.1.0", 34 | "lodash": "^4.10.0", 35 | "ng-file-model": "0.0.5", 36 | "path": "^0.12.7", 37 | "stream-series": "^0.1.1", 38 | "typings": "^1.3.1" 39 | }, 40 | "dependencies": { 41 | "angular": "^1.5.7", 42 | "angular-animate": "^1.5.0", 43 | "angular-aria": "^1.5.0", 44 | "angular-material": "^1.1.0-rc.5", 45 | "angular-ui-router": "^0.3.1", 46 | "jquery": "^3.4.1", 47 | "toastr": "^2.1.2", 48 | "vis": "^4.16.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/Web/typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDevDependencies": { 3 | "angular": "github:DefinitelyTyped/DefinitelyTyped/angularjs/angular.d.ts#1c94cfb32eab35dde9fda7fb738d324e9a0e66d4", 4 | "angular-material": "registry:dt/angular-material#1.0.0-rc5.0+20160317120654", 5 | "angular-ui-router": "github:DefinitelyTyped/DefinitelyTyped/angular-ui-router/angular-ui-router.d.ts#1c94cfb32eab35dde9fda7fb738d324e9a0e66d4", 6 | "jquery": "github:DefinitelyTyped/DefinitelyTyped/jquery/jquery.d.ts#1c94cfb32eab35dde9fda7fb738d324e9a0e66d4", 7 | "lodash": "github:DefinitelyTyped/DefinitelyTyped/lodash/lodash.d.ts#672afd8f7b09d839b61eeb4867753876cf79c8b6", 8 | "toastr": "github:DefinitelyTyped/DefinitelyTyped/toastr/toastr.d.ts#1c94cfb32eab35dde9fda7fb738d324e9a0e66d4" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /source/Web/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | --------------------------------------------------------------------------------