Skip to main content

2 posts tagged with "workflow"

View All Tags

Local Deployment and a Glimpse Into Trek's CI/CD System

· 7 min read
William Xiao
Developer

One of our goals as the Trekkers was always to be able to have a cost-effective but performant deployment of Trek. As someone who self-hosts their own server for their personal site, I thought it would be quite suiting to see if our current needs for deployment and CI/CD could be met through repurposing part of self-hosted solution.

My Site

As a little bit of background, my personal site (https://wyfx.ca) was originally started as just a way for me to take the Raspberry Pi I had sitting at home and make it something useful.

The site was developed under Vite, and deployed under a Docker container running nginx. Whenever I have a new version of the site, I build a new version of the Docker container, then manually restart the container.

At Trek, one of our priorities is always knowing about the status of our product. Hence, with some of my knowledge on the team, we aimed to leverage that knowledge to develop our own self-hosted solution for development purposes.

Considerations

On the implementation side, moving from a simple statically hosted website to an integrated solution with our GitHub workflow would not be easy. Our goals for Trek's deployment solution would be something that is integrated with GitHub, performant enough to run on my Raspberry Pi, and automatic. This solution would be key for our CI/CD process and would ensure that we'd able to quickly iterate on new updates to our codebase.

Security

Security was especially important to me as we needed a solution that would not expose my self-hosted server to too much risk, but would still be able to automatically integrate with GitHub. My home server did not have a publicly exposed SSH port, yet without one we would not be able to upload files to the server. However, as my main account on the server was an administrator, I did not want any possible SSH solutions to have full access to the administrator account.

Performance

The second primary consideration with our design was performance. This includes both the "visible" parts of the site (like API and frontend), but also the build process from a successful push to GitHub. While we would not have to worry about a large amount of users on the development site, we still wanted a solution that would be performant enough for us to evaluate how the site would behave for production deployment in the future. The limited compute power of the Raspberry Pi also means that this deployment needs to be as lean as possible.

Maintainability

The last key consideration was maintainability. The development of Trek moves quickly, so our system should be able to adapt to those changes quickly. Without a maintainable system, the system could quickly become obsolete - sacrificing valuable developer time.


With all of our considerations in mind, we started the design of the CI/CD system.

Deployment

Since I was most familiar with a Docker container setup like my home server, my initial thoughts were to create another container running on the server at the same time as my personal site. So the first challenge was coming up with a deployment that would allow both Docker containers to run at the same time while redirecting to different domains. My solution to this issue was to use nginx-proxy. nginx-proxy essentially acts as a reverse proxy that routes to Docker containers by subdomain. Now, I can start my own website's container with an environment variable VIRTUAL_HOST=wyfx.ca, while having Trek on a subdomain like SUBDOMAIN.wyfx.ca. To handle HTTPS, I extended my original LetsEncrypt certificates using acme-companion, which will automatically generate a new HTTPS certificate for each of my subdomains by specifying LETSENCRYPT_HOST=SUBDOMAIN.wyfx.ca.

The largest decision to make was how to get updates from our GitHub repository. As I didn't want to expose a public SSH port on my home server, I initially thought about a solution that wouldn't directly connect the GitHub repository to the server. Instead, the server could poll for changes on GitHub, and when changes are detected, the following would occur:

  1. Pull the changes from GitHub
  2. Build a new container
  3. Restart the running container

However, we did not end up going with this idea for a few reasons:

  • Unless we polled at a very fast rate, there was still going to be a delay before we could even detect a change from GitHub
  • There were concerns with performance from the fact that the Raspberry Pi would have to do all the building
  • We would have to create our own scripts to make the polling + building possible, which would each cost us maintenance time

The solution we ended up using for the frontend was to create a nginx Docker container that is also running a SSH server. Then, to update the frontend, we could write a GitHub Action to build the frontend, then SSH into the Docker container and replace the static files in the container. This solution was much simpler, which would make it easier to maintain. It also does not involve constantly rebuilding the Docker container, as we would be making local updates to its filesystem instead. In addition, having the publicly exposed SSH port be into a container would gave me some more comfort that any attacks would not be able to immediately access my entire server (though such attacks are possible).

The frontend workflow

Our GitHub action to build and upload the frontend is included in the Appendix. It builds our frontend using npm, and then uploads it via SCP to the Docker container using appleboy/scp-action. This ended up looking like the following:

name: Deploy Frontend Dev
on:
push:
branches:
- "project_[0-9]-dev"
jobs:
deploy:
name: Deploy FE Dev
runs-on: ubuntu-latest
concurrency: deploy-group # optional: ensure only one action runs at a time

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Build FE
run: |
cd frontend
npm install
npm run build

- name: Upload to server
uses: appleboy/scp-action@v0.1.7
with:
host: REDACTED
username: REDACTED
password: REDACTED
port: REDACTED
source: "frontend/dist/*"
target: REDACTED
overwrite: true
strip_components: 2

For the backend deployment, we built off of the frontend deployment. Our backend deployment uses tsc-watch to start up our backend server, and then monitor for file changes. Then, a GitHub action is triggered whenever a push has been made to our dev branches, uploading the files via SCP. tsc-watch is built to automatically detect those changes and rebuild the backend dynamically as its files change. To support the communication between our frontend and backend, the backend runs under the same nginx server as the frontend. Requests to our API endpoints (e.g. /api/v1/users) would then be forwarded to the backend server, allowing us to remain under the same subdomain and avoid spawning too many Docker containers for the server to handle. To make this happen, our nginx.conf includes the following:

server {
location /socket.io {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass REDACTED;
}
location / {
root REDACTED;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass REDACTED;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

By overwriting what happens with the location /api line configuration, we are able to redirect requests to go to the backend server rather than still going to the frontend.

The Future

As Trek continues to develop, our CI/CD system will have to evolve too. One of our upcoming goals for a future sprint is to integrate testing frameworks into both our frontend and backend. When our backend develops more, we might also find that the self-hosted server won't be powerful enough. However, with our maintainable and reproducible system, we are well-equipped to handle these future challenges with ease.

A Deep Dive into Trekkers Project Workflow

· 6 min read
Matthew Kang
Developer

Our primary organizational goal at Trekkers is to develop software using the best industry practices we learn from workshops, lab assignments, and our project experiences. When building Trek, we aimed to adopt Agile values and principles effectively in our project workflow.

The CPSC 455 course, titled "Applied Industry Practices," emphasizes industry practices. However, being Agile is not about merely adopting the most popular industry practices but about embracing practices that support our principles and values. Simply following the most popular "Agile" practices doesn't make us an Agile development team. We strive to be more than just another shop that claims to be Agile.

Manifesto for Agile Software Development:

  • Individuals and interactions over processes and tools
  • Working software over comprehensive documentation
  • Customer collaboration over contract negotiation
  • Responding to change over following a plan

In our development journey, we have chosen to follow Agile principles because they align with our commitment to continuous improvement, collaboration, and delivering value to our users. Our adaptation of Scrum reflects our dedication to these principles, the Trekkers Code, and our desire to create an effective and efficient workflow that supports our project goals.

Our Version of Scrum

Our version of Scrum aims to be as Agile as possible while meeting all project requirements and learning objectives.

Weekly Sprint Schedule

Individual (non-Trekkers) Course Assignments and Deadlines are italicized

DaySchedule
SundayStart of Sprint Meeting @ 10:00 AM
Bi-weekly Formal Scrum due @ 10:00 pm
MondayBi-Weekly Individual Assignments Due @ 10:00 PM
Tuesday
Wednesday
ThursdayMid-Sprint Check-in Meeting @ 5:00 PM
Planning for next sprint or WIP Progress presentation.
FridayBi-Weekly Individual Workshop Survey Due on 11:59 PM
SaturdayEnd of Sprint (Push @ 10:00 AM)
Sprint Review: Read other members' scrum reports.
  • Our sprint begins on Sunday @ 10:00 AM and ends on Saturday @ 10:00 AM.
  • During the Start of Sprint Meeting we review our scrum report (Sprint Review) on what we have accomplished in the past sprint, and formalize new sprint goals.
  • During the Mid-Sprint Check-in Meeting (happens during Labs), we do a brief check-in on the progress of our sprints.
    • Based on our progress, we do some planning for what we should do on the next sprint
    • On weeks where there are Workshop Presentations, we do planning for presentations, writing documentations, etc.
  • Every Saturday @ 10:00 AM is the End of Sprint. Everything should be pushed by then.
    • By noon, every completed issues that have not been closed yet should be closed.
    • By noon, everyone should complete a very brief scrum report. We use these weekly internal scrum reports for bi-weekly external (submittable) scrum reports.

Meeting Agenda Template

Our Scrum meetings are structured on agile principles of communication, interaction, and collaboration. We have structured a meeting agenda flexible template that lets us stay focused on reviewing sprints and internal demos, while letting us plan for our next sprint.

Our meeting agenda template exists as a GitHub issues template (screenshots below).

Issue Template Issue Template 2

GitHub Issues for Project Management

We decided not to use GitHub Projects because we couldn't adjust the visibility settings of the project board. We wanted to create a collaborative environment where team members could actively update their progress without it being publicly visible to other classmates.

Instead, we are using GitHub Issues as our main platform for project management. Our belief is that with effective scrum meetings, the utilization of GitHub Issues can facilitate clear communication and tracking of tasks and promote Agility. Here's how we leverage GitHub Issues in our workflow:

Creating Issues

Mostly after each scrum meeting, every task, bug, or feature is logged as an issue. This ensures that all work items are tracked and nothing is missed. Issues are tagged with appropriate labels (e.g., frontend, backend, bug, documentation) to categorize and prioritize them effectively.

Assigning Issues

During the scrum meetings, each issue is assigned to a specific team member based on their expertise and current workload. We use GitHub's assignee feature to make sure responsibilities are clear.

Tracking Progress:

Issues are updated regularly with comments and status updates. We use Discord as our primary platform for communication, including discussions of issues. Our Discord consists of channels for each type of work (frontend, backend, documentation, etc). We associate branches with issues using GitHub's branch linking feature, which makes peer-review easier.

Sprint Planning and Closing Issues

At the start of each sprint, we create, review, and prioritize issues. High-priority tasks are marked with the "PRIORITY" tag. During the End of Sprint, we review all closed issues. We use the scrum meeting agenda (as an issue), where we link the closed issues for discussions.

Continuous Integration and Deployment

To ensure the quality and stability of our software, we have set up continuous integration and deployment (CI/CD) pipelines:

Automated Testing

Every push to the repository's "progress" branch triggers automated tests. This helps us catch issues early in the development cycle. We aim to maintain a high code coverage to ensure the robustness of our application.

Code Reviews

All code changes are submitted through pull requests (PRs). PRs must pass automated tests and be reviewed by at least one other team member before being merged. This practice helps us maintain code quality and fosters knowledge sharing within the team.

Continuous Deployment

Once a PR is approved and merged, our CI/CD pipeline automatically deploys the latest code to our private staging environment. We perform final checks in the staging environment before promoting changes to the released demo environment along with release notes published on our website.

Our Workflow is Evolving

By adhering to Agile principles and customizing our workflow to fit our team's needs, we strive to deliver high-quality software efficiently. Our structured yet flexible approach to Scrum, combined with the effective use of GitHub Issues and CI/CD pipelines, enables us to stay organized, collaborative, and adaptive. We continuously seek to improve our processes and deliver value through iterative development and frequent feedback during scrum meetings and workshop design reviews.

Our workflow is constantly evolving as we learn and adapt. We are committed to refining our practices, incorporating new insights, and staying responsive to the changing needs of our project and team. This continuous evolution helps us stay agile and ensures that we can meet our goals effectively.

We hope that our detailed project workflow provides insight into how we manage our development process and can serve as our own reference for our future development journeys.