diff --git a/README.md b/README.md new file mode 100644 index 0000000..b6a2a4a --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +Gitea Overleaf evidence package + +Files: +- giteaStartup.tex + Chapter fragment to add to the group's main Overleaf document with: + \input{giteaStartup} + +Screenshots: +- gitea-persistence-repository.png + persistence-test repository after replacement +- gitea-persistence-issue-1.png + persistence verification issue #1 +- gitea-admin-activity.png + Gitea activity/repository view +- gitea-pr-issue-1-merged.png + merged pull request for issue #1 +- gitea-pr-issue-2-merged.png + merged pull request for issue #2 + +No passwords are included. diff --git a/gitea-admin-activity.png b/gitea-admin-activity.png new file mode 100644 index 0000000..87c6fe8 Binary files /dev/null and b/gitea-admin-activity.png differ diff --git a/gitea-persistence-issue-1.png b/gitea-persistence-issue-1.png new file mode 100644 index 0000000..cbca36e Binary files /dev/null and b/gitea-persistence-issue-1.png differ diff --git a/gitea-persistence-repository.png b/gitea-persistence-repository.png new file mode 100644 index 0000000..5e437fd Binary files /dev/null and b/gitea-persistence-repository.png differ diff --git a/gitea-pr-issue-1-merged.png b/gitea-pr-issue-1-merged.png new file mode 100644 index 0000000..f59ede0 Binary files /dev/null and b/gitea-pr-issue-1-merged.png differ diff --git a/gitea-pr-issue-2-merged.png b/gitea-pr-issue-2-merged.png new file mode 100644 index 0000000..5bd09d8 Binary files /dev/null and b/gitea-pr-issue-2-merged.png differ diff --git a/giteaStartup.tex b/giteaStartup.tex new file mode 100644 index 0000000..17579f3 --- /dev/null +++ b/giteaStartup.tex @@ -0,0 +1,472 @@ +% 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.