Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Goal
<!-- What does this PR accomplish? 1 sentence. -->

## Changes
-

## Testing
<!-- How did you verify it? -->

## Checklist
- [ ] Title is a clear sentence (≤ 70 chars)
- [ ] Commits are signed (`git log --show-signature`)
- [ ] `submissions/labN.md` updated
59 changes: 59 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
paths:
- 'app/**'
- '.github/workflows/ci.yml'

permissions:
contents: read

defaults:
run:
working-directory: app

jobs:
vet:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
go: ['1.23', '1.24']
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a
with:
go-version: ${{ matrix.go }}
cache-dependency-path: app/go.sum
- run: go vet ./...

test:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
go: ['1.23', '1.24']
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a
with:
go-version: ${{ matrix.go }}
cache-dependency-path: app/go.sum
- run: go test -race -count=1 ./...

lint:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
- uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a
with:
go-version: '1.24'
cache-dependency-path: app/go.sum
- uses: golangci/golangci-lint-action@82606bf257cbaff209d206a39f5134f0cfbfd2ee
with:
version: v2.12.0
working-directory: app
35 changes: 35 additions & 0 deletions submissions/lab1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Lab 1 submission

## Task 1: SSH Commit Signing & First Signed Commit

### `curl` output

![curl output](src/lab01_curl.png)

### `git log` output

![git log output](src/lab01_sign.png)

### Verification output

![verification output](src/lab01_verified.png)

### *why signed commits matter*

Signing a commit cryptographically ties it to an identity, so a reviewer can check that a change really came from the person it claims to, not from someone who just set user.name and user.email to impersonate them. Git makes this easy to fake.
By default, anyone can author a commit. Looking at the xz-utils backdoor: an attacker using the name "Jia Tan" spent months building maintainer trust, then buried a backdoor in a compression library that ships in most Linux distros.
Require signing, keep a record of who signed what, and an unexpected or unverifiable commit sticks out instead of blending into the history. That alone won't stop a determined attacker, but it raises the cost.

## Task 2: Pull Request Template & First PR

![PR Template](src/lab01_PR_template.png)

## Task 3: GitHub Community Engagement

- **Why starring repositories matters in open source:**

Starring is both a bookmark and a signal: it saves a project to your profile for later and publicly endorses it, and aggregate star counts act as a rough trust/popularity signal that helps others discover worthwhile tools and motivates maintainers who mostly work for free.

- **How following developers helps in team projects and professional growth:**

Following developers turns GitHub into a feed of what your teammates and the wider community are building, you see their new projects and activity, which makes it easier to coordinate on team work, learn from how others structure code, and build the professional network that carries past a single course.
66 changes: 66 additions & 0 deletions submissions/lab2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Lab 2 submission

## Task 1: Git Object Model + Reflog Recovery

### 1.1: Explore your repo's plumbing

- **`HEAD`:**

![head](src/lab02_head.png)

- **tree:**

![tree](src/lab02_tree.png)

- **blob:**

![blob](src/lab02_blob.png)

- **file contents:**

![file contents](src/lab02_file.png)

### 1.2 Inside `.git/`

### 1.2 — .git/ interpretation

- `cat .git/HEAD``ref: refs/heads/feature/lab1`: it stores the current branch name, not a commit SHA.
- `ls .git/refs/heads/``feature main`: `feature` is a directory, not a branch. Slashes in branch names (`feature/lab1`, `feature/lab2`) become real folders, so a branch is just a file holding a commit SHA.
- `.git/objects/`: loose objects sharded into dirs by the first 2 chars of their SHA.
- `find .git/objects -type f | wc -l` counted 34 files

![git folder](src/lab02_git_folder.png)

### 1.3: Simulate disaster + recover

- **`git reflog` output:**

![reflog](src/lab02_reflog.png)

- **`git reset --hard` output:**

![reset](src/lab02_reset.png)

- ***what would happen if git gc had run between the bad reset and your recovery?***

`git gc` prunes unreachable objects, but it respects the reflog and a grace period: by default it only prunes objects older than two weeks and keeps reflog entries for 90 days. Because the reflog still referenced my two commits, an ordinary git gc would not have deleted them, they were seconds old and still reachable via the reflog. The real danger is an aggressive prune that ignores the grace window — `git gc --prune=now` or `git reflog expire --expire=now --all` followed by `git prune` which removes unreachable objects immediately, if that had run, the commits would be unrecoverable.

## Task 2: Tag a Release & Rebase a Feature

### 2.1: Signed release tag

![release tag verification](src/lab02_tags_verification.png)

![release tag verification remote](src/lab02_tags_verification2.png)

### 2.2: Rebase

- **`git log --oneline` output:**

![rebase log before](src/lab02_before_rebase.png)

![rebase log after](src/lab02_after_rebase.png)

- ***When you'd choose merge vs rebase?***

We can use rebase for personal projects or when having a linear history is a priority, but for large teams with many contributors, merge is safer to avoid the risk of losing commits or causing confusion by rewriting history.
77 changes: 77 additions & 0 deletions submissions/lab3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Lab 3 submission

## Task 1: Write the PR Gate

### Why GitHub?

Because I can sign in to github.com and my fork already lives there.

### Green CI run

[Clear CI run](https://github.com/sparrow12345/DevOps-Intro/actions/runs/27569104479)

### Failed run and fix

![Failed run](src/lab03_broken_ci.png)

![Fix](src/lab03_fix_commit.png)

### Branch protection

![Branch rule](src/lab03_branch_rule.png)

![Rule applied](src/lab03_rules_PR.png)

### Questions

**a) Why pin the runner version (`ubuntu-24.04`) instead of ubuntu-latest? What breaks otherwise?**

`ubuntu-latest` is a moving alias. GitHub repoints it to the next LTS on their schedule, and when they do, the preinstalled toolchain change. Which can cause pipelines that were working fine to break. By pinning `ubuntu-24.04` we make sure that the pipeline won't break unexpectedly when GitHub updates their runners. And we can handle the update by ourselves.

**b) Why split vet + test + lint into separate units? What would happen with one combined job?**

- Parallelism: separate jobs run on separate runners simultaneously, since they're not dependent on each other.

- Signal: separate jobs can tell us exactly where our problem is, ex: (vet -> code error/bugs, test -> wrong code logic/broken test, lint -> style issues).

**c) GH path: what real attack does SHA pinning prevent?**

A tag-hijack / supply-chain injection against a third-party action. A Git tag like @v4 is a mutable pointer, whoever controls the action's repo can move it to point at new, malicious code, and every workflow referencing @v4 silently pulls it on the next run. This is exactly the tj-actions/changed-files compromise of March 2025: attackers gained write access and repointed the action's tags to a malicious version, leaking a loot of secrets from multiple runners. By pinning to a specific SHA, we ensure that we're always running the exact code we reviewed and tested, and not a moving target that can be hijacked.

**d) GH path: what is `permissions:` and what's the principle behind it?**

Control the access level of the GITHUB_TOKEN. We can restrict it to only the permissions we need for our workflow. This follows the principle of least privilege, which states that we should only grant the minimum permissions necessary for a task, reducing the attack surface and potential damage if the token is compromised.

## Task 2: Make It Fast and Smart

### Measurements table

![Measurements](src/lab03_CI_timers.png)

| Scenario | Wall-clock |
|----------|-----------|
| Baseline (no cache, single Go version, no path filter) | 41 s |
| With cache | 37 s |
| With cache + matrix | 43 s |

### Description of each optimization

**Caching:** `setup-go` caches the Go module and build caches for us automatically once we point it at the lockfile with `cache-dependency-path: app/go.sum`. The key comes from the `go.sum` hash, so it stays valid until a dependency actually changes.

**Go matrix (1.23 + 1.24):** `vet` and `test` run against both Go versions in parallel, which catches bugs that only show up on one toolchain. Lint stays on 1.24 since it only needs to run once. The cells run on separate runners, so the matrix barely adds wall-clock.

**Path filter:** The `pull_request` trigger is scoped to `app/**` and the workflow file, so a docs-only PR (like a README edit) doesn't trigger any runs and wastes no CI minutes.

### Questions

**f) Why cache `go.sum`-keyed inputs and not build outputs?**

`go.sum` pins the exact version of every module, so a cache keyed on its hash is safe: if the hash matches, the dependencies are the same, and as soon as one changes the key changes with it. Build outputs don't have that property. Compiled archives and binaries bake in the toolchain version, build tags, and paths, so restoring an old one can give a green build that doesn't match your actual source.

**g) What does `fail-fast: false` change in a matrix run, and when do you actually want `fail-fast: true`?**

In a matrix, `fail-fast: true` cancels every still-running cell the moment one cell fails. With `fail-fast: false`, all cells run to completion regardless. We want `false` here because the whole reason for the matrix is to compare go `1.23` and `1.24` and `fail-fast` might kill one of the cells. We'd choose `fail-fast: true` when cells we want all cells to pass like in a large sharded test matrix where any single failure already means "block the merge" and we'd rather save CI minutes and give fast feedback than collect every failure.

**h) What's the risk of an attacker writing a cache from a malicious PR that protected branches later read?**

The risk is cache poisoning. A PR from a fork runs CI and can write to the shared cache, and if a protected branch later reads that cache, it runs whatever the attacker stuffed in there. A poisoned module or build cache basically becomes code execution in a trusted context, with access to real secrets.
Binary file added submissions/src/lab01_PR_template.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab01_curl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab01_sign.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab01_verified.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_after_rebase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_before_rebase.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_blob.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_file.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_git_folder.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_head.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_reflog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_reset.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_tags_verification.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_tags_verification2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab02_tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab03_CI_timers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab03_branch_rule.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab03_broken_ci.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab03_fix_commit.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added submissions/src/lab03_rules_PR.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.