Compare commits
3 Commits
6f2e1e6d8b
...
e8555453f7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8555453f7 | ||
|
|
cdd2618210 | ||
|
|
18f3644f82 |
38
.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# === LaTeX build files ===
|
||||
*.aux
|
||||
*.log
|
||||
*.out
|
||||
*.toc
|
||||
*.nav
|
||||
*.snm
|
||||
*.synctex.gz
|
||||
*.fls
|
||||
*.fdb_latexmk
|
||||
*.xdv
|
||||
|
||||
# === Bibliography ===
|
||||
*.bbl
|
||||
*.bcf
|
||||
*.blg
|
||||
*.run.xml
|
||||
|
||||
# === Beamer specific ===
|
||||
*.vrb
|
||||
|
||||
# === Temporary files ===
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# === Output PDFs ===
|
||||
*.pdf
|
||||
|
||||
# === Latexindent ===
|
||||
*.indent.log
|
||||
|
||||
# === VS Code settings ===
|
||||
.vscode/
|
||||
|
||||
# === OS crap ===
|
||||
.DS_Store
|
||||
BIN
slides/background/CoverPage_OpenBokeron.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
slides/background/OpenBokeronPresentation_Cover.jpg
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
slides/background/corners.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
slides/background/corners2.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
185
slides/beamerthemeBokeronTemp.sty
Normal file
@@ -0,0 +1,185 @@
|
||||
% Bokeron Template based on Bayes Business School Beamer theme by Emilio Luis S\'aenz Guill\'en
|
||||
|
||||
\ProvidesPackage{beamerthemeBokeronTemp}
|
||||
|
||||
\RequirePackage{silence}
|
||||
\WarningsOff[mathdesign/mdbch]
|
||||
\RequirePackage{etoolbox}
|
||||
|
||||
% Eliminate navigation symbols
|
||||
\setbeamertemplate{navigation symbols}{}
|
||||
|
||||
% Section and subsection numbering in table of contents
|
||||
\setbeamertemplate{section in toc}[sections numbered]
|
||||
\setbeamertemplate{subsection in toc}[subsections numbered]
|
||||
% Current position in the presentation
|
||||
\useoutertheme[subsection = false]{miniframes}
|
||||
\setbeamercolor{section in head/foot}{bg=BokeronBlue, fg=white}
|
||||
|
||||
% Captions numbering
|
||||
\setbeamertemplate{caption}[numbered]
|
||||
|
||||
% Set slide margins
|
||||
\setbeamersize{text margin left=2em, text margin right=10em}
|
||||
|
||||
% Set Normal Text Color
|
||||
\setbeamercolor{normal text}{fg=Black}
|
||||
|
||||
% Structure colour (bullet points and section headings...)
|
||||
\setbeamercolor{structure}{fg=BokeronBlue}
|
||||
|
||||
% Font
|
||||
\RequirePackage{fontspec}
|
||||
\setromanfont{Liberation Sans}
|
||||
\IfFontExistsTF{Open Sans}{\setsansfont[Scale=MatchLowercase]{Open Sans}}{\setsansfont[Scale=MatchLowercase]{Liberation Sans}}
|
||||
\RequirePackage{tikz}
|
||||
% Font substitution
|
||||
\DeclareFontFamilySubstitution{TS1}{\sfdefault}{lmr}
|
||||
\DeclareFontFamilySubstitution{OMS}{\sfdefault}{lmr}
|
||||
|
||||
% Define colours
|
||||
\definecolor{BokeronTitleBlue}{RGB}{0, 149, 197}
|
||||
\definecolor{LighterBokeronTitleBlue}{RGB}{204,217,236}
|
||||
\definecolor{BokeronBlue}{RGB}{30, 160, 186}
|
||||
\definecolor{BoxLightBlue}{RGB}{173, 235, 255}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%% TITLE %%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Adjust Frame Title Position
|
||||
\setbeamertemplate{frametitle}{\vskip-0.2\paperheight\insertframetitle}
|
||||
% Frame Title Font
|
||||
\setbeamerfont{frametitle}{family=\rmfamily,series=\mdseries,size=\Large}
|
||||
% Set Color for Title-like Elements
|
||||
\setbeamercolor{titlelike}{fg=BokeronTitleBlue}
|
||||
% Title page
|
||||
\setbeamertemplate{title page}{
|
||||
\vbox{}
|
||||
\vfill
|
||||
\hbox{
|
||||
\begin{minipage}[t]{0.4\textwidth}
|
||||
{\usebeamerfont{title}\usebeamercolor[fg]{title}\inserttitle\par}
|
||||
\ifx\insertsubtitle\@empty\else
|
||||
{\usebeamerfont{subtitle}\usebeamercolor[fg]{subtitle}\insertsubtitle\par}
|
||||
\fi
|
||||
\vspace{0.1in}
|
||||
{\usebeamerfont{author}\usebeamercolor[fg]{author}\insertauthor\par}
|
||||
\vspace{0.1in}
|
||||
{\usebeamerfont{institute}\usebeamercolor[fg]{institute}\insertinstitute\par}
|
||||
{\usebeamerfont{date}\usebeamercolor[fg]{date}\insertdate\par}
|
||||
\end{minipage}
|
||||
\hfill
|
||||
}
|
||||
\vfill
|
||||
}
|
||||
% Sizes
|
||||
\setbeamerfont{title}{family=\rmfamily,size=\LARGE}
|
||||
\setbeamerfont{author}{size=\large}
|
||||
\setbeamerfont{institute}{size=\large}
|
||||
% Colours
|
||||
\setbeamercolor{title}{fg=white}
|
||||
\setbeamercolor{subtitle}{fg=white}
|
||||
\setbeamercolor{author}{fg=white}
|
||||
\setbeamercolor{institute}{fg=white}
|
||||
\setbeamercolor{date}{fg=white}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%% BACKGROUND %%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\usebackgroundtemplate%
|
||||
{%
|
||||
\includegraphics[width=\paperwidth,height=\paperheight]{background/corners2.png}%
|
||||
}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%% CLOSING SLIDE %%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\setbeamertemplate{endpage}{
|
||||
\begin{frame}[plain]
|
||||
\begin{tikzpicture}[remember picture, overlay]
|
||||
% Background image
|
||||
\node[at=(current page.center)] {
|
||||
\includegraphics[width=\paperwidth,height=\paperheight]{background/CoverPage_OpenBokeron.png}
|
||||
};
|
||||
\end{tikzpicture}
|
||||
\end{frame}
|
||||
}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%% ITEMIZE %%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\setbeamertemplate{itemize item}{\textbullet}
|
||||
\setbeamertemplate{itemize subitem}{--}
|
||||
\setbeamertemplate{itemize subsubitem}{\textbullet}
|
||||
% First-Level List Identations
|
||||
\setlength{\leftmargini}{1.25em}
|
||||
% Second-Level List Indentation
|
||||
\setlength{\leftmarginii}{1em}
|
||||
% Third-Level List Indentation
|
||||
\setlength{\leftmarginiii}{1em}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%% FRAME TITLES %%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Normal frame title
|
||||
\newcommand{\normalframetitle}{%
|
||||
\setbeamertemplate{frametitle}{\vskip0.2\paperheight\insertframetitle}
|
||||
\setbeamerfont{frametitle}{size=\LARGE,series=\mdseries}
|
||||
\setstretch{1}
|
||||
}
|
||||
% Small frame title
|
||||
\newcommand{\smallframetitle}{%
|
||||
\setbeamertemplate{frametitle}{\vskip0.05\paperheight\insertframetitle}
|
||||
\setbeamerfont{frametitle}{size={\fontsize{11}{15}},series=\bfseries}
|
||||
}
|
||||
% Smaller frame title
|
||||
\newcommand{\smallerframetitle}{
|
||||
\setbeamertemplate{frametitle}{\vskip0.025\paperheight\insertframetitle}
|
||||
\setbeamerfont{frametitle}{size={\fontsize{8}{12}},series=\bfseries}
|
||||
}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%% TABLES %%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\renewcommand{\arraystretch}{1.3}
|
||||
\newcommand{\tableheadrow}{\rowcolor{BokeronBlue}}
|
||||
\newcommand{\tableheadcol}[1]{{\bfseries\color{white}#1}}
|
||||
\AtBeginEnvironment{tabular}{\color{black}}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%% BLOCK %%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\setbeamerfont{block title}{family=\rmfamily,size=\large}
|
||||
\setbeamercolor{block title}{fg=white,bg=BokeronTitleBlue}
|
||||
\setbeamercolor{block body}{bg=LighterBokeronTitleBlue}
|
||||
\setbeamertemplate{blocks}[rounded][shadow=false]
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%% FOOTLINE %%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
\setbeamertemplate{footline}{%
|
||||
\vskip0.15\paperheight
|
||||
}
|
||||
\setbeamercolor{myfootlinecolor}{bg=white,fg=BokeronBlue}
|
||||
\setbeamertemplate{footline}
|
||||
{%
|
||||
\begin{beamercolorbox}[colsep=1.5pt, ht=3.5ex, dp=1.125ex, leftskip=.3cm, rightskip=.3cm plus1fil]{myfootlinecolor}
|
||||
\leavevmode{\usebeamerfont{author in head/foot}\insertshortauthor}%
|
||||
\hfill
|
||||
{\usebeamerfont{institute in head/foot}\insertshortinstitute}%
|
||||
\hfill%
|
||||
{\usebeamerfont{title in head/foot}\insertframenumber/\inserttotalframenumber}
|
||||
\end{beamercolorbox}
|
||||
}
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%% OTHERS %%%%%%%%%%%%%%%%%%%%
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% Disable warning on \author
|
||||
\pdfstringdefDisableCommands{%
|
||||
\def\\{}%
|
||||
\def\texttt#1{<#1>}%
|
||||
}
|
||||
BIN
slides/images/LogoOpenBokeron.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
slides/images/jenkins_failing_again.jpg
Normal file
|
After Width: | Height: | Size: 91 KiB |
BIN
slides/images/jenkins_smile.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
slides/images/jenkinsbot.jpg
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
slides/images/kubernetes.jpeg
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
slides/images/tenna_spray_github.jpg
Normal file
|
After Width: | Height: | Size: 87 KiB |
179
slides/main.bbl-SAVE-ERROR
Normal file
@@ -0,0 +1,179 @@
|
||||
% $ biblatex auxiliary file $
|
||||
% $ biblatex bbl format version 3.3 $
|
||||
% Do not modify the above lines!
|
||||
%
|
||||
% This is an auxiliary file used by the 'biblatex' package.
|
||||
% This file may safely be deleted. It will be recreated by
|
||||
% biber as required.
|
||||
%
|
||||
\begingroup
|
||||
\makeatletter
|
||||
\@ifundefined{ver@biblatex.sty}
|
||||
{\@latex@error
|
||||
{Missing 'biblatex' package}
|
||||
{The bibliography requires the 'biblatex' package.}
|
||||
\aftergroup\endinput}
|
||||
{}
|
||||
\endgroup
|
||||
|
||||
|
||||
\refsection{0}
|
||||
\datalist[entry]{apa/apasortcite//global/global/global}
|
||||
\entry{fowler_ci}{online}{}{}
|
||||
\name{author}{1}{}{%
|
||||
{{un=0,uniquepart=base,hash=312ac886938a9b2be4f8eb607567fe4c}{%
|
||||
family={Fowler},
|
||||
familyi={F\bibinitperiod},
|
||||
given={Martin},
|
||||
giveni={M\bibinitperiod},
|
||||
givenun=0}}%
|
||||
}
|
||||
\strng{namehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{fullhash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{fullhashraw}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{bibnamehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authorbibnamehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authornamehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authorfullhash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authorfullhashraw}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\field{sortinit}{F}
|
||||
\field{sortinithash}{2638baaa20439f1b5a8f80c6c08a13b4}
|
||||
\field{extradatescope}{labelyear}
|
||||
\field{labeldatesource}{}
|
||||
\true{uniqueprimaryauthor}
|
||||
\field{labelnamesource}{author}
|
||||
\field{labeltitlesource}{title}
|
||||
\field{title}{Continuous Integration}
|
||||
\field{urlday}{18}
|
||||
\field{urlmonth}{1}
|
||||
\field{urlyear}{2026}
|
||||
\field{year}{2023}
|
||||
\field{urldateera}{ce}
|
||||
\verb{urlraw}
|
||||
\verb https://martinfowler.com/articles/continuousIntegration.html
|
||||
\endverb
|
||||
\verb{url}
|
||||
\verb https://martinfowler.com/articles/continuousIntegration.html
|
||||
\endverb
|
||||
\endentry
|
||||
\entry{humble2010continuous}{book}{}{}
|
||||
\name{author}{2}{}{%
|
||||
{{un=0,uniquepart=base,hash=9e89d796d06f74d6551850b1c3bff900}{%
|
||||
family={Humble},
|
||||
familyi={H\bibinitperiod},
|
||||
given={Jez},
|
||||
giveni={J\bibinitperiod},
|
||||
givenun=0}}%
|
||||
{{un=0,uniquepart=base,hash=e88f486e16133b897ffb346a75f56d96}{%
|
||||
family={Farley},
|
||||
familyi={F\bibinitperiod},
|
||||
given={David},
|
||||
giveni={D\bibinitperiod},
|
||||
givenun=0}}%
|
||||
}
|
||||
\list{location}{1}{%
|
||||
{Boston}%
|
||||
}
|
||||
\list{publisher}{1}{%
|
||||
{Addison-Wesley}%
|
||||
}
|
||||
\strng{namehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{fullhash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{fullhashraw}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{bibnamehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authorbibnamehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authornamehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authorfullhash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authorfullhashraw}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\field{sortinit}{H}
|
||||
\field{sortinithash}{23a3aa7c24e56cfa16945d55545109b5}
|
||||
\field{extradatescope}{labelyear}
|
||||
\field{labeldatesource}{}
|
||||
\true{uniqueprimaryauthor}
|
||||
\field{labelnamesource}{author}
|
||||
\field{labeltitlesource}{title}
|
||||
\field{title}{Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation}
|
||||
\field{year}{2010}
|
||||
\endentry
|
||||
\enddatalist
|
||||
\datalist[entry]{apa/global//global/global/global}
|
||||
\entry{fowler_ci}{online}{}{}
|
||||
\name{author}{1}{}{%
|
||||
{{un=0,uniquepart=base,hash=312ac886938a9b2be4f8eb607567fe4c}{%
|
||||
family={Fowler},
|
||||
familyi={F\bibinitperiod},
|
||||
given={Martin},
|
||||
giveni={M\bibinitperiod},
|
||||
givenun=0}}%
|
||||
}
|
||||
\strng{namehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{fullhash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{fullhashraw}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{bibnamehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authorbibnamehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authornamehash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authorfullhash}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\strng{authorfullhashraw}{312ac886938a9b2be4f8eb607567fe4c}
|
||||
\field{sortinit}{F}
|
||||
\field{sortinithash}{2638baaa20439f1b5a8f80c6c08a13b4}
|
||||
\field{extradatescope}{labelyear}
|
||||
\field{labeldatesource}{}
|
||||
\true{uniqueprimaryauthor}
|
||||
\field{labelnamesource}{author}
|
||||
\field{labeltitlesource}{title}
|
||||
\field{title}{Continuous Integration}
|
||||
\field{urlday}{18}
|
||||
\field{urlmonth}{1}
|
||||
\field{urlyear}{2026}
|
||||
\field{year}{2023}
|
||||
\field{urldateera}{ce}
|
||||
\verb{urlraw}
|
||||
\verb https://martinfowler.com/articles/continuousIntegration.html
|
||||
\endverb
|
||||
\verb{url}
|
||||
\verb https://martinfowler.com/articles/continuousIntegration.html
|
||||
\endverb
|
||||
\endentry
|
||||
\entry{humble2010continuous}{book}{}{}
|
||||
\name{author}{2}{}{%
|
||||
{{un=0,uniquepart=base,hash=9e89d796d06f74d6551850b1c3bff900}{%
|
||||
family={Humble},
|
||||
familyi={H\bibinitperiod},
|
||||
given={Jez},
|
||||
giveni={J\bibinitperiod},
|
||||
givenun=0}}%
|
||||
{{un=0,uniquepart=base,hash=e88f486e16133b897ffb346a75f56d96}{%
|
||||
family={Farley},
|
||||
familyi={F\bibinitperiod},
|
||||
given={David},
|
||||
giveni={D\bibinitperiod},
|
||||
givenun=0}}%
|
||||
}
|
||||
\list{location}{1}{%
|
||||
{Boston}%
|
||||
}
|
||||
\list{publisher}{1}{%
|
||||
{Addison-Wesley}%
|
||||
}
|
||||
\strng{namehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{fullhash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{fullhashraw}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{bibnamehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authorbibnamehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authornamehash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authorfullhash}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\strng{authorfullhashraw}{06b8a2e32b3cda03592e143a7f761e36}
|
||||
\field{sortinit}{H}
|
||||
\field{sortinithash}{23a3aa7c24e56cfa16945d55545109b5}
|
||||
\field{extradatescope}{labelyear}
|
||||
\field{labeldatesource}{}
|
||||
\true{uniqueprimaryauthor}
|
||||
\field{labelnamesource}{author}
|
||||
\field{labeltitlesource}{title}
|
||||
\field{title}{Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation}
|
||||
\field{year}{2010}
|
||||
\endentry
|
||||
\enddatalist
|
||||
\endrefsection
|
||||
\endinput
|
||||
|
||||
92
slides/main.tex
Normal file
@@ -0,0 +1,92 @@
|
||||
%% Requires compilation with XeLaTeX or LuaLaTeX
|
||||
\documentclass[10pt,table,dvipsnames, t, compress, aspectratio=169]{beamer}
|
||||
|
||||
%% Packages
|
||||
\makeatletter
|
||||
\def\input@path{{./}{slides/}}
|
||||
\makeatother
|
||||
|
||||
\usepackage{tikz}
|
||||
\usetikzlibrary{tikzmark,calc,arrows.meta,shapes.arrows,decorations.pathreplacing}
|
||||
|
||||
\usepackage{caption, subcaption, bbm, mathtools, amsmath, lmodern}
|
||||
\usepackage{graphicx, amssymb, cancel, pifont, bm}
|
||||
\usefonttheme[onlymath]{serif}
|
||||
|
||||
|
||||
|
||||
% Bibliography
|
||||
\usepackage[style=apa, backend=biber]{biblatex}
|
||||
\addbibresource{sn-bibliography.bib}
|
||||
|
||||
% Theme
|
||||
\usetheme{BokeronTemp}
|
||||
|
||||
% Hyperref
|
||||
\usepackage{hyperref}
|
||||
% url in blue
|
||||
\newcommand{\bluehref}[2]{%
|
||||
\begingroup
|
||||
\hypersetup{urlcolor=blue}%
|
||||
\href{#1}{\color{blue}#2}%
|
||||
\endgroup
|
||||
}
|
||||
|
||||
% Color box
|
||||
\usepackage{xcolor}
|
||||
\usepackage{tcolorbox}
|
||||
\newtcolorbox{mybox}{colback=BoxLightBlue, colframe=BokeronBlue, width=0.9\linewidth}
|
||||
|
||||
%% Title
|
||||
\title{\textbf{\large Taller CI/CD con Jenkins}}
|
||||
\subtitle{De 0 a deployment con FastAPI + Svelte (y Docker)}
|
||||
\author{Open Bokeron}
|
||||
\institute[Open Bokeron]{Open Bokeron\\ \bluehref{https://openbokeron.uma.es}{openbokeron.uma.es}}
|
||||
\date{18/01/2026}
|
||||
|
||||
\begin{document}
|
||||
|
||||
%----------------------------------------------------------------------------------------
|
||||
% TITLE SLIDES
|
||||
%----------------------------------------------------------------------------------------
|
||||
|
||||
{
|
||||
\usebackgroundtemplate{
|
||||
\begin{tikzpicture}[remember picture, overlay]
|
||||
\node[at=(current page.center)] {
|
||||
\includegraphics[width=\paperwidth,height=\paperheight,keepaspectratio]{background/OpenBokeronPresentation_Cover.jpg}
|
||||
};
|
||||
\end{tikzpicture}
|
||||
}
|
||||
\begin{frame}[plain]
|
||||
\titlepage
|
||||
\end{frame}
|
||||
}
|
||||
|
||||
\smallframetitle
|
||||
|
||||
\begin{frame}{Agenda}
|
||||
\tableofcontents
|
||||
\end{frame}
|
||||
|
||||
% Current section
|
||||
\AtBeginSection[ ]
|
||||
{
|
||||
\begin{frame}{}
|
||||
\tableofcontents[currentsection,currentsubsection,
|
||||
sectionstyle=show/shaded]
|
||||
\end{frame}
|
||||
}
|
||||
|
||||
|
||||
\input{sections/01_Introduccion}
|
||||
\input{sections/02_Instalacion}
|
||||
\input{sections/03_Pipeline}
|
||||
\input{sections/04_DesdeFuera}
|
||||
\input{sections/05_Consideraciones}
|
||||
\input{sections/06_Cierre}
|
||||
|
||||
|
||||
\AtEndDocument{\usebeamertemplate{endpage}}
|
||||
|
||||
\end{document}
|
||||
101
slides/sections/01_Introduccion.tex
Normal file
@@ -0,0 +1,101 @@
|
||||
\section{Introducción}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Open Bokeron (spam nuestro)}}
|
||||
\begin{itemize}
|
||||
\item Asociaci\'on de software libre de la ETSII (UMA).
|
||||
\item Hacemos cosas \#HazCosas
|
||||
\item Web: \bluehref{https://openbokeron.uma.es}{openbokeron.uma.es}
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.2in}
|
||||
\begin{mybox}
|
||||
\begin{itemize}
|
||||
\item[\ding{229}] Objetivo del taller: entender un correcto flujo de desarrollo, CI/CD y perderle miedo a Jenkins de forma pr\'actica usando un proyecto base (FastAPI + Svelte).
|
||||
\end{itemize}
|
||||
\end{mybox}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{\'Que es CI/CD?}}
|
||||
\begin{itemize}
|
||||
\item \textbf{CI (Integraci\'on Continua)}: integrar cambios peque\~nos frecuentemente y validarlos autom\'aticamente.
|
||||
\item \textbf{CD (Entrega/Despliegue Continuo)}: llevar el cambio a un entorno desplegable con un proceso reproducible.
|
||||
\item Se apoya en automatizaci\'on: pipeline + artefactos versionados + entornos.
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Ejecuci\'on del taller y consideraciones}}
|
||||
\begin{itemize}
|
||||
\item Se os da una \textbf{VM Ubuntu} con el repo clonado en local.
|
||||
\item Trabajaremos con:\\
|
||||
\hspace{0.25em}-- \texttt{backend/} (FastAPI + pytest + ruff)\\
|
||||
\hspace{0.25em}-- \texttt{frontend/} (Svelte/Vite + svelte-check)\\
|
||||
\hspace{0.25em}-- pipelines: \texttt{Jenkinsfile.ci} y \texttt{Jenkinsfile.cd}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Jenkins vs GitHub Actions}}
|
||||
\begin{columns}[T]
|
||||
\begin{column}{0.48\linewidth}
|
||||
\textbf{Jenkins}
|
||||
\begin{itemize}
|
||||
\item Muy flexible; se integra con casi cualquier cosa.
|
||||
\item Control total: agentes, credenciales, redes, storage.
|
||||
\item Contras: mantenimiento, plugins, upgrades y seguridad.
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.4em}
|
||||
\centering
|
||||
\includegraphics[width=1.2\linewidth]{images/jenkins_smile.jpg}
|
||||
\end{column}
|
||||
\begin{column}{0.48\linewidth}
|
||||
\textbf{GitHub Actions}
|
||||
\begin{itemize}
|
||||
\item Experiencia integrada con GitHub (PRs, checks, permisos).
|
||||
\item Menos ops; runners gestionados (o self-hosted pagando).
|
||||
\item Contras: lock-in, l\'imites de ejecuci\'on, dependes de un externo, starvation.
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.4em}
|
||||
\centering
|
||||
\includegraphics[width=0.75\linewidth]{images/tenna_spray_github.jpg}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Conceptos de Jenkins}}
|
||||
\small
|
||||
\begin{columns}[T]
|
||||
\begin{column}{0.49\linewidth}
|
||||
\begin{block}{Piezas}
|
||||
\begin{itemize}
|
||||
\item \textbf{Controller}: coordina jobs, UI, credenciales y cola.
|
||||
\item \textbf{Nodo/Agente}: m\'aquina (VM, contenedor, bare metal) donde \emph{se ejecuta} el trabajo.
|
||||
\item \textbf{Executor}: \# de trabajos simult\'aneos que un agente puede correr.
|
||||
\item \textbf{Workspace}: carpeta donde Jenkins hace el checkout y trabaja.
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\begin{column}{0.49\linewidth}
|
||||
\begin{block}{Pipeline}
|
||||
\begin{itemize}
|
||||
\item \textbf{Job/Pipeline}: definici\'on del flujo (en este taller: \texttt{Jenkinsfile.*}).
|
||||
\item \textbf{Stage}: fase visible (lint, test, build, deploy...).
|
||||
\item \textbf{Step}: acci\'on concreta dentro de un stage.
|
||||
\item \textbf{Artefactos}: salidas versionadas (builds, reports) que Jenkins guarda/publica.
|
||||
\end{itemize}
|
||||
\end{block}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
|
||||
\vspace{0.3em}
|
||||
\begin{mybox}
|
||||
\begin{itemize}
|
||||
\item[\ding{229}] En \textbf{nuestro caso}: Jenkins corre en \textbf{bare metal} y el \textbf{controller} y el \textbf{agente} son la \textbf{misma m\'aquina}; los pipelines ejecutan comandos lanzando \textbf{contenedores Docker}.
|
||||
\end{itemize}
|
||||
\end{mybox}
|
||||
\end{frame}
|
||||
67
slides/sections/02_Instalacion.tex
Normal file
@@ -0,0 +1,67 @@
|
||||
\section{Instalaci\'on inicial}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Arquitectura del repo}}
|
||||
\begin{itemize}
|
||||
\item API: \texttt{backend/} (FastAPI)
|
||||
\item Front: \texttt{frontend/} (Svelte + Vite, base path \texttt{/taller/})
|
||||
\item Orquestaci\'on: \texttt{docker-compose.yml} (frontend + backend)
|
||||
\item Jenkins:
|
||||
\begin{itemize}
|
||||
\item Pipelines: \texttt{Jenkinsfile.ci} y \texttt{Jenkinsfile.cd}
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.15in}
|
||||
\begin{mybox}
|
||||
\begin{itemize}
|
||||
\item[\ding{229}] Importante: el frontend usa un \textbf{base path} \texttt{/taller/}.
|
||||
\end{itemize}
|
||||
\end{mybox}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Uf seguro que instalar el Jenkins ese es muy difícil...}}
|
||||
\textbf{¿Seguro?}
|
||||
\begin{itemize}
|
||||
\item \texttt{sudo wget -O /etc/apt/keyrings/jenkins-keyring.asc \
|
||||
https://pkg.jenkins.io/debian-stable/jenkins.io-2026.key}
|
||||
\item \texttt{echo "deb [signed-by=/etc/apt/keyrings/jenkins-keyring.asc]" \
|
||||
https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
|
||||
/etc/apt/sources.list.d/jenkins.list > /dev/null}
|
||||
\item \texttt{sudo apt install jenkins}
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Configuraci\'on inicial y plugins}}
|
||||
\begin{itemize}
|
||||
\item Completar el asistente inicial (admin + plugins sugeridos).
|
||||
\item Configurar credenciales.
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}[fragile]
|
||||
\frametitle{\textbf{Job de prueba}}
|
||||
\begin{itemize}
|
||||
\item Creamos un job simple para validar:
|
||||
\item Ejemplo de pipeline:
|
||||
\end{itemize}
|
||||
|
||||
\small
|
||||
\begin{verbatim}
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('¿Ya está, no? Ya sé de DevOps') {
|
||||
steps {
|
||||
sh 'uname -a'
|
||||
sh 'docker version'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
\end{verbatim}
|
||||
\end{frame}
|
||||
62
slides/sections/03_Pipeline.tex
Normal file
@@ -0,0 +1,62 @@
|
||||
\section{Parte pr\'actica}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Objetivo de la parte pr\'actica}}
|
||||
\begin{itemize}
|
||||
\item Entender un pipeline de CI "de verdad": lint + tests + build.
|
||||
\item Entender un pipeline de CD: test en main + build de im\'agenes + deploy.
|
||||
\item Veamos en qué afectan los cambios:
|
||||
\begin{itemize}
|
||||
\item logs del pipeline
|
||||
\item artefactos/im\'agenes Docker
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{CI: qu\'e hace Jenkinsfile.ci?}}
|
||||
\begin{itemize}
|
||||
\item Stage \textbf{Init}: lee autor y hash corto del commit.
|
||||
\item Stage \textbf{Backend: lint \& test}:
|
||||
\begin{itemize}
|
||||
\item usa contenedor \texttt{python:3.11-slim}
|
||||
\item crea venv, instala dev deps
|
||||
\item ejecuta \texttt{ruff} y \texttt{pytest}
|
||||
\end{itemize}
|
||||
\item Stage \textbf{Frontend: check \& build}:
|
||||
\begin{itemize}
|
||||
\item usa contenedor \texttt{node:20-slim}
|
||||
\item \texttt{npm install} + \texttt{npm run check} + \texttt{npm test} + \texttt{npm run build}
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{CD: qu\'e hace Jenkinsfile.cd?}}
|
||||
\begin{itemize}
|
||||
\item Construye im\'agenes Docker con tags tipo \texttt{1.0.\$BUILD\_NUMBER}.
|
||||
\item Inyecta metadatos en runtime:
|
||||
\begin{itemize}
|
||||
\item \texttt{APP\_VERSION}, \texttt{GIT\_COMMIT}, \texttt{COMMIT\_AUTHOR}, \texttt{BUILD\_NUMBER}
|
||||
\end{itemize}
|
||||
\item Despliega con \texttt{docker compose up -d} usando \texttt{BACKEND\_TAG} y \texttt{FRONTEND\_TAG}.
|
||||
\end{itemize}
|
||||
|
||||
\vspace{0.1in}
|
||||
\begin{mybox}
|
||||
\begin{itemize}
|
||||
\item[\ding{229}] El rollback es sencillo: si tienes tags, puedes redeployar una versi\'on anterior en segundos. CD genera artefactos (imágenes versionadas) y en nuestro caso, se ocupa del deploy también.
|
||||
\end{itemize}
|
||||
\end{mybox}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Ejercicio 1: miau}}
|
||||
|
||||
\end{frame}
|
||||
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Ejercicio 2: miau 2}}
|
||||
|
||||
\end{frame}
|
||||
62
slides/sections/04_DesdeFuera.tex
Normal file
@@ -0,0 +1,62 @@
|
||||
\section{CI/CD desde fuera (como dev)}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Flujo de trabajo propuesto (developer view)}}
|
||||
\begin{itemize}
|
||||
\item Crear rama: \texttt{feature/...}
|
||||
\item Hacer cambio peque\~no y con sentido.
|
||||
\item Abrir Merge Request / Pull Request.
|
||||
\item Observar checks:\\
|
||||
\hspace{0.25em}-- CI corre en cada push\\
|
||||
\hspace{0.25em}-- el MR/PR muestra estado verde/rojo
|
||||
\item Si falla: arreglar y push de nuevo.
|
||||
\item Merge a main y observar despliegue (CD).
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Propuesta de cambio para los asistentes}}
|
||||
\textbf{Cambio sugerido}
|
||||
\begin{itemize}
|
||||
\item Cambiar el t\'itulo del hero en \texttt{frontend/src/App.svelte}. Alguien puso "UwU".
|
||||
\item Nuevo texto sugerido: \textit{Taller CI/CD con Jenkins}.
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{El juego: PR en Gitea + feedback autom\'atico}}
|
||||
\begin{columns}[T]
|
||||
\begin{column}{0.62\linewidth}
|
||||
\begin{itemize}
|
||||
\item Har\'eis la PR con un \textbf{usuario gen\'erico} que os damos en nuestro \textbf{Gitea}.
|
||||
\item Al abrir la PR:
|
||||
\begin{itemize}
|
||||
\item Jenkins lanza la \textbf{CI} (y la ver\'eis en la web de Jenkins).
|
||||
\item Aparece el generalísimo \texttt{jenkins-bot}.
|
||||
\end{itemize}
|
||||
\item \texttt{jenkins-bot} comentar\'a en la PR:
|
||||
\begin{itemize}
|
||||
\item si todo ha ido bien: \textbf{verde} y a mergear.
|
||||
\item si la hab\'eis liado: a mirar el log.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
\begin{column}{0.36\linewidth}
|
||||
\centering
|
||||
\includegraphics[height=0.68\textheight]{images/jenkinsbot.jpg}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Qu\'e observar en Jenkins al hacer el MR/PR}}
|
||||
\begin{itemize}
|
||||
\item Log del stage que falla (si falla): ruff, pytest, svelte-check, build...
|
||||
\item En CD: tag de imagen generado (\texttt{APP\_VERSION}) y deploy.
|
||||
\item En la app:
|
||||
\begin{itemize}
|
||||
\item \texttt{GET /health} refleja build/commit/autor.
|
||||
\item \texttt{GET /builds} refleja builds recientes via Jenkins REST.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
49
slides/sections/05_Consideraciones.tex
Normal file
@@ -0,0 +1,49 @@
|
||||
\section{Consideraciones y curiosidades}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Triggers: webhooks vs polling}}
|
||||
\begin{columns}[T]
|
||||
\begin{column}{0.48\linewidth}
|
||||
\textbf{Webhooks}
|
||||
\begin{itemize}
|
||||
\item El repositorio "avisa" a Jenkins.
|
||||
\item R\'apido y eficiente.
|
||||
\item El repo le envía toda la información a Jenkins (autor, commit, rama, etc.)
|
||||
\item Eso tiene pinta de ser difícil... Jenkins tiene plugins para prácticamente cualquier cosa.
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
\begin{column}{0.48\linewidth}
|
||||
\textbf{Polling}
|
||||
\begin{itemize}
|
||||
\item Jenkins pregunta cada X tiempo.
|
||||
\item Absurdamente simple.
|
||||
\item Si abres una PR no es inmediato, debes esperar al polling.
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Rollback con im\'agenes etiquetadas}}
|
||||
\begin{itemize}
|
||||
\item En CD generamos tags con \texttt{APP\_VERSION = 1.0.\$BUILD\_NUMBER}.
|
||||
\item Si el deploy de hoyudorompe algo, puedes volver atr\'as:
|
||||
\begin{itemize}
|
||||
\item desplegar \texttt{BACKEND\_TAG=1.0.41 FRONTEND\_TAG=1.0.41} (ejemplo)
|
||||
\item \texttt{docker compose up -d}
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Jenkins REST API: ver CI/CD sin entrar al UI}}
|
||||
\begin{itemize}
|
||||
\item Jenkins expone una API JSON por job/build.
|
||||
\item En este repo, el backend consulta builds recientes y expone en el frontend las últimas 5 builds del CD.
|
||||
\item Ideas de uso:
|
||||
\begin{itemize}
|
||||
\item dashboards del estado de las builds.
|
||||
\item alertas.
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
34
slides/sections/06_Cierre.tex
Normal file
@@ -0,0 +1,34 @@
|
||||
\section{Cierre}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Recap}}
|
||||
\begin{itemize}
|
||||
\item CI: automatiza validaci\'on (lint/tests/build) en cada cambio.
|
||||
\item CD: produce artefactos versionados y despliega de forma reproducible.
|
||||
\item Jenkins: flexible, potente, pero requiere mimos (seguridad, plugins, mantenimiento). Sin embargo, una vez lo tienes montado, el mantenimiento ya no es tan drama.
|
||||
\item Ahora podr\'as entrar en los pipelines de tu futuro puesto de trabajo sin miedo a romper nada... ¿Verdad?
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Preguntas y siguientes pasos}}
|
||||
\begin{columns}[T]
|
||||
\begin{column}{0.62\linewidth}
|
||||
\begin{itemize}
|
||||
\item Preguntas.
|
||||
\item ¿Ampliamos taller? Se nos ha quedado en el tintero...
|
||||
\begin{itemize}
|
||||
\item Registry (Harbor/Docker Hub/GHCR)
|
||||
\item SonarQube + quality gates
|
||||
\item An\'alisis de vulnerabilidades con Trivy
|
||||
\item Despliegue con Quadlets usando Podman
|
||||
\item Kubernetes (no)
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
\begin{column}{0.36\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{images/kubernetes.jpeg}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
34
slides/sections/__latexindent_temp_06_Cierre.tex
Normal file
@@ -0,0 +1,34 @@
|
||||
\section{Cierre}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Recap}}
|
||||
\begin{itemize}
|
||||
\item CI: automatiza validaci\'on (lint/tests/build) en cada cambio.
|
||||
\item CD: produce artefactos versionados y despliega de forma reproducible.
|
||||
\item Jenkins: flexible, potente, pero requiere mimos (seguridad, plugins, mantenimiento). Sin embargo, una vez lo tienes montado, el mantenimiento ya no es tan drama.
|
||||
\item Ahora podr\'as entrar en los pipelines de tu futuro puesto de trabajo sin miedo a romper nada... ¿Verdad?
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{\textbf{Preguntas y siguientes pasos}}
|
||||
\begin{columns}[T]
|
||||
\begin{column}{0.62\linewidth}
|
||||
\begin{itemize}
|
||||
\item Preguntas.
|
||||
\item ¿Ampliamos taller? Se nos ha quedado en el tintero...
|
||||
\begin{itemize}
|
||||
\item Registry (Harbor/Docker Hub/GHCR)
|
||||
\item SonarQube + quality gates
|
||||
\item An\'alisis de vulnerabilidades con Trivy
|
||||
\item Despliegue con Quadlets usando Podman
|
||||
\item Kubernetes (no)
|
||||
\end{itemize}
|
||||
\end{itemize}
|
||||
\end{column}
|
||||
\begin{column}{0.36\linewidth}
|
||||
\centering
|
||||
\includegraphics[width=0.8\linewidth]{images/kubernetes.jpeg}
|
||||
\end{column}
|
||||
\end{columns}
|
||||
\end{frame}
|
||||
31
slides/sn-bibliography.bib
Normal file
@@ -0,0 +1,31 @@
|
||||
@book{humble2010continuous,
|
||||
author = {Humble, Jez and Farley, David},
|
||||
title = {Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation},
|
||||
year = {2010},
|
||||
publisher = {Addison-Wesley},
|
||||
address = {Boston},
|
||||
}
|
||||
|
||||
@online{jenkins_docs,
|
||||
author = {{Jenkins Project}},
|
||||
title = {Jenkins User Documentation},
|
||||
year = {2026},
|
||||
url = {https://www.jenkins.io/doc/},
|
||||
urldate = {2026-01-18},
|
||||
}
|
||||
|
||||
@online{github_actions_docs,
|
||||
author = {{GitHub}},
|
||||
title = {GitHub Actions Documentation},
|
||||
year = {2026},
|
||||
url = {https://docs.github.com/actions},
|
||||
urldate = {2026-01-18},
|
||||
}
|
||||
|
||||
@online{fowler_ci,
|
||||
author = {Fowler, Martin},
|
||||
title = {Continuous Integration},
|
||||
year = {2023},
|
||||
url = {https://martinfowler.com/articles/continuousIntegration.html},
|
||||
urldate = {2026-01-18},
|
||||
}
|
||||