GithubHelp home page GithubHelp logo

cloudogu / ces-build-lib Goto Github PK

View Code? Open in Web Editor NEW
73.0 12.0 48.0 1.22 MB

Jenkins pipeline shared library adding features for Maven, Gradle, Docker, SonarQube, Git and others

License: MIT License

Groovy 100.00%
jenkins jenkins-pipeline maven git ces java sonarqube docker gradle

ces-build-lib's Issues

Support "main" production branch

The release mechanism should work with production branches called either master or main. Until now it only supports master branches.

findIp() without container object

Make findIp() also run with a container ID String instead of a container Object.

e.g. overloaded (if possible)

  • String findIp(String containerId)
  • String findIp(container)

Or duck typed, if String use as ID, otherwise fall back to container.id.

MavenInDocker: Check if image is already built and reuse it

Less log cluttering on jenkins. Faster builds.

Note: Add a version of the image (like, 1, 2, 3, 4) so a rebuilt can be forced when the dockerfile is changed.

Example to illustrate basic concept:

private static final int DOCKER_FILE_VERSION=1

// ...
    def mvn(String args) {
// ....
if (!dockerImagePresent("ces-build-lib/maven/$dockerImageVersion-$DOCKER_FILE_VERSION")) {
 script.docker.build("ces-build-lib/maven/$dockerImageVersion-$DOCKER_FILE_VERSION", createDockerfilePath())
}

When writeDockerFile() is changed, DOCKER_FILE_VERSION is increased by one, forcing a rebuild of the archive.

Docker.Image.mountJenkinsUser fails when Build executor runs in a container

Problem when using MavenInDocker and running maven like mvn 'install' the following error occurred:

[Pipeline] withDockerContainer
Jenkins seems to be running inside container 6a11e2323ec0b313a455baedc5b28a35dffce026656884298aba4f58149a3b14
$ docker run -t -d -u 1000:1000 -v /var/jenkins_home/workspace/ature_3_continuous_delivery-6HRXLXYING3RPQ2T3ATY6DOVMNEXY7ZFTGPCHQBMB7KHPI2V5JBQ/.jenkins/etc/passwd:/etc/passwd:ro -w /var/jenkins_home/workspace/ature_3_continuous_delivery-6HRXLXYING3RPQ2T3ATY6DOVMNEXY7ZFTGPCHQBMB7KHPI2V5JBQ --volumes-from 6a11e2323ec0b313a455baedc5b28a35dffce026656884298aba4f58149a3b14 -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** --entrypoint cat maven:3.5.0-jdk-8
[Pipeline] // withDockerContainer
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
java.io.IOException: Failed to run image 'maven:3.5.0-jdk-8'. 
Error: docker: Error response from daemon: oci runtime error: container_linux.go:247: starting container process caused "process_linux.go:359: container init caused \"rootfs_linux.go:54:
mounting 
/var/jenkins_home/workspace/ature_3_continuous_delivery-6HRXLXYING3RPQ2T3ATY6DOVMNEXY7ZFTGPCHQBMB7KHPI2V5JBQ/.jenkins/etc/passwd 
to rootfs 
/var/lib/docker/aufs/mnt/7b8aadc86829e9cbc74d8e03555a06bdd67d00865f671ac777638cd94bfe2fe9 
at 
/var/lib/docker/aufs/mnt/7b8aadc86829e9cbc74d8e03555a06bdd67d00865f671ac777638cd94bfe2fe9/etc/passwd 
caused not a directory: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type.

SonarQube: Provide option to Analyze without SQ Plugin

It's also possible to do SQ analyses without having the Jenkins SQ Plugin installed.

See here for example.

In ces-build-lib, the SonarQube class just needs to set following params:

  • sonar.host.url
  • Credentials. Either
    • sonar.login as secret text credential or
    • sonar.login & sonar.password as username and password credential
  • SONAR_MAVEN_GOAL we could use sonar:sonar

SonarQube Preview Mode deprecated

Leads to failing builds for PRs.

The analysis mode parameter is deprecated because it has been replaced by the branch analysis functionality provided in Developer Edition($).
Yes, Developer Edition is commercial/paid,

https://groups.google.com/forum/#!topic/sonarqube/4bzwxkqJGAc

So, which options does that leave for ces-buid-lib - which is supposed to work with free edition?

First idea: Analyze PRs into new Projects, similar to the behavior for branches.
If the Branch Plugin is present we could analyse it into a Branch.

More Docker Utils

void extractFromImageFileSystem(def imageName, String imagePath, String targetPath) {
    sh "tempContainer=\$(docker create '${imageName}') && " +
       "docker cp \${tempContainer}:/${imagePath} '${targetPath}' && " +
       "docker rm \${tempContainer}"
}

Git: Clone without parameters leds to RejectedAccessException

Initializing the git object with credentials and later on try to clone a repository providing only the url runs into an error.
Executing the 'containsKey' method on a string does not result in a MissingMethodException on jenkins but into a RejectedAccessException.

Source

def git = git = script.cesBuildLib.Git.new(script, helmConfig.credentialsId)
// do some other stuff

// finally provide a url and clone runs into the given issue below
git helmConfig.repositoryUrl
org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use method groovy.lang.GroovyObject invokeMethod java.lang.String java.lang.Object (org.codehaus.groovy.runtime.GStringImpl containsKey java.lang.String)
	at org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.StaticWhitelist.rejectMethod(StaticWhitelist.java:270)
	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:159)
	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:142)
	at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:161)
	at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:165)
	at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:17)
	at com.cloudogu.ces.cesbuildlib.Git.git(Git.groovy:52)
	at com.cloudogu.ces.cesbuildlib.Git.call(Git.groovy:40)
	at com.cloudogu.gitopsbuildlib.deployment.helm.repotype.GitRepo.getHelmChartFromGitRepo(GitRepo.groovy:35)
	at ___cps.transform___(Native Method)
	at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:86)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:113)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:83)
	at sun.reflect.GeneratedMethodAccessor92.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
	at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
	at com.cloudbees.groovy.cps.Next.step(Next.java:83)
	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:174)
	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:163)
	at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:129)
	at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:268)
	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:163)
	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18)
	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:51)
	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:185)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:400)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.access$400(CpsThreadGroup.java:96)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:312)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:276)
	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:136)
	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:28)
	at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:59)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

SonarQube/Git: Branch Name works only in multi-branch pipelines

env.BRANCH_NAME is only present in multi-branch pipeline builds, regular pipelines don't have information about branches.

Affected methods:

  • Git.getBranchName()
  • SonarQube.initMavenForRegularAnalysis()

We could use git rev-parse --abbrev-ref HEAD in Git class, instead. SonarQube could use Git class. Keeps everything DRY.

When fixed, update README.

Create first release

So it can be properly referenced when included in Jenkinsfiles.

Workaround: Use the commit hash. Don't use the branch, as the API might change, breaking the build!

@Library('github.com/cloudogu/ces-build-lib@feature/a5955fb')

SonarCloud error

java.lang.IllegalAccessError: class com.cloudogu.ces.cesbuildlib.SonarCloud tried to access private field com.cloudogu.ces.cesbuildlib.SonarQube.isUsingBranchPlugin (com.cloudogu.ces.cesbuildlib.SonarCloud and com.cloudogu.ces.cesbuildlib.SonarQube are in unnamed module of loader org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$CleanGroovyClassLoader @7dfc8298)
	at com.cloudogu.ces.cesbuildlib.SonarCloud.<init>(SonarCloud.groovy:14)
	at com.cloudogu.ces.cesbuildlib.SonarCloud.<init>(SonarCloud.groovy:12)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:238)
	at org.kohsuke.groovy.sandbox.impl.Checker$3.call(Checker.java:230)
	at org.kohsuke.groovy.sandbox.GroovyInterceptor.onNewInstance(GroovyInterceptor.java:42)
	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onNewInstance(SandboxInterceptor.java:196)
	at org.kohsuke.groovy.sandbox.impl.Checker$3.call(Checker.java:227)
	at org.kohsuke.groovy.sandbox.impl.Checker.checkedConstructor(Checker.java:232)
	at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.constructorCall(SandboxInvoker.java:21)
	at WorkflowScript.run(WorkflowScript:42)
	at ___cps.transform___(Native Method)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:100)
	at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:85)
	at jdk.internal.reflect.GeneratedMethodAccessor76.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
	at com.cloudbees.groovy.cps.impl.CollectionLiteralBlock$ContinuationImpl.dispatch(CollectionLiteralBlock.java:55)
	at com.cloudbees.groovy.cps.impl.CollectionLiteralBlock$ContinuationImpl.item(CollectionLiteralBlock.java:45)
	at jdk.internal.reflect.GeneratedMethodAccessor78.invoke(Unknown Source)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
	at com.cloudbees.groovy.cps.impl.ConstantBlock.eval(ConstantBlock.java:21)
	at com.cloudbees.groovy.cps.Next.step(Next.java:83)
	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:152)
	at com.cloudbees.groovy.cps.Continuable$1.call(Continuable.java:146)
	at org.codehaus.groovy.runtime.GroovyCategorySupport$ThreadCategoryInfo.use(GroovyCategorySupport.java:136)
	at org.codehaus.groovy.runtime.GroovyCategorySupport.use(GroovyCategorySupport.java:275)
	at com.cloudbees.groovy.cps.Continuable.run0(Continuable.java:146)
	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.access$001(SandboxContinuable.java:18)
	at org.jenkinsci.plugins.workflow.cps.SandboxContinuable.run0(SandboxContinuable.java:51)
	at org.jenkinsci.plugins.workflow.cps.CpsThread.runNextChunk(CpsThread.java:187)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup.run(CpsThreadGroup.java:420)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:330)
	at org.jenkinsci.plugins.workflow.cps.CpsThreadGroup$2.call(CpsThreadGroup.java:294)
	at org.jenkinsci.plugins.workflow.cps.CpsVmExecutorService$2.call(CpsVmExecutorService.java:67)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at hudson.remoting.SingleLaneExecutorService$1.run(SingleLaneExecutorService.java:139)
	at jenkins.util.ContextResettingExecutorService$1.run(ContextResettingExecutorService.java:30)
	at jenkins.security.ImpersonatingExecutorService$1.run(ImpersonatingExecutorService.java:70)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:829)

Collect events with describe from kubernetes ressources.

Currently the log and archive method collects the pod logs, yaml ressources (kubectl get) and dogu descriptions (kubectl describe).
It would be useful if the method would collect the descriptions from all other ressources too.

These will be the resource being collected:

  • persistentvolumeclaim
  • statefulset
  • replicaset
  • deployment
  • service
  • secret
  • pod
  • configmap
  • persistentvolume
  • replicaset
  • ingress
  • ingressclass

Git.push only works with remote "origin"

When implementing Git.pull() we wondered why Git.push() is implemented as git push origin ${refSpec}. With this it is impossible to push to other remotes than origin.

It would be a good idea to remove the origin, but then this would break backwards compatibility.

So with a version 2.x of ces-build-lib this behaviour should change.
For now, we implement pull() without origin so it behaves more correct but different than push() :-/

SonarQube: Adapt to SonarCloud's changes

Change in SonarCloud for PullRequests in GitHub

if you were relying on the GitHub Plugin, its properties are no longer required and they must be removed your configuration: sonar.analysis.mode, sonar.github.repository, sonar.github.pullRequest,
sonar.github.oauth

The new properties are described here

sonar.pullrequest.base=master
sonar.pullrequest.branch=feature/my-new-feature
sonar.pullrequest.key=5
sonar.pullrequest.provider=GitHub
sonar.pullrequest.github.repository=my-company/my-repo

Does that mean we need to differentiate between SonarQube and SonarCloud now?

Change here
For an example see schnatterer/sonarcloudTest

Also: waitForQualityGateWebhookToBeCalled() should no longer exclude PRs. As they are fully analysed on SonarCloud now (and the PRs are commented via the GitHub/BitBucket integrations of SonarCloud), we can query the QualityGate status, as for normal builds. See
scm-manager.

Shell: Set set pipefail by default

Using the sh step is prone to error when using pipes.
It's good practice to set set -o pipefail in all bash scripts because otherwise:

cat missingFile | sed s/foo/bar/g

would return a non zero exit code, which is unexepcted and hard to debug.

So: Why doesn't ces-build-lib make life easier for us an prepend pipefail before every sh call?
E.g. like so: script.sh(returnStdout: true, script: "set -e && ${args}")
Note: set -o pipefail fails (haha) with
...script.sh: 1: set: Illegal option -o pipefail.

Mercurial Support

Right now, the implementation is Git-only.

The Cloudogu EcoSystem also supports Mercurial (and is develop on mercurial itself), so we should also support mercurial (aka hg).

A quick search reveals at least these two Git-specific implementations that must be generalized (e.g. using a SCM class, that can detect the remote somehow):

  • findEmailRecipients
  • SonarQube - sets the target branch to master (which is default on hg)
  • SonarCloud - uses Git class for BitBucket, which could be hg

Note:

  • getRepositoryUrl: hg paths default
  • getCommitAuthorComplete: hg log --branch . --limit 1 --template '{author}'

MavenInDocker: Don' create new Image, just mount temporary etc passwd

For example

            writeFile file: 'passwd', text: "jenkins:x:1002:1003::${pwd()}:/bin/bash"
// ...
    docker.image('someImage').inside("-v ${pwd()}/passwd:/etc/passwd:ro") {
  • Could this be realized as default in Docker.image()?
  • The UID/GID (1002/1003) might be different in other environments. Parse from /etc/passwd as in MavenInDocker
    Implemented in Docker
  • /home/jenkins might be different in other environments
  • The user jenkins might be different in other environments (see #6)
  • We also have to mount /etc/groups
  • Reuse the Docker class in MavenInDocker
  • Update docs

This is likely to make #4 obsolete.

Provide `GitInDocker` class for more deterministic Git Commands

Especially when implementing extensive Git Operations in Jenkins (e.g. GitFlow or GitOps) it might be important to rely specific git features (or avoid bugs fixed in specific versions).

However, when using the Git class as is, the calls are executed by the git client provided by the Jenkins agent. This makes the outcome of calls to the methods of Git strongly dependend on the agent.

This fact can be changed by executing all Git Commands in a Container that contains a deterministic version of git.
We could implement this similar to MavenInDocker, by deriving from Git and overriding a method that executes all script.sh calls for Git.
In this method we could wrap the original method to be executed in a container, e.g. like so:

  new Docker(this).image("alpine/git:v${gitVersion}")
          .mountJenkinsUser()
          .inside('--entrypoint=""') {
               super.method()
          }

Add github functionality to add release assets

There is a new feature which will automatically create GitHub Releases for Cloudogus Go-Tools. These changes includes new features for Jenkinsfiles which should be included here.
The features will be:

  • Uploading Assets on GitHub Releases
  • Signing Builds with GPG

MavenInDocker: Does not work when pom is in subfolder

I had a problem building a maven project which was using the maven scm plugin. I was using the MavenInDocker-class to build the project.

The maven project is inside of a subfolder in the main project. So i was using the dir block in my Jenkinsfile. The problem is, that in this case only the current dir was mounted into the maven-docker-container which caused the build to fail because the .git-folder in the parent folder is required when using the scm-plugin.

To bypass this i executed the maven build in the base directory (to make sure everything is mounted inside docker) and passed the path to the pom.xml into the additionalArgs of the maven object.

Can you please make sure that the whole project is mounted into the maven-in-docker-container?
Maybe there is also another way, but i dont have an idea.

SCMManager: createPullRequest() fails behind proxy.

Behind nginx the httpResponse by SCMM looks like so for example:

httpResponse header [server:nginx/1.17.10, date:Wed, 24 Mar 2021 20:03:24 GMT, cache-control:no-cache, location:https://my.host/scm/api/v2/pull-requests/fluxv1/gitops/4]

Leading to

java.lang.NullPointerException: Cannot invoke method split() on null object

Because we look up the header like so:

return httpResponse.headers.Location.split("/")[-1]

Maven: Use mirrors

Via settings.xml we can setup mirrors for Maven Repos, e.g. central.

<mirrors>
  <mirror>
    <id>${url}</id>
    <name>${url} Central Mirror</name>
    <url>${urll}/nexus/content/groups/public</url>
    <mirrorOf>central</mirrorOf>
  </mirror>
</mirrors>

ces-build-lib could help us doing that.

We already use writing settings.xml for deployment. So we have to create a generic mechanism for settings.xml e.g. using a builder. We also have to assert that the settings.xml is written at least once before mvn / call() is called and that maven uses the settings.xml (-s parameter).

SonarQube: Breaking builds from 7.9 due to `sonar.branch`

org.sonar.api.utils.MessageException: The 'sonar.branch' parameter is no longer supported. You should stop using it. Branch analysis is available in Developer Edition and above. See https://redirect.sonarsource.com/editions/developer.html for more information.
[ERROR] 

Support Nexus 3

Nexus 3's URLs for Snapshot and release repos have changed, resulting in the following errors when deploying to Nexus 3 using ces-build-libs:

Caused by: org.apache.maven.plugin.MojoExecutionException: Failed to deploy artifacts: Could not transfer artifact ...
Failed to transfer file: https://URL/content/repositories/snapshots/org/springframework/samples/spring-petclinic/1.5.2-SNAPSHOT/spring-petclinic-1.5.2-20180724.001942-1.jar. Return code is: 405, ReasonPhrase: Method Not Allowed.
Snapshot Release
Nexus 2 /content/repositories/snapshots /content/repositories/releases
Nexus 3 /repository/maven-snapshots /repository/maven-releases

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.