diff --git a/LoOPS_TeamCity.ipynb b/LoOPS_TeamCity.ipynb index 2646c62..3e50b70 100644 --- a/LoOPS_TeamCity.ipynb +++ b/LoOPS_TeamCity.ipynb @@ -2,11 +2,11 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 21, "metadata": { "ExecuteTime": { - "end_time": "2018-01-08T23:20:39.306357Z", - "start_time": "2018-01-08T23:20:39.297432Z" + "end_time": "2018-01-09T08:09:49.457463Z", + "start_time": "2018-01-09T08:09:49.451783Z" }, "init_cell": true }, @@ -52,8 +52,10 @@ "* Rich plugin ecosystem\n", "* Really customizable system\n", "* Not so complex...\n", + "* Updates so easy (Compared to redmine...)\n", "* Mostly written in Java\n", - "* Closed source :(" + "* Closed source :(\n", + "* Limited to 100 build cfg & 3 agents " ] }, { @@ -143,10 +145,12 @@ "cell_type": "markdown", "metadata": { "slideshow": { - "slide_type": "fragment" + "slide_type": "subslide" } }, "source": [ + "# Vocabulary\n", + "\n", "* **Build artifact**: a file or a folder produced during the build ( [example](https://hephaistos.lpp.polytechnique.fr/teamcity/overview.html) )" ] }, @@ -154,6 +158,22 @@ "cell_type": "markdown", "metadata": { "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "* **Meta-Runners**: a build config that has been extracted to make a template ( [example](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editProject.html?projectId=_Root&tab=metaRunner) )" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { "slide_type": "slide" } }, @@ -209,6 +229,17 @@ "cell_type": "markdown", "metadata": { "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "# LPP goals (What we want)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { "slide_type": "fragment" } }, @@ -220,6 +251,17 @@ "cell_type": "markdown", "metadata": { "slideshow": { + "slide_type": "fragment" + } + }, + "source": [ + "* Give users maximum freedom" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { "slide_type": "slide" } }, @@ -232,11 +274,15 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, "source": [ "# Available build agents\n", "\n", - "* Based on Docker cloud plugin\n", + "* Based on [Docker cloud plugin](https://plugins.jetbrains.com/plugin/9306-docker-cloud)\n", "* Virtually anything that runs either inside **Docker** or **VirtualBox**(Vagrant)\n", "* Spawn on demand\n", "* For now **Windows**, **Linux**(Fedora, Ubuntu), **FreeBSD** and **Mac OSX** 10.12.2(Siera)\n" @@ -250,13 +296,33 @@ } }, "source": [ + "# FreeBSD & Mac \n", + "\n", + "[meson@lpp](https://hephaistos.lpp.polytechnique.fr/teamcity/viewLog.html?buildId=6388&tab=buildLog&buildTypeId=mesonbuild_Build_2&logTab=tree&filter=all) [meson@travis](https://travis-ci.org/mesonbuild/meson/builds/325859215?utm_source=github_status&utm_medium=notification)\n", + "\n", + "[OSX agent](https://github.com/jeandet/teamcity-docker-agent-osx) [FreeBSD agent](https://github.com/jeandet/teamcity-docker-agent-freebsd)\n", + "\n", + "* Vagrant inside Docker\n", + "* Few txt files produces several GB machines\n", + "* Easy to deploy\n", + "* Hard to debug\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ "# I want to choose where I get built!\n", "\n", "**Team city introduce the notion of build requirements.**\n", "\n", - "- Agents expose [parameters](https://hephaistos.lpp.polytechnique.fr/teamcity/agentDetails.html?agentTypeId=72&tab=agentParameters)(<=> properties). [sources](https://github.com/jeandet/teamcity-docker-complete-agent/blob/master/Dockerfile#L36)\n", + "- Agents expose [parameters](https://hephaistos.lpp.polytechnique.fr/teamcity/agentDetails.html?agentTypeId=72&tab=agentParameters)(<=> properties). ( [sources](https://github.com/jeandet/teamcity-docker-complete-agent/blob/master/Dockerfile#L36) )\n", "\n", - "- So for each build config ones can declare some [requirements](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editRequirements.html?id=buildType:Miniphare_Build) that the agent has to match." + "- For each build config, ones can declare some [requirements](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editRequirements.html?id=buildType:Miniphare_Build) that the agent has to match to get spawned." ] }, { @@ -267,7 +333,13 @@ } }, "source": [ - "# What about reports?\n" + "# What about reports?\n", + "\n", + "* As build artifact ( [LFR](https://hephaistos.lpp.polytechnique.fr/teamcity/viewLog.html?buildId=292&tab=artifacts&buildTypeId=LfrFlightSoftware_Build), [Hephaistos Web](https://hephaistos.lpp.polytechnique.fr/teamcity/project.html?projectId=HephaistosWeb&tab=projectOverview), [LibCDF](https://hephaistos.lpp.polytechnique.fr/teamcity/project.html?projectId=LibCDF&tab=projectOverview) )\n", + "* As Mail\n", + "* As slack message\n", + "* As stdout values ( [GStreamer Ninja](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editRunType.html?id=buildType:mesonbuild_GStreamerNightly&runnerId=RUNNER_69&cameFromUrl=%2Fteamcity%2Fadmin%2FeditBuildRunners.html%3Fid%3DbuildType%253Amesonbuild_GStreamerNightly%26init%3D1&cameFromTitle=) )\n", + "* As XML File ( [LibCDF GTest](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editBuildFeatures.html?id=buildType:LibCDF_Build) )" ] }, { @@ -278,7 +350,13 @@ } }, "source": [ - "# API?" + "# Interactions with other tools\n", + "\n", + "* SonarQube ([example](https://sonarcloud.io/dashboard?id=sciqlop), [config](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editRunType.html?id=buildType:SciQLop_Build&runnerId=RUNNER_22&cameFromUrl=%2Fteamcity%2Fadmin%2FeditBuildRunners.html%3Fid%3DbuildType%253ASciQLop_Build%26init%3D1&cameFromTitle=))\n", + "* Slack ([example](https://sciqlop.slack.com/messages/C57HU1KBJ/), [config](https://hephaistos.lpp.polytechnique.fr/teamcity/slacknotifications/index.html?projectId=SciQLop))\n", + "* Github, Bitbucket, VS Team Services...\n", + "* Rhodecode ([Miniphare PR](https://hephaistos.lpp.polytechnique.fr/rhodecode/GIT_REPOSITORIES/LPP/phare/miniphare/miniphare/pull-request/410), [config](https://hephaistos.lpp.polytechnique.fr/teamcity/admin/editRunType.html?id=buildType:Miniphare_Build&runnerId=RUNNER_82&cameFromUrl=%2Fteamcity%2Fadmin%2FeditBuildRunners.html%3Fid%3DbuildType%253AMiniphare_Build%26init%3D1&cameFromTitle=))\n", + "* [VS IDE plugin](https://confluence.jetbrains.com/display/TCD10/Visual+Studio+Addin), [Eclipse plugin](https://confluence.jetbrains.com/display/TCD10/Eclipse+Plugin), [IntelliJ-based IDEs](https://blog.jetbrains.com/teamcity/2017/10/teamcity-integration-with-intellij-based-ides/)" ] }, { @@ -289,8 +367,62 @@ } }, "source": [ - "# Time to play!" + "# API?\n", + "\n", + "* Quite easy to [understand](https://confluence.jetbrains.com/display/TCD10/REST+API) ( [buildTypes](https://hephaistos.lpp.polytechnique.fr/teamcity/app/rest/buildTypes/), [projects](https://hephaistos.lpp.polytechnique.fr/teamcity/app/rest/projects/) )\n", + "* Almost everything can be done through API" ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "ExecuteTime": { + "end_time": "2018-01-09T08:37:54.802150Z", + "start_time": "2018-01-09T08:37:54.793474Z" + }, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import teamcity\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# What Next?\n", + "\n", + "* Improve Rhodecode integration\n", + "* Boost agents startup time\n", + "* Switch to Docker swarm\n", + "* More Meta-Runners" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Questions?\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/teamcity.py b/teamcity.py new file mode 100644 index 0000000..87b5329 --- /dev/null +++ b/teamcity.py @@ -0,0 +1,73 @@ +import requests +import traceback +import configparser +from os.path import expanduser +home = expanduser("~") + +class TeamCityJsonObject: + + def __init__(self, json): + self.__dict__.update(json) + + def __repr__(self): + return str(self.__dict__) + + +class TeamCityProject(TeamCityJsonObject): + + def __init__(self, json): + super(TeamCityProject, self).__init__(json) + + +class TeamCityBuildType(TeamCityJsonObject): + + def __init__(self, json): + super(TeamCityBuildType, self).__init__(json) + +class TeamCityBuild(TeamCityJsonObject): + + def __init__(self, json): + super(TeamCityBuild, self).__init__(json) + + +class TeamCityAPI: + + def __init__(self, server, user, password): + self.server = server if '/app/rest' in server else server + '/app/rest' + self.session = requests.Session() + self.session.auth = (user, password) + self.session.headers.update({ + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }) + + def projects(self): + projects = self.session.get( + self.server + '/projects').json() + return [TeamCityProject(project) for project in projects['project']] + + def build_types(self, project_id=None, project_name=None): + if project_id is not None: + url = self.server + '/projects/id:' + project_id + '/buildTypes' + elif project_name is not None: + url = self.server + '/projects/name:' + project_id + '/buildTypes' + else: + url = self.server + '/buildTypes' + build_types = self.session.get(url).json() + return [ + TeamCityBuildType(build_type) + for build_type in build_types['buildType'] + ] + + def build_queue(self): + build_queue = self.session.get(tc.server+'/buildQueue').json() + return [ + TeamCityBuild(build) + for build in build_queue['build'] + ] + +config = configparser.ConfigParser() +config.read(home+"/.teamcity.conf") + + +tc = TeamCityAPI(config['DEFAULT']['server'], config['DEFAULT']['user'],config['DEFAULT']['password']) \ No newline at end of file