473 lines
14 KiB
TeX
473 lines
14 KiB
TeX
% Insert this file into the group report with:
|
|
% \input{giteaStartup}
|
|
%
|
|
% Required packages in the main document:
|
|
% \usepackage{graphicx}
|
|
% \usepackage{minted}
|
|
% \usepackage{hyperref}
|
|
%
|
|
% This chapter intentionally does not use minted's caption option. Captions
|
|
% are written as ordinary LaTeX text so it remains compatible with the
|
|
% minted configuration already used by the group report.
|
|
|
|
\chapter{Startup Foundation with Gitea}
|
|
|
|
\section{Overview}
|
|
|
|
The startup's initial operational work hub was deployed on a DigitalOcean
|
|
Droplet using Gitea. The deployment provides a private Git service for the
|
|
team, including repositories, issues, pull requests, and collaborative code
|
|
review.
|
|
|
|
The public Git service is available at:
|
|
|
|
\begin{center}
|
|
\url{https://git.ssw590.tech}
|
|
\end{center}
|
|
|
|
The domain used for the service is \texttt{ssw590.tech}, and the Gitea
|
|
hostname is \texttt{git.ssw590.tech}. The service runs on an Ubuntu
|
|
DigitalOcean Droplet with Docker Compose. Gitea uses the official
|
|
\texttt{gitea/gitea} container image pinned to version \texttt{1.27.3}.
|
|
|
|
\section{Domain and GitHub Student Developer Pack}
|
|
|
|
The domain \texttt{ssw590.tech} was obtained through the GitHub Student
|
|
Developer Pack using Name.com. GitHub's current Student Developer Pack
|
|
lists Name.com as providing a free domain from a selection of extensions,
|
|
and separately lists the .TECH offer as one standard .TECH domain free for
|
|
one year. Thus, the initial registration used for this project was free
|
|
under the student offer.
|
|
|
|
The project domain is:
|
|
|
|
\begin{itemize}
|
|
\item Domain: \texttt{ssw590.tech}
|
|
\item Registrar: Name.com
|
|
\item Gitea hostname: \texttt{git.ssw590.tech}
|
|
\item Initial student-offer term: one year
|
|
\item Automatic renewal date shown by Name.com: September 30, 2027
|
|
\end{itemize}
|
|
|
|
The renewal price is not a fixed GitHub Student Developer Pack price.
|
|
Name.com states that renewals are charged at the then-current renewal fee.
|
|
The automatic-renewal setting is enabled for this domain. This means the
|
|
team must maintain a valid payment method if the domain is to remain active
|
|
after the student-offer term.
|
|
|
|
\section{DNS Configuration}
|
|
|
|
The DNS record for the Gitea service points the hostname
|
|
\texttt{git.ssw590.tech} to the DigitalOcean Droplet's public IPv4 address:
|
|
|
|
\begin{center}
|
|
\texttt{157.230.10.81}
|
|
\end{center}
|
|
|
|
The hostname was verified independently by accessing it from outside the
|
|
Droplet. The HTTPS request succeeded, demonstrating that the hostname
|
|
resolved to the deployed service and that the service was reachable through
|
|
the public Internet.
|
|
|
|
A command-line DNS lookup can also be recorded in the final submission:
|
|
|
|
\begin{minted}{bash}
|
|
dig +short git.ssw590.tech A
|
|
\end{minted}
|
|
|
|
The expected result is the Droplet's public IPv4 address.
|
|
|
|
\section{DigitalOcean Droplet and Docker}
|
|
|
|
The DigitalOcean Droplet runs Ubuntu and was configured for password-based
|
|
SSH authentication. The Droplet was accessed with:
|
|
|
|
\begin{minted}{bash}
|
|
ssh root@157.230.10.81
|
|
\end{minted}
|
|
|
|
Docker Engine and Docker Compose were installed on the Droplet. The final
|
|
Gitea deployment was managed from:
|
|
|
|
\begin{minted}{bash}
|
|
cd ~/gitea
|
|
\end{minted}
|
|
|
|
The Gitea Compose configuration pins the official image to version
|
|
\texttt{1.27.3} rather than using an unversioned \texttt{latest} tag.
|
|
|
|
\begin{minted}{yaml}
|
|
services:
|
|
gitea:
|
|
image: gitea/gitea:1.27.3
|
|
container_name: gitea
|
|
restart: unless-stopped
|
|
ports:
|
|
- "3000:3000"
|
|
volumes:
|
|
- ./data/gitea:/data
|
|
networks:
|
|
- gitea
|
|
|
|
networks:
|
|
gitea:
|
|
driver: bridge
|
|
\end{minted}
|
|
|
|
The official image source is the Gitea container repository:
|
|
\url{https://hub.docker.com/r/gitea/gitea}.
|
|
|
|
The image version was verified with:
|
|
|
|
\begin{minted}{bash}
|
|
docker compose ps
|
|
\end{minted}
|
|
|
|
The resulting service showed:
|
|
|
|
\begin{minted}{text}
|
|
gitea/gitea:1.27.3
|
|
\end{minted}
|
|
|
|
\section{SQLite Database}
|
|
|
|
SQLite was selected for the Gitea database because the deployment is a
|
|
small startup work hub and does not require a separate database service for
|
|
this assignment.
|
|
|
|
The Gitea configuration contains:
|
|
|
|
\begin{minted}{text}
|
|
DB_TYPE = sqlite3
|
|
PATH = /data/gitea/gitea.db
|
|
\end{minted}
|
|
|
|
The database file exists inside the persistent Docker bind mount. It was
|
|
verified on the Droplet with:
|
|
|
|
\begin{minted}{bash}
|
|
ls -lh ~/gitea/data/gitea/gitea/gitea.db
|
|
\end{minted}
|
|
|
|
The file was approximately 2.1 MB at the time of verification.
|
|
|
|
The corresponding file inside the container was also verified:
|
|
|
|
\begin{minted}{bash}
|
|
docker exec gitea ls -lh /data/gitea/gitea.db
|
|
\end{minted}
|
|
|
|
\section{Persistent Storage}
|
|
|
|
The Docker Compose configuration maps the host directory
|
|
\texttt{./data/gitea} to \texttt{/data} inside the Gitea container:
|
|
|
|
\begin{minted}{yaml}
|
|
volumes:
|
|
- ./data/gitea:/data
|
|
\end{minted}
|
|
|
|
This is important because Gitea stores its database, repositories, SSH
|
|
configuration, attachments, package data, indexes, and other application
|
|
state below \texttt{/data}. The database was observed on the host at:
|
|
|
|
\begin{minted}{text}
|
|
/root/gitea/data/gitea/gitea/gitea.db
|
|
\end{minted}
|
|
|
|
The mount itself was inspected with:
|
|
|
|
\begin{minted}{bash}
|
|
docker inspect gitea --format '{{json .Mounts}}'
|
|
\end{minted}
|
|
|
|
The result showed the host path
|
|
\texttt{/root/gitea/data/gitea} mounted read/write at \texttt{/data}.
|
|
|
|
\section{Container Replacement and Persistence Test}
|
|
|
|
Persistence was explicitly tested by recording the existing container,
|
|
recreating it, and then checking that Gitea data remained available.
|
|
|
|
Before replacement, the container identity was recorded with:
|
|
|
|
\begin{minted}{bash}
|
|
docker inspect gitea --format '{{.Id}}'
|
|
\end{minted}
|
|
|
|
The persistent mount was also recorded with:
|
|
|
|
\begin{minted}{bash}
|
|
docker inspect gitea --format '{{json .Mounts}}'
|
|
\end{minted}
|
|
|
|
The Gitea service was then force-recreated:
|
|
|
|
\begin{minted}{bash}
|
|
docker compose up -d --force-recreate gitea
|
|
\end{minted}
|
|
|
|
After the replacement, the running services were verified with:
|
|
|
|
\begin{minted}{bash}
|
|
docker compose ps
|
|
\end{minted}
|
|
|
|
The new container continued to use the same host-backed data directory.
|
|
The SQLite database remained present:
|
|
|
|
\begin{minted}{bash}
|
|
ls -lh ~/gitea/data/gitea/gitea/gitea.db
|
|
\end{minted}
|
|
|
|
The Gitea repository and issue used for the persistence test also remained
|
|
available after the replacement. This demonstrates that the application
|
|
state was stored in the persistent volume rather than only in the
|
|
container's writable layer.
|
|
|
|
\section{Persistence Evidence}
|
|
|
|
Figure~\ref{fig:gitea-persistence-repository} shows the
|
|
\texttt{persistence-test} repository after the container replacement.
|
|
|
|
\begin{figure}[htbp]
|
|
\centering
|
|
\includegraphics[width=0.90\textwidth]{gitea-persistence-repository.png}
|
|
\caption{Gitea persistence-test repository after container replacement}
|
|
\label{fig:gitea-persistence-repository}
|
|
\end{figure}
|
|
|
|
Figure~\ref{fig:gitea-persistence-issue} shows issue \#1 from the same
|
|
repository after the replacement.
|
|
|
|
\begin{figure}[htbp]
|
|
\centering
|
|
\includegraphics[width=0.90\textwidth]{gitea-persistence-issue-1.png}
|
|
\caption{Persistence verification issue \#1 after container replacement}
|
|
\label{fig:gitea-persistence-issue}
|
|
\end{figure}
|
|
|
|
\section{Startup Repository and Team Collaboration}
|
|
|
|
The startup repository used for the development workflow is
|
|
\texttt{ssw590-startup}. No separate Gitea organization was required for
|
|
the assignment, so the repository itself is used as the team's shared
|
|
workspace.
|
|
|
|
Team members were added to the Gitea project using their individual
|
|
accounts. The usernames used for the team accounts are:
|
|
|
|
\begin{itemize}
|
|
\item \texttt{mfriedma1}
|
|
\item \texttt{alexn41}
|
|
\item \texttt{so.stoeckel}
|
|
\end{itemize}
|
|
|
|
Passwords are intentionally not included in the report.
|
|
|
|
The repository README was completed, and five issues were created and
|
|
assigned as part of the team's issue-driven development workflow.
|
|
|
|
\section{Issue and Pull Request Workflow}
|
|
|
|
The first implementation task was tracked as issue \#1,
|
|
``Implement product landing page.'' A feature branch named
|
|
\texttt{feature/issue-1-landing-page} was used for the implementation.
|
|
The work was submitted as a pull request and reviewed before being merged
|
|
into \texttt{main}.
|
|
|
|
Figure~\ref{fig:gitea-pr-issue-1} shows the merged pull request for issue
|
|
\#1. The screenshot records the assignment, commit, review comment,
|
|
merge, and deletion of the feature branch.
|
|
|
|
\begin{figure}[htbp]
|
|
\centering
|
|
\includegraphics[width=0.90\textwidth]{gitea-pr-issue-1-merged.png}
|
|
\caption{Merged pull request implementing issue \#1}
|
|
\label{fig:gitea-pr-issue-1}
|
|
\end{figure}
|
|
|
|
The second implementation task was tracked as issue \#2 and used the
|
|
feature branch \texttt{feature/issue-2-tests}. The pull request added the
|
|
repository validation tests and was merged into \texttt{main}.
|
|
|
|
Figure~\ref{fig:gitea-pr-issue-2} shows the completed merge.
|
|
|
|
\begin{figure}[htbp]
|
|
\centering
|
|
\includegraphics[width=0.90\textwidth]{gitea-pr-issue-2-merged.png}
|
|
\caption{Merged pull request for issue \#2 repository validation tests}
|
|
\label{fig:gitea-pr-issue-2}
|
|
\end{figure}
|
|
|
|
Figure~\ref{fig:gitea-activity} shows the Gitea activity view containing
|
|
the repository and issue activity.
|
|
|
|
\begin{figure}[htbp]
|
|
\centering
|
|
\includegraphics[width=0.90\textwidth]{gitea-admin-activity.png}
|
|
\caption{Gitea repository and issue activity}
|
|
\label{fig:gitea-activity}
|
|
\end{figure}
|
|
|
|
\section{HTTPS with Caddy and Let's Encrypt}
|
|
|
|
Gitea is not directly exposed to the public Internet on port 3000 in the
|
|
final deployment. A Caddy reverse proxy is used in front of Gitea.
|
|
|
|
The final service layout is:
|
|
|
|
\begin{center}
|
|
\texttt{Internet -> Caddy :443 -> Gitea :3000}
|
|
\end{center}
|
|
|
|
Caddy publishes ports 80 and 443 on the Droplet while Gitea's port 3000 is
|
|
kept internal to the Docker network. The Gitea container therefore shows
|
|
port 3000 as an internal container port rather than as a host-published
|
|
port.
|
|
|
|
The current service state was verified with:
|
|
|
|
\begin{minted}{bash}
|
|
docker compose ps
|
|
\end{minted}
|
|
|
|
The output showed Caddy publishing ports 80 and 443 and Gitea exposing
|
|
port 3000 only to the Docker network.
|
|
|
|
Gitea's public URL was configured as:
|
|
|
|
\begin{minted}{text}
|
|
DOMAIN = git.ssw590.tech
|
|
ROOT_URL = https://git.ssw590.tech
|
|
\end{minted}
|
|
|
|
\section{Let's Encrypt Certificate Verification}
|
|
|
|
Caddy automatically obtained a Let's Encrypt certificate for
|
|
\texttt{git.ssw590.tech}. The Caddy log showed successful ACME
|
|
authorization and certificate issuance.
|
|
|
|
The live HTTPS service was verified externally with:
|
|
|
|
\begin{minted}{bash}
|
|
curl -4 -I https://git.ssw590.tech
|
|
\end{minted}
|
|
|
|
The response was HTTP/2 200.
|
|
|
|
Plain HTTP was also tested:
|
|
|
|
\begin{minted}{bash}
|
|
curl -4 -I http://git.ssw590.tech
|
|
\end{minted}
|
|
|
|
The response was:
|
|
|
|
\begin{minted}{text}
|
|
HTTP/1.1 308 Permanent Redirect
|
|
Location: https://git.ssw590.tech/
|
|
\end{minted}
|
|
|
|
This demonstrates that HTTP requests are redirected to HTTPS.
|
|
|
|
The certificate itself was inspected with:
|
|
|
|
\begin{minted}{bash}
|
|
echo | openssl s_client \
|
|
-connect git.ssw590.tech:443 \
|
|
-servername git.ssw590.tech 2>/dev/null \
|
|
| openssl x509 -noout -issuer -subject -dates
|
|
\end{minted}
|
|
|
|
The observed certificate information was:
|
|
|
|
\begin{minted}{text}
|
|
issuer=... Let's Encrypt ...
|
|
subject=CN = git.ssw590.tech
|
|
notBefore=Sep 30 17:50:34 2026 GMT
|
|
notAfter=Dec 29 17:50:33 2026 GMT
|
|
\end{minted}
|
|
|
|
Caddy's automatic certificate management is also responsible for renewal,
|
|
so a separate manual renewal job is not required.
|
|
|
|
\section{Firewall Configuration}
|
|
|
|
The DigitalOcean Cloud Firewall was configured to allow the ports required
|
|
by the deployment:
|
|
|
|
\begin{center}
|
|
\begin{tabular}{lll}
|
|
\textbf{Protocol} & \textbf{Port} & \textbf{Source} \\
|
|
TCP & 22 & All IPv4 \\
|
|
TCP & 80 & All IPv4 and All IPv6 \\
|
|
TCP & 443 & All IPv4 and All IPv6
|
|
\end{tabular}
|
|
\end{center}
|
|
|
|
Port 80 is required for normal HTTP access and HTTP-to-HTTPS redirection,
|
|
while port 443 provides the public HTTPS service. SSH on port 22 is used
|
|
for administration.
|
|
|
|
Port 3000 is not opened in the DigitalOcean firewall. An external
|
|
connection test to the public address on port 3000 timed out, while
|
|
external access to ports 80 and 443 succeeded. This confirms that Gitea
|
|
is reached through Caddy rather than directly through its application port.
|
|
|
|
\section{Restart Behavior}
|
|
|
|
The Compose service uses:
|
|
|
|
\begin{minted}{yaml}
|
|
restart: unless-stopped
|
|
\end{minted}
|
|
|
|
This instructs Docker to restart Gitea after an unexpected container or
|
|
Docker daemon restart, while still allowing an intentional manual stop to
|
|
remain stopped.
|
|
|
|
The deployment can therefore be brought back up with:
|
|
|
|
\begin{minted}{bash}
|
|
docker compose up -d
|
|
\end{minted}
|
|
|
|
and its state checked with:
|
|
|
|
\begin{minted}{bash}
|
|
docker compose ps
|
|
\end{minted}
|
|
|
|
\section{Deployment Summary}
|
|
|
|
The completed startup work hub has the following architecture:
|
|
|
|
\begin{enumerate}
|
|
\item \texttt{ssw590.tech} was registered using the GitHub Student
|
|
Developer Pack offer through Name.com.
|
|
\item \texttt{git.ssw590.tech} was configured to resolve to the
|
|
DigitalOcean Droplet.
|
|
\item Gitea \texttt{1.27.3} was deployed using the official
|
|
\texttt{gitea/gitea} image.
|
|
\item SQLite was selected for the Gitea database.
|
|
\item Gitea application data was stored in the persistent
|
|
\texttt{./data/gitea:/data} bind mount.
|
|
\item Container replacement was tested and the repository and issue
|
|
data persisted.
|
|
\item The \texttt{ssw590-startup} repository was created for team
|
|
collaboration.
|
|
\item Team members were added and issues were assigned.
|
|
\item Pull requests were used to review and merge work into
|
|
\texttt{main}.
|
|
\item Caddy was placed in front of Gitea as a reverse proxy.
|
|
\item Let's Encrypt provided the live TLS certificate for
|
|
\texttt{git.ssw590.tech}.
|
|
\item HTTP was redirected to HTTPS.
|
|
\item Port 3000 remained closed to external clients.
|
|
\end{enumerate}
|
|
|
|
The resulting deployment provides the team with a persistent,
|
|
version-pinned, HTTPS-protected Git service suitable for the startup's
|
|
initial development workflow.
|