Code Monkey home page Code Monkey logo

guet's Introduction

guet

enable contribution tracking when pair programming with guet

Pair programming is integral part of many software development lifecycles. When pairing, you may want to track each committer's contributions. Using guet enables that functionality without changing the normal git workflow.

Install

To include it in your project:

npm install guet -D

To install globally:

npm install -g guet

Usage

Below are the available commands. For more detailed usage documentation, run guet <command> --help.

Command Description
init start using guet in the current repository
add add committers for use in pairing
set set the current committers in the current repository
get get information about guet committers
remove remove a committer by its initials
yeet remove guet configurations
hook apply guet modifications to the current git commit

In pre-existing hooks.

guet can easily integrate with other hook managing systems. In each of pre-commit, commit-msg, and post-commit within .git/hooks/ you'll want to add each of the following commands respectively

// .git/hooks/pre-commit
npx guet hook pre-commit

// .git/hooks/commit-msg
npx guet hook commit-msg

// .git/hooks/post-commit
npx guet hook post-commit

guet's People

Contributors

chiptopher avatar ctobolski avatar dependabot[bot] avatar jonnynabors avatar pyroan avatar

Stargazers

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

Watchers

 avatar

guet's Issues

Adding comitters before initializing guet throws error log

Bug report

When trying to add a committer before running guet init, an unhelpful error log is thrown.

Actual behavior?

guet add cb "chris" chris@localhost
Traceback (most recent call last):
  File "/usr/local/bin/guet", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.6/dist-packages/guet/main.py", line 23, in main
    executor.execute(sys.argv[1:])
  File "/usr/local/lib/python3.6/dist-packages/guet/executor.py", line 26, in execute
    command.execute()
  File "/usr/local/lib/python3.6/dist-packages/guet/commands/addcommitter.py", line 42, in execute
    self._user_gateway.add_user(self._args[1], self._args[2], self._args[3])
  File "/usr/local/lib/python3.6/dist-packages/guet/gateways/gateway.py", line 184, in add_user
    if self.get_user(initials):
  File "/usr/local/lib/python3.6/dist-packages/guet/gateways/gateway.py", line 197, in get_user
    self._connection = sqlite3.connect(self._connection_path)
sqlite3.OperationalError: unable to open database file

Expected behavior?

Something helpful like

guet add cb "chris" chris@chris

guet has not been initialized yet! Please do so by running the command "guet init".

Environment:

Ubuntu 18.04.2

Notes

It might be helpful to include something like this for all commands that have to be ran after guet init.

Bad error message when adding user before initializing

Bug report

When attempting to guet add without having done guet init, it prints a stack trace of the saying the database cannot be accessed.

Actual behavior?

(venv) โžœ  ~ guet add us "name" email@localhost
Traceback (most recent call last):
  File "/home/user/workspace/guet/venv/bin/guet", line 11, in <module>
    load_entry_point('guet==1.0.0', 'console_scripts', 'guet')()
  File "/home/user/workspace/guet/venv/lib/python3.6/site-packages/guet-1.0.0-py3.6.egg/guet/main.py", line 22, in main
  File "/home/user/workspace/guet/venv/lib/python3.6/site-packages/guet-1.0.0-py3.6.egg/guet/executor.py", line 26, in execute
  File "/home/user/workspace/guet/venv/lib/python3.6/site-packages/guet-1.0.0-py3.6.egg/guet/commands/addcommitter.py", line 42, in execute
  File "/home/user/workspace/guet/venv/lib/python3.6/site-packages/guet-1.0.0-py3.6.egg/guet/gateway.py", line 108, in add_user
  File "/home/user/workspace/guet/venv/lib/python3.6/site-packages/guet-1.0.0-py3.6.egg/guet/gateway.py", line 121, in get_user
sqlite3.OperationalError: unable to open database file

Expected behavior?

An error message saying something to the effect of:
You must initialize guet before adding committers!

Working with Husky

Actual behavior?

Installing Guet is incompatible with another dependency that also modifies your git hooks

Expected behavior?

Guet plays nicely with husky

Print helpful common error messages out to terminal

Feature Request

Right now guet prints all errors to an error file. It would be a lot more convenient in the case of common errors (for example, if I'm not in a directory with a .git file) for guet to just print out a nice error message directly.

Description

Description of the feature, and why it might be beneficial.

Example

Example of feature in use in the following format:

$ (in directory with no .git)
$ guet set <initials>
$ OLD: An error has occurred, please refer to error logs (~/.guet/errors) for more information
$ NEW: You must be in a directory with a .git file to set committers.

I think there are probably several more error cases where a similar approach would be really useful. I don't like having to look at the log file.

Initialize in repo with pre-existing commit hooks

Feature Request

When initializing guet in a repo that already has a pre-commit hook, put it both in a sub folder and replace it with a script that calls both.

Optional user story format:

Context

guet is unusable for projects that already have a pre-commit hook.

Acceptance Criteria

When there is a folder structure like this:

/.git
  |- /hooks
       | - pre-commit

calling guet start will produce something like this:

/.git
  |- /hooks
       | - pre-commit
       | - /pre-commit-hooks
             | - guet-pre-commit
             | - old-pre-commit

Call unexpected errors unexpected

Feature Request

Description

When an unexpected error occurs, it just says an error occurred. Instead, it should describe the error was "unexpected" so the user isn't confused why guet doesn't handle the error.

Example

Example of feature in use in the following format:

$ guet some command that causes an error
An unexpected error has occurred, and been logged at ~/.guet/errors. Please include these logs if reporting an issue.

Ignore initials case

Feature Request

Description

When adding a committer the case of the initials should be set to all lower case and ignored in inputs.

Example

$ guet add IN name email
$ guet get committers
All committers
in - name <email>

Path to error file in error message

Feature Request

When there is an error in a guet command, the error message An error has occurred, please refer to error logs for more information is printed to the console. It should also list the path to the errors file, which is ~/.guet/errors.

Acceptance Criteria

~/.guet/errors is included in the error message in some capacity.

Unhelpful error message when set committers using unknown initials

Bug report

When calling guet set with initials not in the system it prints out a stack trace.

Actual behavior?

If there isn't a committer with the initials fi currently in the system, and a user calls guet set fi, then this is what the system will print:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/bin/guet", line 11, in <module>
    sys.exit(main())
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/guet/main.py", line 22, in main
    executor.execute(sys.argv[1:])
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/guet/executor.py", line 26, in execute
    command.execute()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/guet/commands/setcommitters.py", line 37, in execute
    committers.append(CommitterInput(name=committer.name, email=committer.email))
AttributeError: 'NoneType' object has no attribute 'name'

Expected behavior?

It should say something like this:

$ guet set fi
No committer with the initials "fi".

Environment:

OS X with python 3.

guet set runs in non-git folders

Bug report

Calling guet set from folder that isn't a git project still succeeds.

Actual behavior?

In /path/to/projectA calling guet set cb will add cb,1579969290283,/path/to/projectA/.git even though no /path/to/projectA/.git folder exists.

Expected behavior?

An error method should appear that says something to the effect of git not initialized in this directory., similar to how it does for running guet start in a non-git initialized directory.

Doesn't work where python 2 is default

Bug report

When I go to commit on a fresh install in OSX mojave, it throws an error, saying it can't import a certain module.

Actual behavior?

It throws an error.

Expected behavior?

It doesn't throw an error.

Environment:

Python 2 on OSX mojave

Add committers local to a repository

Feature Request

Description

Right now, committers are saved globally in a machine in the ~/.guet/committers file. For example, if three committers are all remote, each committer would have to add everyone to their machine. Instead it would be cool if committers could be saved locally to a repository so remote teams only had to set up committers once.

Example

Below outlines what I would expect a normal workflow to be.

guet add n1 "Name1" [email protected]
guet start
guet add --local n2 "Name2" [email protected]
guet set n1 n2

The outcomes would be:

  1. add n1 "Name1" [email protected] to the global ~/.guet/committers file
  2. start the repository for guet tracking
  3. add n2 "Name2" [email protected] to a .guet/committers folder that it creates at the root of your repository
  4. set the committers to Name1 and Name2

Another thing to consider is a situation where a local name overlaps a global name. Imagine a workflow like below

guet add n1 "Global Name1" [email protected]
guet add n2 "Global Name2" [email protected]
guet start
guet add --local n1 "Local Name1" [email protected]
guet set n1 n2

In this case, The committers would be Local Name1 and Global Name2 because local names take precedence over global names.

Catch bad commands

Feature Request

When calling a guet command that doesn't exist, guet will error out. For example, guet fake will cause an error because guet fake is not a valid command. Instead, it should print out the guet usage docs.

Acceptance Criteria

$ guet fake
The command "fake" is not recognized. Please refer to the bellow doumentaiton on how to
use guet:

usage: guet [commad] [args...]
....

Tests can pass if setup incorrectly

Bug report

When running the tests, if they're set up incorrectly but there is a .guet folder present on the system, the tests can sometimes still pass.

Force set of pairs after a timeout

Feature Request

The pre-commit hook should stop you if you haven't paired in a certain amount of time.

Optional user story format:

Context

guet if you haven't changed pairs in a while, you could accidentally commit under an incorrect set of names.

Acceptance Criteria

When you git commit after a set period of time, you get a message like this and stop committing.

$ git commit
You haven't git paired recently. Please set the current pairs.

Notes

The default time to expire should be nine hours.

guet cannot find git repo if not called from project root

Bug report

In a situation where a guet hook is called in a way that the current working directory isn't the root of the project, it will fail to add the Co-Authored by messages to the commit.

Actual behavior?

Co-Authored by is not being added when the script is ran outside of the root directory.

Expected behavior?

guet should be able to walk up the directory graph until until it finds the .git folder. For example:

project/
   .git/
   ui/
      --> running guet here

Running guet from the ui folder should still be able to find the .git folder.

guet get -h returns message with a mispelled option

Bug report

guet get -h tells user that "comitters" is a valid command, but it is not. "committers" is the correct valid command it should be listing. This caused me confusion when I tried using "comitter" as a command and it didn't work.

Actual behavior?

guet get -h returns this message:

usage: guet get <identifier> [-flag, ...]

Get currently set information.

Valid Identifier

	current - lists currently set committers
	comitters - lists all committers

Flags
	l  -  Print values as truncated list

"comitters" is misspelled in this message, and it is not a valid command.

Expected behavior?

guet get -h returns this message:

usage: guet get <identifier> [-flag, ...]

Get currently set information.

Valid Identifier

	current - lists currently set committers
	committers - lists all committers

Flags
	l  -  Print values as truncated list

Note that "comitters" has been changed to "committers", which is the valid command.

Environment:

Python 3.8.2
Guet 2.3.4

Warn user when set committers and commit committers differ

Feature Request

Description

When appending a commit message with the intention of changing the authors, one might do something like this:

$ guet set initials1 initials2
$ git commit -m "Initial commit"
$ guet set initials2 initials3
$ git commit --amend

However, if they don't supply the --reset-author flag to the git commit, the author will remain initials1. It would be nice if a warning that told them there is a mismatch between the currently set committers and the committers on the commit.

Example

$ guet set initials1 initials2
$ git commit -m "Initial commit"
$ guet set initials2 initials3
$ git commit --amend
The guet committers are set to "initials2 initials3", but the most recently amended
commit has the committers set to "initials1 initials2". To reset the author of the
commit you must include the --reset-author command. 

Associate guet set with a repo

Feature Request

When running guet set, the initials set should be scoped to the git repo they've be used in.

Context

Right now, if you have guet initialized on multiple repos on once machine, a guet set in one will set the current committer for all the other ones. Instead, guet set should only affect the current repository.

Notes

As far as implementation is concerned, the .guet/committersset file is in charge of keeping track of what the current committers are. In follows a format like this:

cb,aw,1578846792059

where the initials are related to a committer, and the last number is the millisecond timestamp that the set was done. To accomplish this, something like this could be done:

cb,aw,1578846792059,/absolute/path/to/worspace/project1/.git
ts,cs,1578846792059,/absolute/path/to/worspace/project2/.git

'guet get'

Feature Request

Optional user story format:

As a developer
I want to be able to query Guet for the currently set pair
So that I can know whether a 'guet set' worked the way I expected.

Current Behavior

Tanmay: Why did this commit from you come in as me?
Me: I don't know. I did 'guet set cs mw'. You're 'ts'.
Tanmay: -_-

Desired Behavior:

Tanmay: Why did this commit from you come in as me?
Me: I don't know. Let me check what pair guet has set.
Me: checks Oh. It's still set to you for some reason.
Tanmay: -_-

Include linting in workflow

Feature Request

Description

Get linters in order, and then include it in the workflow.

Notes

Right now there's a pylint and yapf configuration. One of them should be chosen.

Don't commit if you haven't set committers yet

Feature Request

Don't let commits succeed without having called guet set in a repository.

Acceptance Criteria

Committing in a repo where guet start has happened but guet set hasn't been done will stop you with an error message letting you know that you have to commit first.

Notes

Related to #17

guet --version

Feature Request

Running guet --version/-v should print the current version of guet.

Context

It can be hard to find the current version of guet installed without a flag to do such.

Acceptance Criteria

Running guet --version/-v prints the current version of guet.

Guet errors out when hooks directory isn't present in .git folder

Bug report

Guet errors out when you run guet start and there isn't a .git/hooks directory

Actual behavior?

Produces an error

Expected behavior?

guet creates the hooks directory

Environment:

OSX 10.15.4 / Python 3.7.6

Notes

stacktrace:
File "/usr/local/lib/python3.7/site-packages/guet/util/errors.py", line 9, in wrapper wrapped() File "/usr/local/lib/python3.7/site-packages/guet/main.py", line 57, in main command.execute() File "/usr/local/lib/python3.7/site-packages/guet/commands/strategies/strategy_command.py", line 11, in execute self.strategy.apply() File "/usr/local/lib/python3.7/site-packages/guet/commands/usercommands/start/create_hook_strategy.py", line 6, in apply self.git.create_hooks() File "/usr/local/lib/python3.7/site-packages/guet/git/git.py", line 88, in create_hooks hook.save() File "/usr/local/lib/python3.7/site-packages/guet/git/hook.py", line 62, in save write_lines(self.path, self.content) File "/usr/local/lib/python3.7/site-packages/guet/files/write_lines.py", line 22, in write_lines return _write_lines_to_path(path, lines) File "/usr/local/lib/python3.7/site-packages/guet/files/write_lines.py", line 18, in _write_lines_to_path path.write_text(text) File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 1235, in write_text with self.open(mode='w', encoding=encoding, errors=errors) as f: File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 1203, in open opener=self._opener) File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/pathlib.py", line 1058, in _opener return self._accessor.open(self, flags, mode) FileNotFoundError: [Errno 2] No such file or directory: '/Users/jcrouc15/workspace/conference-call-bingo/.git/hooks/pre-commit'

upgrading doesn't work with old guet sets

Bug report

When upgrading guet versions to the most recent version (2.3.2), trying to run guet will put guet in a broken state.

Actual behavior?

Running a guet set on 2.3.2 will cause the committersset file to be in the following state:

n1,n2,1581036719234
n1,n2,1581036724199,/root/test-env/project1/.git

Then trying to use any of the other commands results in an error like this:

Traceback (most recent call last):
  File "/usr/local/lib/python3.7/site-packages/guet-2.3.2-py3.7.egg/guet/util/errors.py", line 9, in wrapper
    wrapped()
  File "/usr/local/lib/python3.7/site-packages/guet-2.3.2-py3.7.egg/guet/hooks/pre_commit.py", line 14, in pre_commit
    _fail_if_past_valid_timeframe()
  File "/usr/local/lib/python3.7/site-packages/guet-2.3.2-py3.7.egg/guet/hooks/pre_commit.py", line 26, in _fail_if_past_valid_timeframe
    set_time = most_recent_committers_set()
  File "/usr/local/lib/python3.7/site-packages/guet-2.3.2-py3.7.egg/guet/config/most_recent_committers_set.py", line 13, in most_recent_committers_set
    return int(set_time)
ValueError: invalid literal for int() with base 10: 'n2'

Expected behavior?

guet should be capable of ignoring committersset that are out of date.

Notes

Ideally, it should handle as though they've never guet set in a particular repo if they've only ever used an out of date version.

guet set should error when no guet start has been ran

Feature Request

Warn users when they are running guet set in a directory that hasn't had guet start ran in it.

Optional user story format:

Context

Right now, guet set is blocked in a repository that doesn't have a git repository. It should also print an error message when guet start hasn't been ran in the repository.

Acceptance Criteria

$ guet set cb aw
guet has not been initialized in this repository.

Error files contain newlines after every character

Bug report

Actual behavior?

Error logs are printed with newlines after every character, making them unreadable

Expected behavior?

Error logs should be readable

Environment:

macOS Mojave 10.14.6, Python 3.7.4

Examples

T
r
a
c
e
b
a
c
k
 
(
m
o
s
t
 
r
e
c
e
n
t
 ...

Initial commit uses system default author

Bug report

If the first commit commit comes after the most recent guet set, then the initial commit to the repository will use the system default credentials.

Actual behavior?

In the below example, the system default for git is a user named system user with an email of system@localhost. The pair on this project is user1 <email1> and user2 <email2>. In the first commit, the Author field should read user1 <email1>, but reads system user <system@localhost>.

This comes about because guet set u1 u2 was called before the inital git commit was called. The inner workings of guet set set the author for the current repository, so since that never happens the system default is used.

commit 98b7eef392b195c7d92f3b730a70a42a3a5471a4 (HEAD -> master)
Author: user1 <email1>
Date:   Sun Nov 4 18:44:23 2018 -0500

    C
    
    Co-authored-by: user1 <email1>
    Co-authored-by: user2 <email2>

commit eaaee03b6328d50968a0a7298d8ae7a5d8d70343
Author: user2 <user1>
Date:   Sun Nov 4 18:44:05 2018 -0500

    B
    
    Co-authored-by: user2 <emal2>
    Co-authored-by: user1 <email1>

commit 49caecab600a02012cca28e68e2a07a4bade81ff
Author: system user <system@localhost>
Date:   Sun Nov 4 18:43:40 2018 -0500

    A
    
    Co-authored-by: user1 <email1>
    Co-authored-by: user2 <email2>

Expected behavior?

The Author field on the first commit should be user1 <email1>.

Multiple guet sets

Feature Request

Make it so that guet set can be used simultaneously in multiple repos on one machine.

Context

guet set can only keep track of one set of committers at a time.

Acceptance Criteria

One could independently guet set in projectA and projectB without conflict.

guet --help doesn't print help

Bug report

Actual behavior?

When running guet --help or guet -h it will fail to print the help message.

Expected behavior?

When running guet --help or guet -h it should print a help message.

Lower cyclomatic complexity constraint to 3

The cyclomatic complexity constraint is currently set to five, but setting it to three might lead to some cleaner code in certain situations.

This is in the .pylint file.

max-complexity=5 -> max-complexity=3

Include comment explaining guet in local config

Feature Request

Description

When doing a guet add --local a local .guet directory is created. It would be nice if a configuration file was added to folder detailing some information about guet.

Example

$ guet add --local cb "chris boyer" [email protected]
$ ls .guet
committers  config
$ cat config
# Created using guet. Learn more at https://guet.sh
# Version 2.4.1

Throw error is hooks are malformed

Feature Request

Description

When someone upgrades from a pre 2.0 version, they can encounter an error where guet hooks complain that they can't work. Usually, it comes from the fact that the hook has a python shebang instead of a python3 shebang.

Ideally some of the commands would check if the hooks are malformed and throw an error.

Example

$ guet set initials1 initials2
Your guet hooks are incorrectly formatted. Please use guet start to recreate them.

Have the committer/author rotate between pairs

Feature Request

Make it so that when pairing, every commit will swap who the user is that is committing and authoring.

Optional user story format:

Context

Some older GitHub systems, or things like GitLab don't recognize the way that guet keeps track of multiple committers, so rotating people will give people equal credit.

Acceptance Criteria

When Johnny, Jone, and Jess are mobbing, on commit A, Johnny gets the committer/author credit with Jone and Jess appearing as co-authors; on commit B, Jone gets the committer/author credit with Johnny and Jess appearing as co-authors; on commit C, Jess gets the committer/author credit, with Johnny and Jone appearing as co-authors.

Deploy script

Feature Request

Create a script for deploying to pypi.

Optional user story format:

Context

I do the deploy so infrequently that it would be nice if I could write it out to a script to keep track of it.

Acceptance Criteria

$ ./scripts/deploy
... 
(does the deploy)

"Must set pairs again" message is not emphatic or specific enough

I would like it to be more clear when a 'git commit' command fails because guet pairs aren't set.

Environment:

Flabs pairing station

Context

As a guet user
I want it to be more obvious when guet prevents a commit
So that I can notice and do what I need to do to commit

Acceptance Criteria

Given that I have guet configured on a Git repository
When I use 'git commit' without having used 'guet set' recently
Then my failure message is very clear and emphatic about the fact a commit wasn't made

Better warning message for when guet set is called in a non-git folder

Feature Request

Description

Calling guet set in a folder that doesn't have a .git folder causes an the global error message to appear. Issue #29 addresses this issue. In the meantime, an error message that describes the situation would be good.

Example

# in the root directory
$ cd api/
$ guet set n1 n2
You are not in a directory with a git folder. Change back
to your project's root directory.

guet set is scoped to a repo

Feature Request

I'd like to scope setting committers to a repo.

Optional user story format:

Context

Currently when you guet set committers, it'll set those committers for all repositories on the machine.

Acceptance Criteria

When I do guet set in one repo, committing in another repo won't use the committers from the first repo.

Run end to end tests in parallel

Enginnering

Run the end to end tests in parallel using this command unittest-parallel --start-directory e2e -t . This should decrease the runtime of the pipeline.

guet does not care about rebase -> reword

Bug report

guet does not handle 'git rebase' as a tool for making revisions.

Actual behavior?

The author of a commit seems unaffected by a 'git rebase -i' that chooses to 'reword' that commit.

Expected behavior?

I would expect that upon rewording several consecutive commits with guet set to a pair of committers through each rewording, the commits would be split between the two committers as main author.

Environment:

Mac OS X Ford edition, Python 3.7.4

P.S I realize this may be a huge ask; just recording the case!

Don't overwrite committers without warning.

Feature Request

When you guet add a new committer, if the initials match initials that are already being tracked they will be overwritten. It should ask you to confirm if you want to overwrite them.

Acceptance Criteria

A workflow like this would be preferable:

$ guet add cb "chris boyer" [email protected]
$ guet add cb "chad bower" [email protected]
Matching initials. Adding "chad bower" <[email protected]> will
overwrite "chris boyer" <[email protected]>. Would you like to
continue (y) or cancel (x)?

Not started error message edit

Bug report

The error message for when a repository hasn't started tracking with guet is like this:
guet not initialized in this repository. Please use guet start to initialize repository for use with guet.

The guet start should be in quotations to match the style of other similar error messages.

Catch all erros and log them

Feature Request

When an error occurs, rather than just logging the stack trance to the console, it'd be cool if it caught the error and sent it to a log file.

Optional user story format:

Context

It's a bit obtuse to the user when they see something like they do in #5.

Acceptance Criteria

When the user encouters an error, they should be presented with a message like this:

It looks like you have encountered an error. This is probably a problem
with the software and not with you. If you could please open an issue,
that would be greatly appreciated. More information, including the stack
trace can be found at ~/.guet/errors.log

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.