Coder Social home page Coder Social logo

octoprint / octoprint-docker Goto Github PK

View Code? Open in Web Editor NEW
454.0 19.0 226.0 274 KB

The dockerized snappy web interface for your 3D printer!

License: GNU General Public License v3.0

Dockerfile 60.58% Shell 12.97% Makefile 26.45%
octoprint docker 3d-printing

octoprint-docker's Introduction

OctoPrint-docker Chat

This is the primary image of octoprint/octoprint. It is designed to work similarly, and support the same out of the box features as the octopi raspberry-pi machine image, using docker.

The octoprint/octoprint image uses semantic versioning, but the tags for octoprint/octoprint follow the version of octoprint contained in the image. As a result we recommend you always check the CHANGELOG or Releases before pulling an image, even if you are pulling the same tag.

You can subscribe to be notified of releases as well, by selecting the Watch button in the upper right corner, choosing "Custom", and checking "Releases".

In addition, we know that OctoPrint is not the best suited type of application for containerization, but we're working hard to make it as compatible as possible. Please check out our Roadmap, or join the discussion in the #dev-docker or #support-docker channels on the official OctoPrint Discord discord.octoprint.org.

Tags and platforms

All images for the octoprint/octoprint image are multi-arch images, and we publish for arm64, arm/v7, and amd64 using the below tags:

  • latest - will always follow the latest stable release
  • edge - will always follow the latest release including prereleases.
  • canary - follows the OctoPrint/Octoprint@maintenance branch
  • bleeding- follows the OctoPrint/Octoprint@devel branch
  • X,X.Y,X.Y.Z,X.Y.Z-rc - these tags will follow the latest build that matches the tag
  • minimal - This is built whenever latest is built, but uses the minimal image
  • latest|edge|canary|bleeding|X.Y.Z-minimal - a minimal version of each of the tags described above, published under the same condition but from the minimal image

Table of Contents

Usage

We recommend you use docker-compose to run octoprint via docker, and have included a recommended docker-compose.yml file for your convenience.

Save the contents of this file on your machine as docker-compose.yml, and then run docker-compose up -d.

Open octoprint at http://<octoprint_ip_or_url

Configuration

Enabling Webcam Support with Docker

In order to use the webcam, you'll need to make sure the webcam service is enabled. This is done by setting the environment variable ENABLE_MJPG_STREAMER=true in yourdocker run command, or in the docker-compose.yml file.

You'll also need to add --device /dev/video0:/dev/video0 to your docker run, or ensure it's listed in the devices array in your docker-compose.yml.

If you map a video device other than /dev/video0, you will additionally need to set an environment variable for CAMERA_DEV to match the mapped device mapping.

Make sure you use the following internal configuration (Settings ยป Webcam & Timelapse):

  • Stream URL: /webcam/?action=stream
  • Snapshot URL: http://localhost:8080/?action=snapshot
  • Path to FFMPEG: /usr/bin/ffmpeg

URLs for reaching the camera from outside the container are:

  • Stream: http://dockerIP:dockerport/webcam/?action=stream
  • Snapshot: http://dockerport:dockerport/webcam/?action=snapshot

See container Environment Variables for a full list of webcam configuration options configured with docker.

Container Environment Variables

There are configuration values that you pass using container --environment options. Listed below are the options and their defaults. These are implicit in example docker-compose.yml, and if you wish to change them, refer to the docker-compose docs on setting environment variables.

variable default description
CAMERA_DEV /dev/video0 The camera device(s) inside the container. (See Camera Devices note below)
MJPG_STREAMER_INPUT -n -r 640x480 params for mjpg-streamer
ENABLE_MJPG_STREAMER false enable or disable mjpg-streamer
AUTOMIGRATE false Will attempt to detect and migrate filesystems structures from previous versions of this image to be compatible with the latest release version. recommend you backup before trying this as this is a new feature that has been difficult to test fully
Camera Devices

You will still need to declare the device mapping in your docker-compose file or docker command, even if you explicitly declare the CAMERA_DEV. The value of CAMERA_DEV is used in starting the mjpg-streamer service, whereas the devices mapping is used by docker to make sure the container has access to the device.

For example, if you change the CAMERA_DEV to be /dev/video1, you would also need to map /dev/video1:/dev/video1 in your container.

Multiple Cameras

You may optionally provide a comma separated list of devices such as /dev/video0,/dev/video1 to map multiple devices. Remember to map them all to the container in the devices array.

MJPG Streamer will be started for each device, and the stream URL will be /webcam/<device_name>/?action=stream where <device_name> is the name of the device, e.g. video0.

Within the container the MJPG port will start at 8080 and increment for each device, e.g. 8080, 8081, 8082, etc.

Restarting OctoPrint

Whilst the container should be pre-configured to allow for OctoPrint to be restarted within the container, there are still some edge cases where this pre-configuration does not take effect. If the option to restart OctopPrint is not present in the user interface, ensure the following command is present in the Restart OctoPrint field under the Server section of the OctoPrint Settings.

s6-svc -r /var/run/s6/services/octoprint

Editing Config files manually

This docker-compose file also contains a container based instance of vscode, accessible via your browser at the same url as your octoprint instance, allowing you to edit configuration files without needing to login to your octoprint host.

To make use of this editor, just uncomment the indicated lines in your docker-compose.yml then run the following commands:

docker-compose up -d config-editor

Now go to http://<octoprint_ip_or_url>:8443/?folder=/octoprint in your browser to edit your octoprint files! Use the 'explorer' (accessible by clicking the hamburger menu icon) to explore folder and files to load into the editor workspace.

All configuration files are in the octoprint folder, and the active configuration will be accessible at /octoprint/octoprint/config.yaml

When you're done, we recommend you stop and remove this service/container:

docker-compose stop config-editor && docker-compose rm config-editor

For full documentation about the config editor, see the docs for the product at github.com/cdr/code-server.

Without docker-compose

If you prefer to run without docker-compose, first create an octoprint docker volume on the host, and then start your container:

docker volume create octoprint
docker run -d -v octoprint:/octoprint --device /dev/ttyACM0:/dev/ttyACM0 --device /dev/video0:/dev/video0 -e ENABLE_MJPG_STREAMER=true -p 80:80 --name octoprint octoprint/octoprint

Extended Documentation

We are in the process of creating more extensive documentation for using the octoprint/octprint image. Check out the docs

If you would like to build the docker image yourself, please read building-an-octoprint-image

Contributions Welcome

We are welcoming contributions, and looking to add maintainers to the team. View CONTRIBUTING.md for more info!

octoprint-docker's People

Contributors

adelton avatar armicron avatar artmorse avatar ax42 avatar badsmoke avatar bruvv avatar cociweb avatar cp2004 avatar drizuid avatar foosel avatar gaetancollaud avatar galenguyer avatar koosc avatar kuchel77 avatar kylegordon avatar longlivechief avatar m1xzg avatar pfeerick avatar redparchel avatar rpwoodbu avatar sammcj avatar shift avatar ssmale avatar tbhova avatar technologywin avatar torresmvl avatar toyvo avatar zyao89 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  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  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  avatar  avatar  avatar  avatar

Watchers

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

octoprint-docker's Issues

Restore configuration is broken

After last version I noted that the restore configuration feature is failing giving this message

Uploading backup, this can take a while. Please wait... Restoring from backup... Unpacking backup to /tmp/tmpb1oodoad...UnpackedRenaming /root/.octoprint to /root/.octoprint.bck...Removing temporary unpacked folderError while running restoreTraceback (most recent call last):  File "/usr/local/lib/python3.8/shutil.py", line 788, in move
    os.rename(src, real_dst)OSError: [Errno 16] Device or resource busy: '/root/.octoprint' -> '/root/.octoprint.bck'
During handling of the above exception, another exception occurred:Traceback (most recent call last):  File "/usr/local/lib/python3.8/site-packages/octoprint/plugins/backup/__init__.py", line 907, in _restore_backup
    shutil.move(basedir, basedir_backup)  File "/usr/local/lib/python3.8/shutil.py", line 800, in move
    rmtree(src)  File "/usr/local/lib/python3.8/shutil.py", line 719, in rmtree
    onerror(os.rmdir, path, sys.exc_info())  File "/usr/local/lib/python3.8/shutil.py", line 717, in rmtree
    os.rmdir(path)OSError: [Errno 16] Device or resource busy: '/root/.octoprint' Restore failed! Check the above output and octoprint.log for reasons as to why.Removing temporary zip

I also noted that the user of the container in the previous version was octoprint vs root in last version

Permission denied for home.config.yaml

Not sure how to fix this error
Trying to follow this video also https://www.youtube.com/watch?v=Y-2o0gyGB4c

Starting octoprint-docker_octoprint_1 ... done
Attaching to octoprint-docker_octoprint_1
octoprint_1 | 2020-03-26 18:47:51,628 - octoprint.settings - ERROR - Error while saving config.yaml!
octoprint_1 | Traceback (most recent call last):
octoprint_1 | File "/opt/venv/lib/python2.7/site-packages/OctoPrint-1.4.0-py2.7.egg/octoprint/settings.py", line 1388, in save
octoprint_1 | self._dirty = False
octoprint_1 | File "/usr/local/lib/python2.7/contextlib.py", line 24, in exit
octoprint_1 | self.gen.next()
octoprint_1 | File "/opt/venv/lib/python2.7/site-packages/OctoPrint-1.4.0-py2.7.egg/octoprint/util/init.py", line 1105, in atomic_write
octoprint_1 | shutil.move(temp_config.name, filename)
octoprint_1 | File "/usr/local/lib/python2.7/shutil.py", line 325, in move
octoprint_1 | copy2(src, real_dst)
octoprint_1 | File "/usr/local/lib/python2.7/shutil.py", line 153, in copy2
octoprint_1 | copyfile(src, dst)
octoprint_1 | File "/usr/local/lib/python2.7/shutil.py", line 97, in copyfile
octoprint_1 | with open(dst, 'wb') as fdst:
octoprint_1 | IOError: [Errno 13] Permission denied: u'/home/octoprint/.octoprint/config.yaml'
octoprint_1 | 2020-03-26 18:47:51,629 - octoprint.startup - CRITICAL - Could not initialize settings manager: [Errno 13] Permission denied: u'/home/octoprint/.octoprint/config.yaml'
octoprint_1 | 2020-03-26 18:47:51,629 - octoprint.startup - CRITICAL - There was a fatal error starting up OctoPrint.
octoprint_1 | Could not initialize settings manager: [Errno 13] Permission denied: u'/home/octoprint/.octoprint/config.yaml'
octoprint_1 | There was a fatal error starting up OctoPrint.
octoprint-docker_octoprint_1 exited with code 0

Here is my docker-compose.yaml

version: '3.7'

volumes:
octoprint-docker:

services:
octoprint:
image: octoprint/octoprint
ports:
- 5001:5001
# devices:
# - /dev/ttyACM0:/dev/ttyACM0
volumes:
- octoprint-docker:/home/octoprint/.octoprint

Add gpio access and control

On a test of the OctoPrint PSU control plugin via docker, I believe it is necessary to enable and document a few things related to using GPIO for our users.

The following 2 docker container settings are required for gpio dependent plugins to work:

  • cap-add SYS_RAWIO
  • --device /dev/mem

Additionally, the gpio device tree needs to be made writeable, and any pins that are controlled by octoprint plugins need to have corresponding INPUT/OUTPUT modes set, and the pin number registered in the /sys/class/gpio device tree.

This can be done manually with the gpio ubuntu package available from the wiringpi package in the default ubuntu registry.

I'm not sure if these plugins use this program, as I can't remember if I had to set things up manually the first time I installed PSU Control on a normal Octopi Raspbian image.

Tasks:

  • determine best practice for adding gpio channel manipulation so that the users have easiest possible experience when using plugins that utilize gpio controls
  • add any updates needed to Dockerfile or entrypoint script based on the determined best practice
  • determine if gpio plugins will automatically control pin mode if the appropriate system devices and packages are present, or if these need to be set manually

Documentation:

  • add docs about any necessary host commands/configs
  • docker-compose settings for cap_add and --device /dev/mem
  • any downstream Dockerfile changes needed to support gpio control if turns out that's the best practice

mjpg steamer issues and thoughts

Hello,

I'm curious about thoughts here for mjpg steamer. If there is interest I can do a pull request towards whichever option folks think would be best. I also include instructions for where I got stuck trying to use the info badsmoke provided.

First off thanks @badsmoke for all you did. That said I had some issues with the instructions as is which made it a bit tricky for me coming at it not knowing mjpg at all.

The first problem I ran into is the Dockerfile didn't specify a default command. Which meant that it took some digging into mjpg to figure out what I had to pass it. I realize a default command might not make sense given the diversity of webcams, but an example command with a working one for the rpi and a link to a helpful page about mjpg would be helpful.

Additionally the image at docker.io/badsmoke/mjpg-streamer:1.0.0 specifically is incompatible with docker on the RPI giving 'exec format error' when run. I believe this simply means that the image doesn't support arm. I've not done a multi-architecture build before, but this is what it needs to not require rebuilding the docker image.

If there is interest I can do docker best practices on it (build it in a multistage build etc to make it smaller, do the git pull within the Dockerfile, do multi-architecture etc.) and do a pull request. I'm not certain if you want that in this repo though as it will be dependent on https://github.com/jacksonliam/mjpg-streamer. I could also see if they are interested in a pull request.

The other option which might be best if there is interest in having this in the same container is I could attempt to add it to the current image similar to how ffmpeg is installed perhaps with an environmental variable to enable it or not and configure the command sent to it to start rather than having it be part of the entrypoint/cmd.

In the meantime for anyone stuck on this here are some working instructions for pi-camera on a tested on a RPI4 running the up to date Raspbian Buster image:

Enable the camera with raspi-config (or google for command line method) and reboot.

git clone https://github.com/jacksonliam/mjpg-streamer
cd mjpg-streamer/mjpg-streamer-experimental

Edit the Dockerfile:

FROM ubuntu:18.04

RUN apt-get update && apt-get install git cmake make build-essential libjpeg-dev imagemagick subversion libv4l-dev checkinstall lib\
jpeg8-dev libv4l-0 gcc g++ -y

RUN git clone https://github.com/jacksonliam/mjpg-streamer.git

WORKDIR /mjpg-streamer/mjpg-streamer-experimental

COPY . .
RUN make USE_LIBV4L2=true clean all
RUN make install
RUN cat docker-start.sh
RUN chmod +x docker-start.sh
EXPOSE 8080/TCP
ENTRYPOINT ["/mjpg-streamer/mjpg-streamer-experimental/docker-start.sh"]
CMD ["output_http.so -w ./www", "input_uvc.so"]

Working docker command (the command is the same as specified as the default in the dockerfile in this case):

docker run -d --device /dev/video0 -e "ENV_FPS=5" -e "ENV_RESOLUTION=1280x720" -p 8080:8080 mjpg-streamer:local "output_http.so -w ./www", "input_uvc.so"

Other work: This doesn't include running mjpg with the "input_raspi.so" which I think would be better. I don't think this is hard, but given it is already working with input_uvc.so I went with that for now. I'm not certain what the advantages to using input_raspi.so would be for this use case.

README updates for tag list

Update README to include a list of docker tags, similar to that you would find on all the official images on docker hub.

Each tag should link back to the respective Dockerfile (at the moment they're all built from the same one)

Feaure: support --user flag

Think we add UID and GID to the docker container and use those UID/GID to run the container this will allow better control the user / groups that is running this, the moment I am having to build this into a custom container.

any interest in a PR for this? I can pull one together.

Document ways to backup using docker

related #92

Regardless of if we wind up implementing support of the bundled backup restore plugin, we should document a recommended/alternative method for backups of both the container and the volume using docker commands.

Update docker image to at least 1.3.11

The official releases are at version 1.3.12rc3, the current docker version (probably) is 1.3.9, which is >10 months old. Release candidates might be too much work to keep up with but at least the latest stable version should be available as a docker image. If work is done on that new images should probably feature more precise tags as noted in #12.

I wasn't able to test further because this image is not ARM-ready as noted in #6, this completely disqualifies this image for me as I am using a Raspberry Pi, which seemed like it's the recommended platform, at least for the installed builds.

I switched to https://hub.docker.com/r/nunofgs/octoprint for now but will happily switch to an official build once it is usable.

failes ffmpeg url

Please check:

--2018-05-17 09:43:27--  https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-32bit-static.tar.xz
Resolving johnvansickle.com (johnvansickle.com)... 162.222.226.121
Connecting to johnvansickle.com (johnvansickle.com)|162.222.226.121|:443... connected.
HTTP request sent, awaiting response... 404 Not Found
2018-05-17 09:43:29 ERROR 404: Not Found.

replace in Dockerfile with:
https://ffmpeg.org/releases/ffmpeg-4.0.tar.bz2

Stop using latest tag for betas/dev/rc version

Heya,

pls dont use the docker latest tag for dev versions.

add a second tag like dev/beta/deveoper which points to beta versions and update latest tag only if its a stable release.

the easiest way would be to use github releases which triggers travis.
if "rc" is in release name, update dev tag
else update latest tag.

Add gcc to build stage image

The current final build stage image doesn't include gcc, and some plugins fail to build wheel as a result. Example from psu control

----------------------------------------
ERROR: Failed building wheel for RPi.GPIO
Successfully built OctoPrint-PSUControl
Failed to build RPi.GPIO
Installing collected packages: RPi.GPIO, OctoPrint-PSUControl
Running setup.py install for RPi.GPIO: started
Running setup.py install for RPi.GPIO: finished with status 'error'
ERROR: Command errored out with exit status 1:
command: /opt/venv/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-ijUMuU/RPi.GPIO/setup.py'"'"'; __file__='"'"'/tmp/pip-install-ijUMuU/RPi.GPIO/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-eUNrZj/install-record.txt --single-version-externally-managed --compile --install-headers /opt/venv/include/site/python2.7/RPi.GPIO
cwd: /tmp/pip-install-ijUMuU/RPi.GPIO/
Complete output (16 lines):
running install
running build
running build_py
creating build
creating build/lib.linux-aarch64-2.7
creating build/lib.linux-aarch64-2.7/RPi
copying RPi/__init__.py -> build/lib.linux-aarch64-2.7/RPi
creating build/lib.linux-aarch64-2.7/RPi/GPIO
copying RPi/GPIO/__init__.py -> build/lib.linux-aarch64-2.7/RPi/GPIO
running build_ext
building 'RPi._GPIO' extension
creating build/temp.linux-aarch64-2.7
creating build/temp.linux-aarch64-2.7/source
gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/opt/venv/include/python2.7 -c source/py_gpio.c -o build/temp.linux-aarch64-2.7/source/py_gpio.o
unable to execute 'gcc': No such file or directory
error: command 'gcc' failed with exit status 1
----------------------------------------
ERROR: Command errored out with exit status 1: /opt/venv/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-ijUMuU/RPi.GPIO/setup.py'"'"'; __file__='"'"'/tmp/pip-install-ijUMuU/RPi.GPIO/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-eUNrZj/install-record.txt --single-version-externally-managed --compile --install-headers /opt/venv/include/site/python2.7/RPi.GPIO Check the logs for full command output.
Error!
Could not parse output from pip, see plugin_pluginmanager_console.log for generated output

The goal should be to make the final build stage image as lean as possible for true docker implementation, however I'm betting a significant portion of our userbase will probably just use docker as a means of install and not otherwise be docker users... so I think it would be best to go ahead and include some build essentials in the final build stage.

We should also build a minimal image for those more advanced users who will use this as a base image. (Will open a different issue for that)

Build fails due to MarkupSafe alpha version

Hey there, great project!

I just forked this repository to include some plugins in my own version of the docker image, but sadly the build fails because of the automated version match to a recently released alpha version of MarkupSafe:

2020-04-12T19:03:10.5517666Z #67 137.2 Searching for MarkupSafe
2020-04-12T19:03:10.5517891Z #67 137.2 Reading https://pypi.org/simple/MarkupSafe/
2020-04-12T19:03:10.6521002Z #67 137.3 Downloading https://files.pythonhosted.org/packages/e0/bf/acc45baeb2d7333ed724c30af188640d9cb0be4b28533edfc3e2ae5aad72/MarkupSafe-2.0.0a1.tar.gz#sha256=beac28ed60c8e838301226a7a85841d0af2068eba2dcb1a58c2d32d6c05e440e
2020-04-12T19:03:10.8019546Z #67 137.4 Best match: MarkupSafe 2.0.0a1
2020-04-12T19:03:10.8022385Z #67 137.4 Processing MarkupSafe-2.0.0a1.tar.gz
2020-04-12T19:03:10.8022918Z #67 137.4 Writing /tmp/easy_install-xEbjlb/MarkupSafe-2.0.0a1/setup.cfg
2020-04-12T19:03:11.8209608Z #67 138.5 Running MarkupSafe-2.0.0a1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-xEbjlb/MarkupSafe-2.0.0a1/egg-dist-tmp-1RkEL_
2020-04-12T19:03:12.0705031Z #67 138.8 Traceback (most recent call last):
2020-04-12T19:03:12.1802607Z #67 138.8   File "setup.py", line 277, in <module>
2020-04-12T19:03:12.1802930Z #67 138.8     setup(**params())
2020-04-12T19:03:12.1804851Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/__init__.py", line 145, in setup
2020-04-12T19:03:12.1805150Z #67 138.8     return distutils.core.setup(**attrs)
2020-04-12T19:03:12.1805409Z #67 138.8   File "/usr/local/lib/python2.7/distutils/core.py", line 151, in setup
2020-04-12T19:03:12.1805811Z #67 138.8     dist.run_commands()
2020-04-12T19:03:12.1806084Z #67 138.8   File "/usr/local/lib/python2.7/distutils/dist.py", line 953, in run_commands
2020-04-12T19:03:12.1806363Z #67 138.8     self.run_command(cmd)
2020-04-12T19:03:12.1806755Z #67 138.8   File "/usr/local/lib/python2.7/distutils/dist.py", line 972, in run_command
2020-04-12T19:03:12.1807548Z #67 138.8     cmd_obj.run()
2020-04-12T19:03:12.1808891Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/install.py", line 67, in run
2020-04-12T19:03:12.1809202Z #67 138.8     self.do_egg_install()
2020-04-12T19:03:12.1810201Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/install.py", line 117, in do_egg_install
2020-04-12T19:03:12.1810499Z #67 138.8     cmd.run(show_deprecation=False)
2020-04-12T19:03:12.1811040Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 424, in run
2020-04-12T19:03:12.1811190Z #67 138.8     self.easy_install(spec, not self.no_deps)
2020-04-12T19:03:12.1811559Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 666, in easy_install
2020-04-12T19:03:12.1811710Z #67 138.8     return self.install_item(None, spec, tmpdir, deps, True)
2020-04-12T19:03:12.1812115Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 713, in install_item
2020-04-12T19:03:12.1812269Z #67 138.8     self.process_distribution(spec, dist, deps)
2020-04-12T19:03:12.1812935Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 758, in process_distribution
2020-04-12T19:03:12.1813098Z #67 138.8     [requirement], self.local_index, self.easy_install
2020-04-12T19:03:12.1813461Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/pkg_resources/__init__.py", line 782, in resolve
2020-04-12T19:03:12.1813605Z #67 138.8     replace_conflicting=replace_conflicting
2020-04-12T19:03:12.1813965Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/pkg_resources/__init__.py", line 1065, in best_match
2020-04-12T19:03:12.1814114Z #67 138.8     return self.obtain(req, installer)
2020-04-12T19:03:12.1814464Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/pkg_resources/__init__.py", line 1077, in obtain
2020-04-12T19:03:12.1814783Z #67 138.8     return installer(requirement)
2020-04-12T19:03:12.1815187Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 685, in easy_install
2020-04-12T19:03:12.1815343Z #67 138.8     return self.install_item(spec, dist.location, tmpdir, deps)
2020-04-12T19:03:12.1815723Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 711, in install_item
2020-04-12T19:03:12.1815871Z #67 138.8     dists = self.install_eggs(spec, download, tmpdir)
2020-04-12T19:03:12.1816246Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 896, in install_eggs
2020-04-12T19:03:12.1816399Z #67 138.8     return self.build_and_install(setup_script, setup_base)
2020-04-12T19:03:12.1816786Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 1164, in build_and_install
2020-04-12T19:03:12.1816944Z #67 138.8     self.run_setup(setup_script, setup_base, args)
2020-04-12T19:03:12.1817323Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/command/easy_install.py", line 1150, in run_setup
2020-04-12T19:03:12.1817470Z #67 138.8     run_setup(setup_script, args)
2020-04-12T19:03:12.1818100Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 253, in run_setup
2020-04-12T19:03:12.1818250Z #67 138.8     raise
2020-04-12T19:03:12.1818370Z #67 138.8   File "/usr/local/lib/python2.7/contextlib.py", line 35, in __exit__
2020-04-12T19:03:12.1818513Z #67 138.8     self.gen.throw(type, value, traceback)
2020-04-12T19:03:12.1818900Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 195, in setup_context
2020-04-12T19:03:12.1819038Z #67 138.8     yield
2020-04-12T19:03:12.1819166Z #67 138.8   File "/usr/local/lib/python2.7/contextlib.py", line 35, in __exit__
2020-04-12T19:03:12.1819304Z #67 138.8     self.gen.throw(type, value, traceback)
2020-04-12T19:03:12.1820065Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 166, in save_modules
2020-04-12T19:03:12.1820224Z #67 138.8     saved_exc.resume()
2020-04-12T19:03:12.1820617Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 141, in resume
2020-04-12T19:03:12.1821082Z #67 138.8     six.reraise(type, exc, self._tb)
2020-04-12T19:03:12.1821490Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 154, in save_modules
2020-04-12T19:03:12.1821632Z #67 138.8     yield saved
2020-04-12T19:03:12.1821981Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 195, in setup_context
2020-04-12T19:03:12.1822120Z #67 138.8     yield
2020-04-12T19:03:12.1822576Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 250, in run_setup
2020-04-12T19:03:12.1822725Z #67 138.8     _execfile(setup_script, ns)
2020-04-12T19:03:12.1823319Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/sandbox.py", line 45, in _execfile
2020-04-12T19:03:12.1823520Z #67 138.8     exec(code, globals, locals)
2020-04-12T19:03:12.1823969Z #67 138.8   File "/tmp/easy_install-xEbjlb/MarkupSafe-2.0.0a1/setup.py", line 56, in <module>
2020-04-12T19:03:12.1824208Z #67 138.8     "semantic_version>=2.8,<2.9",
2020-04-12T19:03:12.1824712Z #67 138.8   File "/tmp/easy_install-xEbjlb/MarkupSafe-2.0.0a1/setup.py", line 43, in run_setup
2020-04-12T19:03:12.1825035Z #67 138.8     "werkzeug>=0.16,<0.17",
2020-04-12T19:03:12.1825603Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/__init__.py", line 145, in setup
2020-04-12T19:03:12.1825752Z #67 138.8     return distutils.core.setup(**attrs)
2020-04-12T19:03:12.1826028Z #67 138.8   File "/usr/local/lib/python2.7/distutils/core.py", line 124, in setup
2020-04-12T19:03:12.1826237Z #67 138.8     dist.parse_config_files()
2020-04-12T19:03:12.1826909Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/_virtualenv.py", line 21, in parse_config_files
2020-04-12T19:03:12.1827502Z #67 138.8     result = old_parse_config_files(self, *args, **kwargs)
2020-04-12T19:03:12.1828423Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/dist.py", line 702, in parse_config_files
2020-04-12T19:03:12.1828579Z #67 138.8     ignore_option_errors=ignore_option_errors)
2020-04-12T19:03:12.1828960Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/config.py", line 121, in parse_configuration
2020-04-12T19:03:12.1829086Z #67 138.8     meta.parse()
2020-04-12T19:03:12.1829424Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/config.py", line 426, in parse
2020-04-12T19:03:12.1829568Z #67 138.8     section_parser_method(section_options)
2020-04-12T19:03:12.1829921Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/config.py", line 399, in parse_section
2020-04-12T19:03:12.1830062Z #67 138.8     self[name] = value
2020-04-12T19:03:12.1830411Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/config.py", line 184, in __setitem__
2020-04-12T19:03:12.1830564Z #67 138.8     value = parser(value)
2020-04-12T19:03:12.1830923Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/config.py", line 515, in _parse_version
2020-04-12T19:03:12.1831073Z #67 138.8     version = self._parse_attr(value, self.package_dir)
2020-04-12T19:03:12.1831424Z #67 138.8   File "/opt/venv/lib/python2.7/site-packages/setuptools/config.py", line 350, in _parse_attr
2020-04-12T19:03:12.1831564Z #67 138.8     value = getattr(module, attr_name)
2020-04-12T19:03:12.1832261Z #67 138.8 AttributeError: 'module' object has no attribute '__version__'
2020-04-12T19:03:13.3233056Z #67 ERROR: executor failed running [/bin/sh -c python setup.py install]: buildkit-runc did not terminate sucessfully

I did not investigate where this dependency comes from, but it would probably be a good idea to pin dependency versions per release to known and working versions (if this is not a transitive dependency).

[Synology/DSM] Permission denied config files

I tried this image docker in a Synology NAS x86 with DSM 6.0

My docker-compose.yml:

version: '2'
services:
octoprint:
build: .
image: octoprint/octoprint
container_name: octoprint
ports:
- 6001:5000
devices:
- /dev/ttyUSB0:/dev/ttyUSB0
volumes:
- /volume3/docker/Octoprint/config:/home/octoprint/.octoprint

I use port 6001 because 5000 is in use by DSM.

I can ran docker-compose up -d without problems but then the containers stopped and this is the log:
octoprint_log.zip

date stream content
2019-01-18 20:45:24 stderr There was a fatal error starting up OctoPrint.
2019-01-18 20:45:24 stdout 2019-01-18 20:45:24,714 - octoprint.startup - CRITICAL - There was a fatal error starting up OctoPrint.
2019-01-18 20:45:24 stdout 2019-01-18 20:45:24,714 - octoprint.startup - CRITICAL - Could not initialize settings manager: [Errno 13] Permission denied: '/home/octoprint/.octoprint/config.yaml'
2019-01-18 20:45:24 stdout IOError: [Errno 13] Permission denied: '/home/octoprint/.octoprint/config.yaml'
2019-01-18 20:45:24 stdout with open(dst, 'wb') as fdst:
2019-01-18 20:45:24 stdout File "/usr/local/lib/python2.7/shutil.py", line 97, in copyfile
2019-01-18 20:45:24 stdout copyfile(src, dst)
2019-01-18 20:45:24 stdout File "/usr/local/lib/python2.7/shutil.py", line 144, in copy2
2019-01-18 20:45:24 stdout copy2(src, real_dst)
2019-01-18 20:45:24 stdout File "/usr/local/lib/python2.7/shutil.py", line 316, in move
2019-01-18 20:45:24 stdout shutil.move(temp_config.name, filename)
2019-01-18 20:45:24 stdout File "/opt/octoprint/venv/lib/python2.7/site-packages/OctoPrint-1.3.9-py2.7.egg/octoprint/util/init.py", line 896, in atomic_write
2019-01-18 20:45:24 stdout self.gen.next()
2019-01-18 20:45:24 stdout File "/usr/local/lib/python2.7/contextlib.py", line 24, in exit
2019-01-18 20:45:24 stdout self._dirty = False
2019-01-18 20:45:24 stdout File "/opt/octoprint/venv/lib/python2.7/site-packages/OctoPrint-1.3.9-py2.7.egg/octoprint/settings.py", line 1318, in save
2019-01-18 20:45:24 stdout Traceback (most recent call last):
2019-01-18 20:45:24 stdout 2019-01-18 20:45:24,658 - octoprint.settings - ERROR - Error while saving config.yaml!
2019-01-18 20:45:24 stderr Could not initialize settings manager: [Errno 13] Permission denied: '/home/octoprint/.octoprint/config.yaml'

The problem is that the containers can't write in the volume of DSM.
If I delete the line : - /volume3/docker/Octoprint/config:/home/octoprint/.octoprint` from docker-compose.yml all works fine but when ran docker-compose down I lost all configuration.

I tried adding PUID and PGID variables but I have the same result.

With others dockers images like jackket, sonnar or raddar with the PUID and PGID I can write in the volumen without problems.

README updates for multi-arch usage

  • Update README to include multi-arch image information (just pull and you will automatically get the right image for your architecture)
  • create table with supported architecture

Add an option to disable mjpg_streamer

When running this image without a camera connected and configured properly, the service just crashes repetitively.

There's should be a flag to disable it.

In nunofgs's image there's the MJPEG_STREAMER_AUTOSTART envvar that disables the service, see here.

octoprint/octoprint image cannot connect to /dev/tty*

OS: NixOS

Steps to reproduce:

  1. Start docker-compose file with /dev/ttyUSB0 (or any other serial port) connected
  2. Try connecting to a printer

It might be because udev rules don't work inside base image you are using.

Anyways โ€” current Dockerfile adds octoprint user to dialout group, which only works if udev does. /dev/tty* device files have sudo group, and the easiest fix is to add octoprint to sudo group.

no "/webcam" address support in camera versions

This is more of a PSA than a bug report, I wanted to let other people know how to make a webcam work.

From my experience with some other (non-official) docker images, as well as octopi, the standard octorprint address for a video stream, given a properly connected usb webcam is '/webcam/?action=stream'

This format currently does not work with this image. However, ':8080/?action=stream' does work.

I've looked at the files and settings related to the camera and mjpg-streamer. I can't really find any reference that would replace /webcam/.

Build or install ffmpeg in a way that consistently succeeds

The current build downloads static releases of ffmpeg from https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-$ARCH-static.tar.xz.

This has become inconsistent in its availability, sometimes succeeding and then subsequently failing mere minutes later, causing failures between workflow stages.

see #66 where it clearly shows all checks passed, yet if you go to the log for those jobs, where I re-ran them a few minutes later, those same jobs failed.

Create CHANGELOG

Create a CHANGELOG.md file that gets updated with specific instructions tips on what is changing from release to release (of images, not OctoPrint)

Permisions on /dev/ttyACM0

I mapped ttyACM0 to the container by "--device /dev/ttyACM0:/dev/ttyACM0".
ttyACM0 is present in the container as follows:
"crw-rw---- 1 root root 166, 0 Mar 24 00:42 ttyACM0"

Is seems as OctoPrint hasn't the right permissions to access ttyACM0 as I'm getting the following error on my attempt to connect.

Connecting to: /dev/ttyACM0
Changing monitoring state from 'Offline' to 'Error: Connection error, see Terminal tab'
Unexpected error while connecting to serial port: /dev/ttyACM0 OSError: '[Errno 13] Permission denied: '/dev/ttyACM0'' @ comm.py:_openSerial:1931 (hook default)

Pretty much lost with octoprint docker

Hey! I'm quite lost setting up my printer on octoprint.
I've got one Synology DS2018+ with Docker (with UI) and one octoprint instance runing on it.

Octoprint server is up and running and I could see any Serial Port at first so I set the octoprint instance with high privilege which mounted the ports /dev/ttyS0 -> /dev/ttyS3 and /dev/ttyUSB0

none of the port are working and I've that same error message like with the USB0 :

Unexpected error while connecting to serial port: /dev/ttyUSB0 SerialException: '[Errno 13] could not open port /dev/ttyUSB0: [Errno 13] Permission denied: '/dev/ttyUSB0'' @ comm.py:_openSerial:2691 (hook default)

Any help would be much appreciated!

raspicam 2.1 parameters

I have installed the docker container with compose and everything works except my raspberry cam.
I think it's my fault because I don't know the richt settings:
in compose:
/dev/ttyACM0:/dev/ttyACM0

in the container settings:
CAMERA_DEV:/dev/ttyACM0
CAMERA_DEV:MJPEG_STREAMER_INPUT -y -n -r 640x48

but can't get it working. what do I wrong?

Incorrect folder mount in README

README states -v ./config:/home/octoprint/.octoprint as a required mount but the Dockerfile lists VOLUME /home/octoprint instead. Mounting /home/octoprint as per the Dockerfile seems to cause execution permission errors on my system. Changing to VOLUME /home/octoprint/.octoprint in the Dockerfile fixes these issues.

Pull Request

armhf: "./config" includes invalid characters for a local volume name

Banana Pi M2 Zero with Armbian Bionic. Getting error message when running $ docker run -d -v ./config:/home/octoprint/.octoprint --device /dev/ttyUSB0:/dev/ttyUSB0 -p 5000:5000 --name octoprint badsmoke/octoprint

Error message
Unable to find image 'badsmoke/octoprint:latest' locally
latest: Pulling from badsmoke/octoprint
fff3167bf8c7: Pull complete 
e3a57370d71f: Pull complete 
1857b6e06e34: Pull complete 
513b7f3d59a2: Pull complete 
8a98ddf64c8c: Pull complete 
1fb6bcc7332c: Pull complete 
48070cb75b7e: Pull complete 
562ad4447872: Pull complete 
2c4cf33fc7ad: Pull complete 
6c177655d5dc: Pull complete 
5bac32922e23: Pull complete 
8dde1731c6cf: Pull complete 
0ac2c6c2dc76: Pull complete 
af429de8f6ef: Pull complete 
4b84157043c4: Pull complete 
304a48c84d30: Pull complete 
Digest: sha256:9e5e79aebd25ff19594cbecaad8f8bd66b67bc58844bfec3f30be7b0f112d1a3
Status: Downloaded newer image for badsmoke/octoprint:latest
docker: Error response from daemon: create ./config: "./config" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed. If you intended to pass a host directory, use absolute path.
See 'docker run --help'.

Plugins are not persisted

If a plugin is installed, and the container is recreated, then the previously installed plugins will be lost. Would volumizing /opt/venv/lib/python2.7/site-packages/ be the correct way to fix this?

Settings are not persisted between restarts

Hello,

I am using the octoprint-docker image with the default docker-compose file, which specifies a volume.

When starting octoprint with the command:
docker-compose up -d
it starts up normally and I am greeted with the setup.

If I stop it using:
docker-compose down,
it stops gracefully.

Upon restarting it again, I am greeted with the same setup that I have completed earlier and all my settings are lost. What am I doing wrong? Thanks!

Investigate/Implement support of backup-restore plugin

Determine what changes could be made to enable support of the backup-restore bundled plugin.

  • Need to identify what problems exist with current implementation (users have reported permissions errors)

  • identify changes that could be made to allow restore to work without errors

  • determine any caveats to those changes due to docker ecosystem

  • implement changes if severity of caveats is low

reference discord conversation start: https://discordapp.com/channels/704958479194128507/744267928559550566/760421775925444658

Q: How do I disable mjpg-streamer

Question/Problem Description

I need to disable mjpg-streamer (or atleast its output) since i don't want to use a webacam and my log gets spamed with:

MJPG-streamer [11840]: TV-Norm...........: DEFAULT
 i: init_VideoIn failed

if It's possible via an environment variable it would be great.
I tried to identify the process but all i get is:

root@octoprint:/mjpg/mjpg-streamer-master/mjpg-streamer-experimental# ps -e                                                     
  PID TTY          TIME CMD                                                                                                     
    1 pts/0    00:00:00 s6-svscan                                                                                               
   34 pts/0    00:00:00 foreground                                                                                              
   35 pts/0    00:00:00 s6-supervise                                                                                            
   44 pts/0    00:00:00 foreground                                                                                              
  184 pts/0    00:00:00 s6-supervise                                                                                            
  191 ?        00:00:00 haproxy                                                                                                 
  205 pts/0    00:00:11 octoprint                                                                                               
 6655 pts/1    00:00:00 bash                                                                                                    
 6978 pts/0    00:00:02 s6-supervise                                                                                            
13260 pts/1    00:00:00 ps  

System Details

I'm running octoprint on a Synology Discstation 218+


Include the output of `docker version` (and `docker-compose` version if using compose):

Client:
 Version:           18.09.8
 API version:       1.39
 Go version:        go1.11
 Git commit:        bfed4f5
 Built:             Fri Mar 13 06:46:11 2020
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.09.8
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.11
  Git commit:       3a371f3
  Built:            Fri Mar 13 06:44:35 2020
  OS/Arch:          linux/amd64
  Experimental:     false

Describe your hardware

see above

fix deploy stage of travis.yml

Deploy currently doesn't work because of the disconnect between stages. (Oh Travis how I hate thee).

If you have a deploy travis by default runs it on every script (oh travis how i hate thee), which means we need to figure out a way to do CI with merge requests that won't overwrite the main tags for octoprint/octoprint until it's in master.

Inspirations for the solution can be take from both node's approach with docker and travis, as well as the moby/buildkit project's approach.

One thing to note though, is that both of the travis configs for those projects seem to use deprecated techniques for deploy, and the new syntax for travis deploy's actually makes it harder (oh travis how I hate thee)

[Feature] Make plugins persistent

Describe the bug
Recommended docker compose file at https://github.com/OctoPrint/octoprint-docker/blob/master/docker-compose.yml won't work under the latest docker compose. Can also be easily fixed and improved to support permanent plugins, between images.

To Reproduce
Steps to reproduce the behavior:

  1. Curl locally https://github.com/OctoPrint/octoprint-docker/blob/master/docker-compose.yml
  2. execute docker-compose up --detach
  3. Docker error will fire immediately, as
    ports:
    • 80:80
      is expecting a string.

Expected behavior

  1. Docker container won't start, due to some minor sintax issue.

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

Desktop (please complete the following information):

docker --version
Docker version 19.03.13, build 4484c46

docker-compose  --version
docker-compose version 1.27.4, build unknown

cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian

new suggested docker-compose.yml

version: '2.4'

services:
  octoprint:
    container_name: octoprint
    image: octoprint/octoprint
    restart: unless-stopped
    ports:
    - "80:80"
    devices:
    - /dev/ttyUSB0:/dev/ttyUSB0
    # - /dev/ttyACM0:/dev/ttyACM0
    # - /dev/video0:/dev/video0
    volumes:
     - octoprint:/octoprint

    # This volume will make the plugin permanent during restarts or image upgrade
     - octoprint-packages:/usr/local/lib/python3.8/sitepackages

    # uncomment the lines below to ensure camera streaming is enabled when
    # you add a video device
    #environment:
    #  - ENABLE_MJPG_STREAMER=true

  ####
  # uncomment if you wish to edit the configuration files of octoprint
  # refer to docs on configuration editing for more information
  ####

#config-editor:
  #  image: linuxserver/code-server
  #  ports:
  #    - 8443:8443
  #  depends_on:
  #    - octoprint
  #  restart: unless-stopped
  #  environment:
  #    - PUID=0
  #    - GUID=0
  #    - TZ=America/Chicago
  #  volumes:
  #    - octoprint:/config

#below directives will create a volume only if does not exist already
volumes:
  octoprint:
    external: false
  octoprint-packages:
    external: false

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.