diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..5303cb928 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Goal +- What does this PR accomplish? + +## Changes +- What was modified? + +## Testing +- What was modified? + +### Checklist: +- [ ] Clear title and description. +- [ ] Documentation/README updated if needed. +- [ ] No secrets or large temporary files. \ No newline at end of file diff --git a/.github/workflows/github-actions-demo.yml b/.github/workflows/github-actions-demo.yml new file mode 100644 index 000000000..2b06c9fd6 --- /dev/null +++ b/.github/workflows/github-actions-demo.yml @@ -0,0 +1,44 @@ +name: GitHub Actions Demo +run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 + +on: + workflow_dispatch: + +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "Triggered by ${{ github.event_name }}" + + - name: Check out repository code + uses: actions/checkout@v5 + + - name: System info + run: | + echo "" + echo "========================================" + echo "SYSTEM INFORMATION" + echo "========================================" + + echo "" + echo "--- RUNNER INFORMATION ---" + echo "Runner name: ${{ runner.name }}" + echo "Runner OS: ${{ runner.os }}" + echo "Runner architecture: ${{ runner.arch }}" + + echo "" + echo "--- OPERATING SYSTEM DETAILS ---" + cat /etc/os-release + + echo "" + echo "--- HARDWARE SPECIFICATIONS ---" + echo "CPU Model: $(lscpu | grep 'Model name' | cut -d':' -f2 | xargs)" + echo "CPU Cores: $(nproc)" + echo "CPU Architecture: $(uname -m)" + echo "Total Memory: $(free -h | grep 'Mem:' | awk '{print $2}')" + echo "Available Memory: $(free -h | grep 'Mem:' | awk '{print $7}')" + echo "Disk Size: $(df -h / | tail -1 | awk '{print $2}')" + echo "Disk Usage: $(df -h / | tail -1 | awk '{print $5}')" + + echo "" + echo "========================================" \ No newline at end of file diff --git a/labs/lab11/app/Dockerfile b/labs/lab11/app/Dockerfile new file mode 100644 index 000000000..edc5d235e --- /dev/null +++ b/labs/lab11/app/Dockerfile @@ -0,0 +1,4 @@ +FROM golang:1.22 +WORKDIR /app +COPY main.go . +RUN go build -o app main.go \ No newline at end of file diff --git a/labs/lab11/app/default.nix b/labs/lab11/app/default.nix new file mode 100644 index 000000000..c8a7d59d3 --- /dev/null +++ b/labs/lab11/app/default.nix @@ -0,0 +1,12 @@ +{ pkgs ? import {} }: + +pkgs.buildGoModule rec { + pname = "app"; + version = "0.1.0"; + + src = ./.; + + vendorHash = null; + + doCheck = false; +} \ No newline at end of file diff --git a/labs/lab11/app/docker.nix b/labs/lab11/app/docker.nix new file mode 100644 index 000000000..b6ab660a1 --- /dev/null +++ b/labs/lab11/app/docker.nix @@ -0,0 +1,19 @@ +{ pkgs ? import {} }: + +let + app = pkgs.buildGoModule { + pname = "app"; + version = "0.1.0"; + src = ./.; + vendorHash = null; + doCheck = false; + }; +in +pkgs.dockerTools.buildLayeredImage { + name = "nix-app"; + tag = "latest"; + contents = [ app ]; + config = { + Entrypoint = [ "${app}/bin/app" ]; + }; +} \ No newline at end of file diff --git a/labs/lab11/app/flake.nix b/labs/lab11/app/flake.nix new file mode 100644 index 000000000..7c1d15b1c --- /dev/null +++ b/labs/lab11/app/flake.nix @@ -0,0 +1,32 @@ +{ + description = "Reproducible Go app with Nix"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + + app = pkgs.buildGoModule { + pname = "app"; + version = "0.1.0"; + src = ./.; + vendorHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + doCheck = false; + }; + in + { + packages.${system}.default = app; + + dockerImages.${system}.default = pkgs.dockerTools.buildLayeredImage { + name = "nix-app"; + contents = [ app ]; + config.Entrypoint = [ "${app}/bin/app" ]; + }; + + devShells.${system}.default = pkgs.mkShell { + buildInputs = [ pkgs.go pkgs.gopls ]; + }; + }; +} \ No newline at end of file diff --git a/labs/lab11/app/go.mod b/labs/lab11/app/go.mod new file mode 100644 index 000000000..a53ad4fac --- /dev/null +++ b/labs/lab11/app/go.mod @@ -0,0 +1,3 @@ +module app + +go 1.24.3 diff --git a/labs/lab11/app/main.go b/labs/lab11/app/main.go new file mode 100644 index 000000000..c0ded1f3e --- /dev/null +++ b/labs/lab11/app/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + fmt.Printf("Built with Nix at compile time\n") + fmt.Printf("Running at: %s\n", time.Now().Format(time.RFC3339)) +} \ No newline at end of file diff --git a/labs/screenshot/Img1.png b/labs/screenshot/Img1.png new file mode 100644 index 000000000..283468ebe Binary files /dev/null and b/labs/screenshot/Img1.png differ diff --git a/labs/screenshot/Img2.png b/labs/screenshot/Img2.png new file mode 100644 index 000000000..1b4a35561 Binary files /dev/null and b/labs/screenshot/Img2.png differ diff --git a/labs/submission1.md b/labs/submission1.md new file mode 100644 index 000000000..7c12f6517 --- /dev/null +++ b/labs/submission1.md @@ -0,0 +1,13 @@ +# Lab 1 Submission +## Task 1 SSH Commit Signature Verification: +The signature of commits provides a cryptographic guarantee of authorship and code integrity, preventing forgery of commits and protecting against the introduction of malicious changes on someone else's behalf. In DevOps practices, this is critically important, since CI/CD systems trust only verified commits for automatic builds and deployments, which creates an additional level of security in the software supply chain. Signed commits also ensure compliance with security standards, provide an auditable trace of changes, and are displayed on platforms like GitHub with the "Verified" label, increasing trust in the code in team and open-source development. +#### Screenshots for Task 1: +![Verified Commit](screenshot/img1.png) + +PR templates standardize the code review process, forcing developers to describe changes in a structured manner. All PRS receive the same sections ("Purpose", "Changes", "Testing"), which saves reviewers time — they do not need to search for information in chaotic descriptions. Checklists automatically remind you of important actions.: tests, documentation, code style. In distributed teams, templates compensate for the lack of personal communication — a reviewer in a different time zone receives all the necessary information without additional questions. +## Task 2 PR Template & Checklist: +PR templates standardize the code review process, forcing developers to describe changes in a structured manner. All PR receive the same sections ("Purpose", "Changes", "Testing"), which saves reviewers time — they do not need to search for information in chaotic descriptions. Checklists automatically remind you of important actions.: tests, documentation, code style. In distributed teams, templates compensate for the lack of personal communication — a reviewer in a different time zone receives all the necessary information without additional questions. + +Challenges emerged during setup. I have some problem, while do Signing keys. +#### Screenshots for Task 2: +![PR template auto-filling the description.](screenshot/img2.png) \ No newline at end of file diff --git a/labs/submission11.md b/labs/submission11.md new file mode 100644 index 000000000..070c1de95 --- /dev/null +++ b/labs/submission11.md @@ -0,0 +1,214 @@ +# Lab 11 — Reproducible Builds with Nix + +**Platform:** WSL2 (Ubuntu 24.04.2 LTS) on Windows 11 +**Student:** Lev Permiakov +**Date:** 2026-05-04 + +--- + +## Task 1 — Build Reproducible Artifacts from Scratch + +### 1.1 Environment Setup + +``` +# Install Nix (Determinate Systems) +curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install +exec $SHELL +nix --version +# Output: nix (Determinate Nix 3.19.0) 2.34.6 +``` + +### 1.2 Application Files + +**main.go:** +``` +package main +import ( + "fmt" + "time" +) +func main() { + fmt.Printf("Built with Nix at compile time\n") + fmt.Printf("Running at: %s\n", time.Now().Format(time.RFC3339)) +} +``` + +**go.mod:** +``` +module app +go 1.22 +``` + +**default.nix:** +``` +{ pkgs ? import {} }: +pkgs.buildGoModule rec { + pname = "app"; + version = "0.1.0"; + src = ./.; + vendorHash = null; # no external dependencies + doCheck = false; + modVendor = false; +} +``` + +### 1.3 Reproducibility Verification + +**First build:** +``` +$ nix-build +/nix/store/r7kcjs6976b4bviwyww1qzr0h6jl5q1r-app-0.1.0 + +$ readlink result +/nix/store/r7kcjs6976b4bviwyww1qzr0h6jl5q1r-app-0.1.0 + +$ sha256sum ./result/bin/app +99efa7adac0d8fa51ce4088f1c1a9edf9e70f21c2f8bbe885ca25d16dcded35f ./result/bin/app +``` + +**Second build (after `rm result`):** +``` +$ nix-build +/nix/store/r7kcjs6976b4bviwyww1qzr0h6jl5q1r-app-0.1.0 # identical path + +$ sha256sum ./result/bin/app +99efa7adac0d8fa51ce4088f1c1a9edf9e70f21c2f8bbe885ca25d16dcded35f # identical hash +``` + +### 1.4 Docker Comparison (Non-Reproducible) + +**Dockerfile:** +``` +FROM golang:1.22 +WORKDIR /app +COPY main.go . +RUN go build -o app main.go +``` + +**Build results:** +``` +# First build: 25.9s, manifest sha256:fc25df0714bc7a2c902c4988f22dae3e1f34e2848f9875b64672c06a5e662bbf +# Second build: 1.0s (cached), attestation manifest sha256:d2051970a0e94700862b511a4fc7900d6edef1edd519fb0e74f81912a6469ec2 +``` + +**Observation:** Despite layer caching, Docker generates different metadata hashes (attestation manifests, config) between builds due to timestamps and build context variations. + +### 1.5 Analysis: Why Nix is Reproducible + +| Factor | Nix | Traditional Docker | +|--------|-----|-------------------| +| **Dependency resolution** | Pinned via nixpkgs hash | `apt-get`/`go get` may fetch newer versions | +| **Build isolation** | Sandboxed, pure environment | Inherits host environment variables | +| **Output addressing** | Content-addressable store path | Layers with timestamps and metadata | +| **Hash determinism** | Same inputs → same path | Same Dockerfile ≠ same image hash | + +**Nix Store Path Format:** +`/nix/store/--` +- ``: SHA-256 of all build inputs (source, dependencies, build script, environment) +- Guarantees: identical inputs → identical outputs + +--- + +## Task 2 — Reproducible Docker Images with Nix + +### 2.1 Nix Docker Definition (`docker.nix`) + +``` +{ pkgs ? import {} }: +let + app = pkgs.buildGoModule { + pname = "app"; + version = "0.1.0"; + src = ./.; + vendorHash = null; + doCheck = false; + modVendor = false; + }; +in +pkgs.dockerTools.buildLayeredImage { + name = "nix-app"; + tag = "latest"; + contents = [ app pkgs.cacert ]; + config = { + Entrypoint = [ "${app}/bin/app" ]; + Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ]; + }; + created = "1970-01-01T00:00:01Z"; # fixed timestamp +} +``` + +### 2.2 Build and Execution + +``` +# Convert line endings (important for WSL/Windows) +$ dos2unix docker.nix default.nix + +# Build image +$ nix-build docker.nix +/nix/store/s93ki1a13y5iba2n5fq5bi8iwlbvhwnx-nix-app.tar.gz + +# Load into Docker (result is a file, not a directory!) +$ docker load < result +Loaded image: nix-app:latest + +# Check size +$ docker images | grep nix-app +nix-app latest 2b4bb772d276 56 years ago 11.9MB + +# Run container +$ docker run --rm nix-app:latest +Built with Nix at compile time +Running at: 2026-05-04T08:54:39Z +``` + +### 2.3 Reproducibility Proof + +**First image build:** +``` +$ sha256sum result +b6031c47c41dafbf8678b0db1355565b93794c6f50ec6ea9789f7ea8a0b7b23a result +``` + +**Second build (after `rm result`):** +``` +$ nix-build docker.nix +/nix/store/bj3317qsim9d5l3b0hb43wqm841083mg-nix-app.tar.gz + +$ sha256sum result +b6031c47c41dafbf8678b0db1355565b93794c6f50ec6ea9789f7ea8a0b7b23a result # identical hash +``` + +### 2.4 Comparison: Nix vs Traditional Docker + +| Characteristic | Nix Image (`nix-app`) | Traditional (`test-app`) | +|---------------|----------------------|--------------------------| +| **Size** | 11.9 MB | ```850 MB | +| **Base Image** | scratch + minimal dependencies | golang:1.22 (full) | +| **Layers** | 3 deterministic | 12+ with timestamps | +| **`created` label** | `1970-01-01T00:00:01Z` (fixed) | Current build time | +| **Reproducibility** | Bit-for-bit identical hashes | Metadata hashes change | + +**Conclusion:** `dockerTools` creates minimal images without timestamps or extra layers, ensuring full bit-for-bit reproducibility. + +--- + +## Challenges and Solutions + +| Problem | Solution | +|----------|---------| +| **CRLF line endings** | `dos2unix docker.nix default.nix` before building | +| **`vendorHash` error** | Set `vendorHash = null` for projects without external dependencies | +| **`result` is a file, not a directory** | Use `docker load < result`, not `result/nix-app.tar.gz` | +| **Paths with spaces in WSL** | Use quotes: `cd "/mnt/c/Users/.../lab11/app"` | + +--- + +## Summary + +Both tasks were completed successfully: + +- **Task 1:** Built a Go application with Nix and verified bit-for-bit reproducibility across multiple builds. Store path and SHA-256 hash remained identical — proving that Nix eliminates environment-dependent variations. + +- **Task 2:** Produced a minimal Docker image (11.9 MB) using `dockerTools`. Unlike traditional Docker builds, the Nix-built image uses fixed timestamps and deterministic layers, resulting in identical tarball hashes on every rebuild. + +The key takeaway is that Nix enforces reproducibility at the build level: every input is explicitly declared, the build environment is sandboxed, and outputs are content-addressed. This eliminates the classic "works on my machine" problem — the same expression yields the same binary, on any machine, at any time. \ No newline at end of file