Giter Site home page Giter Site logo

suesslabs / vslinuxdebug Goto Github PK

View Code? Open in Web Editor NEW
34.0 3.0 8.0 865 KB

VS Extension to remotely deploy and debug your .NET (Core) C# solutions directly to your Linux or Raspberry Pi devices! .NET Core 3.1, .NET 5, 6, 7, 8, 9

Home Page: https://marketplace.visualstudio.com/items?itemName=SuessLabs.VSLinuxDebugger

License: MIT License

C# 98.58% PowerShell 1.42%
deploy debugger remote visual-studio visual-studio-extension suesslabs xeno-innovations linux raspberry-pi raspberrypi

vslinuxdebug's Introduction

Remotely deploy and debug your .NET C# apps via SSH to Linux using Visual Studio 2022.

Get it on the VS MarketPlace!

Visual Studio's "attach to process via SSH" is cute, but it lacks deployment and automatic attaching. This project allows you to do just that on your Linux VM or Raspberry Pi over the network!

Suess Labs consulting is sponsored by Xeno Innovations, Inc.

Overview

Now developers can build, deploy and debug projects on their remote Linux (Ubuntu, Raspberry PI, etc) devices! Customize your SSH connection to use either a password or a private key.

If you enjoy using the extension, please give it a ★★★★★ rating on the Visual Studio Marketplace.

Supported Remote OS

The following Linux distrobutions have been validated and are supported.

  • Ubuntu (20.04 LTS, 22.04 LTS)
  • Raspberry PI OS

Usage

VS Menu

  • Build and upload to remote devices
  • Remote debugging*
    • This is still in the experimental stages. Please use VS' Attach to Process if you have issues
  • VS Linux Debugger will automatically detect and install vsdbg for you!

For GUI app debugging, you can use the Build and Deploy feature, however, you must manually Attach to Process via SSH using Visual Studio at this time.

Getting Started

Linux, we'll need SSH and cURL for access and downloading any missing tools:

sudo apt install openssh-server
sudo apt install curl

Windows:

  1. Open Visual Studio (VS) > Tools > Options > Linux Debugger
  2. Input: Remote Host IP address
  3. Input: Remote's User Name and Password
  4. VS > Extensions > Linux Debugger > Build, Deploy, Debug

Tools Options

Manually Attaching (for GUI apps)

For GUI projects, you can use Build and Deploy and then manually attach to the process via SSH by using Visual Studio's built-in tool

  1. Deploy to remote machine via
    1. Extensions > Linux Debugger > "Build and Deploy"
  2. Run GUI app on remote machine
    1. dotnet MyGuiApp.dll
  3. Debug > "Attach to Process.."
  4. Connection Type: SSH
  5. Connection Target: (Remote machine's IP)
  6. (Select process)
  7. Click, Attach
  8. Check, "Managed (.NET Core for Unix)"
  9. Click, OK

This will save you 1.5 minutes on every build of manual uploading and updating rights via chown -R.

How To Generate Private Key (optional)

The following steps are options if you wish to use an SSH Private Key. These steps were written for Windows 10, however, on Linux the steps are similar.

  1. Open PowerShell:
  2. Generate key (with old PEM format)
    1. ssh-keygen -m PEM -t rsa -b 4096
    2. In the future, we'll be able to use ssh-keygen.. just not yet.
  3. Set output name (default is okay for basic setups)
  4. Input a passphrase for the key (OPTIONAL)
  5. Windows will now generate your RSA public/private key pair.
    1. Default location: %UserProfile%\.ssh (WINOWS)
    2. The public key will be stored as id_rsa.pub in the directory
  6. Upload the public key to your remote machine
    1. Navigate to folder, ~/.ssh/ on Linux device
    2. If ~/.ssh/authorized_keys exists, append the contents of id_rsa.pub to the next line.
    3. If it does not exist, simply upload id_rsa.pub and rename it to, authorized_keys
  7. DONE!

Future Features

  • Debugging: Launching of GUI apps for remote debugging
  • Debugging: PLink using PPK instead of manual password
  • Options Window: Multiple remote profile management
  • Options Window: SSH PPK generator assistant tool

Developers Wanted

Contributors and Q/A are welcomed!

To contribute, please pick off an item from the project or issue page. We'd love to hear your enhancement ideas as well.

References

Copyright 2024 Xeno Innovations, Inc.

vslinuxdebug's People

Contributors

clahil-linum avatar damiansuess avatar dependabot[bot] avatar hakanl avatar zeusware avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

vslinuxdebug's Issues

[2.0] Disable the switching of tabs during build

Whenever I'm building my solution VS2022 is always switching to the "Linux Debugger" output, which is very annoying, since I want to see the build output as it has all the details. I just want the Linux Debugger output when I'm actually deploying to the linux device.

Should "Build and Deploy" start the app automatically on my Linux target machine?

Not sure how else I could reach out to post a question but when I try both Build and Deploy, and Build, Deploy and Debug, am I expecting that the app will run automatically on my target linux machine? because it doesnt, I have to go to where the project was uploaded and do dotnet run to run it.

Would be cool if it runs by itself like I would run a Windows app, Android app on a phone, etc.

Thanks in advance, this tool is still super helpful to me. Nice work!

Duplicate log entries in "Linux Debugger" window

I have a solution with 5 projects (of which one is a Linux project). When I build I get the following in the "Linux Debugger" window (I cleared it before building):

20:44:45.311: Project: Sx1509\Sx1509.csproj --- Success: True

20:44:45.314: Project: Sx1509\Sx1509.csproj --- Success: True

20:44:45.315: Project: Sx1509\Sx1509.csproj --- Success: True

20:44:45.315: Project: Sx1509\Sx1509.csproj --- Success: True

20:44:45.322: Project: ..\..\MagnusEngine\MagnusEngine\MagnusEngine.csproj --- Success: True

20:44:45.322: Project: ..\..\MagnusEngine\MagnusEngine\MagnusEngine.csproj --- Success: True

20:44:45.322: Project: ..\..\MagnusEngine\MagnusEngine\MagnusEngine.csproj --- Success: True

20:44:45.322: Project: ..\..\MagnusEngine\MagnusEngine\MagnusEngine.csproj --- Success: True

20:44:45.329: Project: BusinessLogic\BusinessLogic.csproj --- Success: True

20:44:45.330: Project: BusinessLogic\BusinessLogic.csproj --- Success: True

20:44:45.330: Project: BusinessLogic\BusinessLogic.csproj --- Success: True

20:44:45.330: Project: BusinessLogic\BusinessLogic.csproj --- Success: True

20:44:45.882: Project: DMXCore100.Linux\DMXCore100.Linux.csproj --- Success: True

20:44:45.887: Project: DMXCore100.Linux\DMXCore100.Linux.csproj --- Success: True

20:44:45.887: Project: DMXCore100.Linux\DMXCore100.Linux.csproj --- Success: True

20:44:45.888: Project: DMXCore100.Linux\DMXCore100.Linux.csproj --- Success: True

20:44:56.985: Project: DMXCore100.Windows\DMXCore100.Windows.csproj --- Success: True

20:44:56.986: Project: DMXCore100.Windows\DMXCore100.Windows.csproj --- Success: True

20:44:56.986: Project: DMXCore100.Windows\DMXCore100.Windows.csproj --- Success: True

20:44:56.986: Project: DMXCore100.Windows\DMXCore100.Windows.csproj --- Success: True

20:44:57.023: Build was notsuccessful
20:44:57.024: Build was notsuccessful
20:44:57.024: Build was notsuccessful
20:44:57.024: Build was notsuccessful

But the build is successful, this is the output in the "Build" window:

Build started...
1>------ Build started: Project: DMXCore100.Windows, Configuration: Debug x64 ------
1>DMXCore100.Windows -> C:\Projects\DMXCore\100\Software\DMXCore100.Windows\bin\x64\Debug\net6.0-windows10.0.19041.0\DMXCore100.Windows.dll
========== Build: 1 succeeded, 0 failed, 4 up-to-date, 0 skipped ==========
========== Elapsed 00:11.732 ==========

I'm assuming the 4 "notsuccessful"(!) lines correspond to the 4 "up-to-date" projects?

[Enhancement] Start debugging with sudo

Description

I would be very nice to be able to start the debugging session with sudo when developing software that need to be run with admin rights. Often that ise the case when developing systemd services.

Right now I have to use the build and deploy and the use the DebugAdapterHost.Launch manually from the command window to do that. The only thing different from the generated launch.json and my manually written launch.json that works is the adapter args.

Generated: -ssh -pw password user@host -T ~/vsdbg/vs2022/vsdbg --engineLogging=./VSLinuxDbg/Application/_vsdbg.log
Needed: -ssh -pw password user@host -batch -T sudo ~/vsdbg/vs2022/vsdbg --engineLogging=./VSLinuxDbg/Application/_vsdbg.log

It would be nice to implement an option for this.

Specify folders to exclude and include when deploying

I noticed I have a runtimes folder on my Linux host (the files that are deployed with this extension) that includes binaries for 12 platforms including Windows and OSX. It would speed up deployment if I could exclude these from the deployment.
Also I have a folder for client-side web files (Spa) that sits in a shared project, I'd like these to be included (they aren't by default).

[vNext] Launch GUI After Deploying Package

WARNING: EXPERIMENTAL FEATURE

Research the experimental feature of the launching of uploaded packages via SSH.

From a standard SSH connection, the application can be launched. However, SSH.NET the process will start and then immediately close. This may be due to the closing SSH.NET connection after the command is sent.

[Issue] Unable to upload files

Description

I'm unable to upload my artefacts to the remote device. No problem to connect BUT there is no commands results, no folder created and nothing to push.

Severity (1-5)

4=Low (annoyance), 5=High (crashes visual studio)

Steps To Reproduce

Steps to reproduce the behavior:

  1. Click on Build and Deply
  2. Error message :
12:08:44.468: Build was successful
12:08:45.039: Connected to [email protected]:22 via SSH and SFTP
12:08:45.058: BASH> uname -m
12:08:45.081: BASH> which curl ; echo $?
12:08:45.102: BASH> [ -d /.vs-debugger/vs2022 ] || curl -ksSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /.vs-debugger/vs2022
12:08:45.177: Returned: 
12:08:45.177: BASH> mkdir -p 
12:08:45.196: BASH> sudo chown -R tiflex:tiflex 
12:08:45.250: BASH> rm -rf Ockham125/{*,.*}
12:08:45.268: BASH> mkdir -p Ockham125
12:08:45.289: Destination Tar.GZ: 'Ockham125/vsldBuildContents.tar.gz'
12:08:45.289: Getting bin files for transfer...
12:08:45.301: Compressing files for transfer...
12:08:53.588: Update file count: 181; File Size: [137163064 bytes] of Total Files: 181 [137163064 bytes] need to be updated
12:08:53.588: Uploading...
12:08:53.593: Error while uploading file. No such file
   à Renci.SshNet.Sftp.SftpSession.RequestOpen(String path, Flags flags, Boolean nullOnError)
   à Renci.SshNet.SftpClient.InternalUploadFile(Stream input, String path, Flags flags, SftpUploadAsyncResult asyncResult, Action`1 uploadCallback)
   à VsLinuxDebugger.Core.SshTool.<>c__DisplayClass22_0.<PayloadCompressAndUploadAsync>b__1()
   à System.Threading.Tasks.Task.Execute()
--- Fin de la trace de la pile à partir de l'emplacement précédent au niveau duquel l'exception a été levée ---
   à System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   à System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   à VsLinuxDebugger.Core.SshTool.<PayloadCompressAndUploadAsync>d__22.MoveNext()
12:08:53.593: BASH> set -e;cd "Ockham125";tar -zxf "vsldBuildContents.tar.gz"
12:08:53.610: Payload Decompress results: '' (blank=OK)
12:08:53.610: Upload completed with failure.

Dev environment:

Win 10.0.19044
VS2022:latest
LinuxDebugger: latest

Linux Distribution:

  • OS: Raspbian 64b / Debian
  • Version: 11 Bullseye

Error while uploading file

Description

Getting this error:

Error while uploading file. Object reference not set to an instance of an object.
   at VsLinuxDebugger.Core.SshTool.<>c__DisplayClass22_0.<PayloadCompressAndUploadAsync>b__1()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at VsLinuxDebugger.Core.SshTool.<PayloadCompressAndUploadAsync>d__22.MoveNext()

Severity (1-5)

1=Low (annoyance), 5=High (crashes visual studio)
4 - can't use the extension

Steps To Reproduce

Steps to reproduce the behavior:
I'm just clicking deploy and it happens during upload

Expected Behavior

The upload should be successful

Output

22:02:24.706: Project [success=True]: ..\..\MagnusEngine\MagnusEngine\MagnusEngine.csproj
22:02:24.801: Project [success=True]: BusinessLogic\BusinessLogic.csproj
22:02:25.451: Project [success=True]: DMXCore100.Linux\DMXCore100.Linux.csproj
22:02:25.475: Build was successful
22:02:31.411: Connected to [email protected]:22 via SSH and SCP
22:02:31.596: BASH> uname -m
22:02:31.615: BASH> which curl ; echo $?
22:02:31.631: BASH> [ -d ~/.vs-debugger/vs2022 ] || curl -ksSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/.vs-debugger/vs2022
22:02:31.645: Returned: 
22:02:31.645: BASH> mkdir -p ./VSLinuxDbg
22:02:31.662: BASH> sudo chown -R root ./VSLinuxDbg
22:02:31.702: BASH> rm -rf ./VSLinuxDbg/DMXCore100.Linux/{*,.*}
22:02:31.717: BASH> mkdir -p ./VSLinuxDbg/DMXCore100.Linux
22:02:31.732: Destination Tar.GZ: './VSLinuxDbg/DMXCore100.Linux/vsldBuildContents.tar.gz'
22:02:31.732: Getting bin files for transfer...
22:02:31.761: Compressing files for transfer...
22:02:52.276: Update file count: 572; File Size: [233597785 bytes] of Total Files: 572 [233597785 bytes] need to be updated
22:02:52.276: Uploading...
22:02:52.277: Error while uploading file. Object reference not set to an instance of an object.
   at VsLinuxDebugger.Core.SshTool.<>c__DisplayClass22_0.<PayloadCompressAndUploadAsync>b__1()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at VsLinuxDebugger.Core.SshTool.<PayloadCompressAndUploadAsync>d__22.MoveNext()
22:02:52.277: BASH> set -e;cd "./VSLinuxDbg/DMXCore100.Linux";tar -zxf "vsldBuildContents.tar.gz"
22:02:52.300: Payload Decompress results: '' (blank=OK)
22:02:52.300: Upload completed with failure.

Linux Distribution:

BalenaOS

Additional Context

Add any other context about the problem here.

[vNext] Manually attach to process using sudo/root

Unfortunately, I cannot use VsLinuxDebug to build, deploy and attach. Build and deploy works fine but it does not attach automatically and it's quite frustrating since I don't see any error message in the output window.

Your help is very much appreciated. I have to run several commands in my C# console app under administrative rights, e.g. modify the wpa_supplicant configuration etc... Therefore, I have to launch my console app using "sudo dotnet consoleapp.dll". Whenever I want to attach the debugger, It complains it also requires root previleges in order to attach.

*invalid issue, please delete*

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '...'
  3. Scroll down to '...'
  4. Error message '...'

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Linux Distribution:

  • OS: [e.g. Ubuntu, Debian, ...]
  • Version [e.g. 20.04 LTS, 22.04 LTS, ...]

Additional context
Add any other context about the problem here.

SSH Connection error since update 2.2.0

Description

Since the last extension update I cannot use the extension anymore. The code builds fine, but then I get the following error while connecting through SSH:

08:37:50.349: Build was successful
08:37:50.371: SSH configuring private key connection...
08:37:50.401: SSH connecting...
08:37:52.091: An error occurred during the build process. La valeur ne peut pas être null.
Nom du paramètre : connectionInfo
08:37:52.091: Failed to perform actions.

I updated Visual Studio to the latest version, but it did not help.

Severity (1-5)

4: Unusable extension, but no crash.

Steps To Reproduce

Steps to reproduce the behavior:

  1. Click on "Build and Deploy" in the Linux Debugger extension drop-down menu.

Expected Behavior

SSH upload working as before.

Configuration:

  • Local OS: Windows 10 22H2 Build19045.4170
  • Visual Studio: 2022 64bits Version 17.9.3
  • VS Linux Debugger: Version 2.2.0
  • Remote OS: Raspberry Pi 4 with Raspberry Pi OS Lite (64-bit). All packages updated.

Additional Context

It was working fine two weeks ago. I did not use Visual Studio since.
I am using an SSH key file as credentials (username/password not allowed).
I can connect to my remote device using Putty or powershell with the same SSH key file.

[Enhancement] Deploy only Diff

Description

If using many nuget packages at a project, the tar 'complete' project folder and transport it, unpack it, is quite time intensive.

In VS u have normaly the Commands
(i use german, so sorry if the translation is incorrect, but i guess u know waht i mean)

  • Clean Project
  • Clean Solution
  • Build Project
  • Re-Build Project
  • Build Solution
  • Re-Build Solution

on such Re-Build task it is not necessary to transport the whole stuff.

Options

B4 Re-Build iterate and Cache the FileInfo's with path, name and TimeStamp
After Re-Build check what's changed in the local build folder (compare with new iteration)
Just copy/delete to target the modificated files.

With best regards

Raspberry PI: launch: program '' does not exist, with workaround

Description

When launching the debugger, I get the error message [launch: program '' does not exist]
A crude fix is shown below.

Severity (1-5)

5 - Cannot debug

Steps To Reproduce

Generate a stock .net console app
Setup the device specific data in the VSLinuxDebug Setup (IP addr, username, pwd)
Launch the debugger
See error message
To fix this, I had to make sure the setting [.Net executable] points to the full [dotnet] pathname. See below:

image

The arrow points to the path detail I needed to add for my specific installation.
I tried this also: [export DOTNET_ROOT=/usr/lib/dotnet] but that did not solve the problem.

Expected Behavior

Hit breakpoint.

Screenshots

Not applicable

Linux Distribution:

  • OS: [Debian]

Live update of GUI project

I'm using the VsLinuxDebug extension to develop a Platform Uno project running on a Raspberry Pi. My workflow is that I build code in VS2022, make sure it runs under WinUI (switch start project), then I push it to my RPi for further testing/debugging. In this workflow it would be great with two features:

  1. Be able to control which project is pushed to my RPi. There are many times I've accidentally deployed the Windows project since I forgot to switch the startup. A setting would be good, or maybe a flag in the csproj that would skip my non-Linux project.
  2. In my current workflow I have to have a SSH open to the RPi (it has a screen, but no keyboard/mouse) and after each VsLinuxDebug deploy I kill the app (Ctrl-C in the SSH session) and then launch it again (dotnet App.dll). This works fine, but what would be very neat would be a form of "live push" where the app would be killed and re-launched automatically. I'm not sure exactly how this would be implemented. Or if VsLinuxDebug could launch the app after deploy (killing the previous instance), that way I wouldn't even need the SSH session (but I also use it to see console messages, so this is less of a need for my case.

Maybe it could be crafted with a "runner script" that would watch for modified files?

[Enhancement] Setting environment

Description

It would be nice if a DOTNET_ENVIRONMENT could be set to remote debug with different profiles.
Only thing needed in launch.json to support this is in the configuration where this could be added:

"env": {
  "DOTNET_ENVIRONMENT": "Development"
}

Unable to auto-attach to debugger

Hey, thank you very much for developing this plugin. I have encountered some problems in the process of using it and I hope to get your help. Here is my Linux system information and I have followed the tutorial to install dotnet、openssh-server and curl

Linux VM-0-16-ubuntu 4.15.0-159-generic #167-Ubuntu SMP Tue Sep 21 08:55:05 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

And this is the error message
image

The console output
image

The code is compiled locally.

[3.0] Optimize sync of project

My project is about 100 MB, it would be neat to have a way where VsLinuxDebug would only copy over modified files instead of delete and transfer the whole package.

[Issue] VSLinuxDebug no more working after recent VS updates

Hello, thanks for this great tool!

I noticed that starting from latest VS upgrades (last month) the tool is not working anymore.

When I do Extensions/Linux Debugger/Build and Deploy, on the "Linux Debugger" output window, after the "Build was successful" I get the error message "An error occurred during the build process. Unable to find method 'Void Renci.SshNet.PrivateKeyAuthenticationMethod..ctor(System.String, Renci.SshNet.PrivateKeyFile[])'"

It's crear to me thas something changed from the VS environment side, not from the tool side, because the problems started after a VS update. I already tried to uninstall/reinstall plugin, but nothing changed.

So I cloned the project to my PC, opened the solution in VS, updated all the NuGet packages, including the SSH.NET to the version 2024.0.0 (wich is the component I mind is raising the above error).

After the update some code started to not compile anymore.. but with a few corrections and a comment-out of

  public static void SetupConnection(ConnectionInfo connection)
  {
    //connection.HostKeyAlgorithms[RSA_SHA2_256] = data => new KeyHostAlgorithm(RSA_SHA2_256, new RsaKey(), data);
  }

(I don't know how to correct), the project now compile well, and the new .vsix package now works well and the Build and Deploy is successfully (also the debug!!)

Please, update the NuGet packages, correct the commented code (I'm not an expert in this) and release a new tools version for everyone's interested!

Thanks

[3.0] Add custom Profiles

Display Name of profile in VS Menu.

Each profile contains the following properties

  • Display Name (in menu)
  • IP or HostName
  • UserName : Password
  • Port Number

Sample

"Profiles"
    > ☑ "[UbuntuVM-22]"
    >   "[KaliLinux-Dev]"
"Build, Deploy"
"Build, Deploy, Debug"

[Enhance] Embed PLink into extension

As a user, I want to use PLink exclusively as it is the most reliable tool currently for debugging remotely. Windows' SSH will execute, however, it will sometimes not hit breakpoints.

Action Items:

  • Package PLink.exe or KLink.exe in project but not source control
  • Remove from Tools/Options "Path to PLink"
  • Add usage instructions

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.