diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..1a68db5e5 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Goal + + +## Changes +- + +## Testing + + +## Checklist +- [ ] Title is a clear sentence (≤ 70 chars) +- [ ] Commits are signed (`git log --show-signature`) +- [ ] `submissions/labN.md` updated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..7aa3c563c --- /dev/null +++ b/.github/workflows/ci.yml @@ -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 \ No newline at end of file diff --git a/submissions/lab1.md b/submissions/lab1.md new file mode 100644 index 000000000..22b40d35b --- /dev/null +++ b/submissions/lab1.md @@ -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. diff --git a/submissions/lab2.md b/submissions/lab2.md new file mode 100644 index 000000000..991c12a6a --- /dev/null +++ b/submissions/lab2.md @@ -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. diff --git a/submissions/lab3.md b/submissions/lab3.md new file mode 100644 index 000000000..44697b340 --- /dev/null +++ b/submissions/lab3.md @@ -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. diff --git a/submissions/src/lab01_PR_template.png b/submissions/src/lab01_PR_template.png new file mode 100644 index 000000000..6112dc518 Binary files /dev/null and b/submissions/src/lab01_PR_template.png differ diff --git a/submissions/src/lab01_curl.png b/submissions/src/lab01_curl.png new file mode 100644 index 000000000..682fdc0c4 Binary files /dev/null and b/submissions/src/lab01_curl.png differ diff --git a/submissions/src/lab01_sign.png b/submissions/src/lab01_sign.png new file mode 100644 index 000000000..902c418a5 Binary files /dev/null and b/submissions/src/lab01_sign.png differ diff --git a/submissions/src/lab01_verified.png b/submissions/src/lab01_verified.png new file mode 100644 index 000000000..ce5efce1a Binary files /dev/null and b/submissions/src/lab01_verified.png differ diff --git a/submissions/src/lab02_after_rebase.png b/submissions/src/lab02_after_rebase.png new file mode 100644 index 000000000..3cd257e3d Binary files /dev/null and b/submissions/src/lab02_after_rebase.png differ diff --git a/submissions/src/lab02_before_rebase.png b/submissions/src/lab02_before_rebase.png new file mode 100644 index 000000000..73f101cb5 Binary files /dev/null and b/submissions/src/lab02_before_rebase.png differ diff --git a/submissions/src/lab02_blob.png b/submissions/src/lab02_blob.png new file mode 100644 index 000000000..a27f524bd Binary files /dev/null and b/submissions/src/lab02_blob.png differ diff --git a/submissions/src/lab02_file.png b/submissions/src/lab02_file.png new file mode 100644 index 000000000..aa63b44d9 Binary files /dev/null and b/submissions/src/lab02_file.png differ diff --git a/submissions/src/lab02_git_folder.png b/submissions/src/lab02_git_folder.png new file mode 100644 index 000000000..abbe00a36 Binary files /dev/null and b/submissions/src/lab02_git_folder.png differ diff --git a/submissions/src/lab02_head.png b/submissions/src/lab02_head.png new file mode 100644 index 000000000..8c45adb37 Binary files /dev/null and b/submissions/src/lab02_head.png differ diff --git a/submissions/src/lab02_reflog.png b/submissions/src/lab02_reflog.png new file mode 100644 index 000000000..4d86622fb Binary files /dev/null and b/submissions/src/lab02_reflog.png differ diff --git a/submissions/src/lab02_reset.png b/submissions/src/lab02_reset.png new file mode 100644 index 000000000..010714a50 Binary files /dev/null and b/submissions/src/lab02_reset.png differ diff --git a/submissions/src/lab02_tags_verification.png b/submissions/src/lab02_tags_verification.png new file mode 100644 index 000000000..73aac9b30 Binary files /dev/null and b/submissions/src/lab02_tags_verification.png differ diff --git a/submissions/src/lab02_tags_verification2.png b/submissions/src/lab02_tags_verification2.png new file mode 100644 index 000000000..32363854b Binary files /dev/null and b/submissions/src/lab02_tags_verification2.png differ diff --git a/submissions/src/lab02_tree.png b/submissions/src/lab02_tree.png new file mode 100644 index 000000000..3bfc0cf6f Binary files /dev/null and b/submissions/src/lab02_tree.png differ diff --git a/submissions/src/lab03_CI_timers.png b/submissions/src/lab03_CI_timers.png new file mode 100644 index 000000000..6c3b5aa93 Binary files /dev/null and b/submissions/src/lab03_CI_timers.png differ diff --git a/submissions/src/lab03_branch_rule.png b/submissions/src/lab03_branch_rule.png new file mode 100644 index 000000000..5550b9f04 Binary files /dev/null and b/submissions/src/lab03_branch_rule.png differ diff --git a/submissions/src/lab03_broken_ci.png b/submissions/src/lab03_broken_ci.png new file mode 100644 index 000000000..aea5d8349 Binary files /dev/null and b/submissions/src/lab03_broken_ci.png differ diff --git a/submissions/src/lab03_fix_commit.png b/submissions/src/lab03_fix_commit.png new file mode 100644 index 000000000..f4b29b059 Binary files /dev/null and b/submissions/src/lab03_fix_commit.png differ diff --git a/submissions/src/lab03_rules_PR.png b/submissions/src/lab03_rules_PR.png new file mode 100644 index 000000000..d2b017491 Binary files /dev/null and b/submissions/src/lab03_rules_PR.png differ