Add project file update helpers
This commit is contained in:
279
07-linuxplus-update-files.el
Normal file
279
07-linuxplus-update-files.el
Normal file
@@ -0,0 +1,279 @@
|
||||
;;; 07-linuxplus-update-files.el --- linuxplus-update-files
|
||||
;;;
|
||||
;;; Provide safe helpers for updating existing project files.
|
||||
;;;
|
||||
;;; This file is part of the Linux+ Learning project.
|
||||
;;;
|
||||
;;; Copyright (c) 2026, Scott C. MacCallum (scm@sdf.org).
|
||||
;;;
|
||||
;;; This program is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU Affero General Public License as
|
||||
;;; published by the Free Software Foundation, either version 3 of the
|
||||
;;; License, or (at your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU Affero General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU Affero General
|
||||
;;; Public License along with this program. If not, see
|
||||
;;; <https://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This file provides small update helpers for existing project files.
|
||||
;;
|
||||
;; The main command is:
|
||||
;;
|
||||
;; M-x linuxplus-apply-firewall-score-update
|
||||
;;
|
||||
;; It updates STUDY.md by marking the firewall review section as Solid
|
||||
;; and by recording the 20/20 firewall quiz score.
|
||||
;;
|
||||
;; The helper functions are intentionally simple. They are meant to
|
||||
;; avoid fragile hand-written patch hunks when making repeated project
|
||||
;; documentation updates.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'subr-x)
|
||||
|
||||
(defvar linuxplus-update-base-directory
|
||||
(file-name-directory
|
||||
(or load-file-name buffer-file-name default-directory))
|
||||
"Base directory for Linux+ project update helpers.")
|
||||
|
||||
(defun linuxplus-update--file (name)
|
||||
"Return the full project path for file NAME."
|
||||
(expand-file-name name linuxplus-update-base-directory))
|
||||
|
||||
(defun linuxplus-update--read-file (file)
|
||||
"Return the contents of FILE as a string."
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(buffer-string)))
|
||||
|
||||
(defun linuxplus-update--write-file (file text)
|
||||
"Write TEXT to FILE."
|
||||
(with-temp-file file
|
||||
(insert text)))
|
||||
|
||||
(defun linuxplus-update--backup-file (file)
|
||||
"Create a numbered backup copy of FILE."
|
||||
(let* ((index 1)
|
||||
(backup (format "%s.bak.%d" file index)))
|
||||
(while (file-exists-p backup)
|
||||
(setq index (1+ index))
|
||||
(setq backup (format "%s.bak.%d" file index)))
|
||||
(copy-file file backup)
|
||||
backup))
|
||||
|
||||
(defun linuxplus-update--section-bounds (text heading)
|
||||
"Return section bounds for HEADING in TEXT.
|
||||
The result is a cons cell whose car is the start position and whose
|
||||
cdr is the end position. The end is the next Markdown level two
|
||||
heading or the end of TEXT."
|
||||
(let ((start (string-match (concat "^" (regexp-quote heading) "$")
|
||||
text)))
|
||||
(unless start
|
||||
(error "Could not find heading: %s" heading))
|
||||
(let* ((search-start (+ start (length heading)))
|
||||
(next (string-match "^## " text search-start)))
|
||||
(cons start (or next (length text))))))
|
||||
|
||||
(defun linuxplus-update-replace-section (file heading new-section)
|
||||
"In FILE, replace Markdown section HEADING with NEW-SECTION."
|
||||
(let* ((text (linuxplus-update--read-file file))
|
||||
(bounds (linuxplus-update--section-bounds text heading))
|
||||
(start (car bounds))
|
||||
(end (cdr bounds))
|
||||
(backup (linuxplus-update--backup-file file))
|
||||
(updated (concat (substring text 0 start)
|
||||
(string-trim-right new-section)
|
||||
"\n\n"
|
||||
(substring text end))))
|
||||
(linuxplus-update--write-file file updated)
|
||||
(message "Updated %s; backup saved as %s" file backup)))
|
||||
|
||||
(defun linuxplus-update-insert-before-heading (file heading text-to-insert)
|
||||
"In FILE, insert TEXT-TO-INSERT before Markdown HEADING."
|
||||
(let* ((text (linuxplus-update--read-file file))
|
||||
(marker (concat "\n" heading))
|
||||
(pos (string-match (regexp-quote marker) text))
|
||||
(backup (linuxplus-update--backup-file file)))
|
||||
(unless pos
|
||||
(error "Could not find heading: %s" heading))
|
||||
(linuxplus-update--write-file
|
||||
file
|
||||
(concat (substring text 0 (1+ pos))
|
||||
(string-trim-right text-to-insert)
|
||||
"\n\n"
|
||||
(substring text (1+ pos))))
|
||||
(message "Updated %s; backup saved as %s" file backup)))
|
||||
|
||||
(defun linuxplus-update-replace-section-with-region
|
||||
(file heading beg end)
|
||||
"Replace Markdown section HEADING in FILE with region BEG to END."
|
||||
(interactive
|
||||
(list (read-file-name "File to update: "
|
||||
linuxplus-update-base-directory)
|
||||
(read-string "Section heading: ")
|
||||
(region-beginning)
|
||||
(region-end)))
|
||||
(linuxplus-update-replace-section
|
||||
file heading (buffer-substring-no-properties beg end)))
|
||||
|
||||
(defun linuxplus-update-insert-region-before-heading
|
||||
(file heading beg end)
|
||||
"Insert region BEG to END before Markdown HEADING in FILE."
|
||||
(interactive
|
||||
(list (read-file-name "File to update: "
|
||||
linuxplus-update-base-directory)
|
||||
(read-string "Insert before heading: ")
|
||||
(region-beginning)
|
||||
(region-end)))
|
||||
(linuxplus-update-insert-before-heading
|
||||
file heading (buffer-substring-no-properties beg end)))
|
||||
|
||||
(defun linuxplus-update--firewall-section ()
|
||||
"Return the corrected Firewall Review STUDY.md section."
|
||||
"## Weak Area: Firewall Review
|
||||
|
||||
Missed area:
|
||||
|
||||
Firewall basics, stateful firewall behavior, firewalld persistence,
|
||||
UFW command syntax, and DenyHosts/Fail2Ban recognition
|
||||
|
||||
Correct concept:
|
||||
|
||||
Firewalls use ACLs, inspect network packets, and use configuration
|
||||
files for persistence.
|
||||
|
||||
ACL means access control list. Firewall ACL rules allow or deny
|
||||
traffic based on details such as source address, destination
|
||||
address, protocol, port, or interface.
|
||||
|
||||
A stateless firewall checks packets against rules without remembering
|
||||
the state of a connection.
|
||||
|
||||
A stateful firewall tracks connection state. For this book review,
|
||||
remember that a stateful firewall can operate faster for established
|
||||
connections and can determine if packets are fragmented.
|
||||
|
||||
firewalld can use runtime configuration and permanent configuration.
|
||||
After testing firewalld runtime rules, make them permanent with:
|
||||
|
||||
sudo firewall-cmd --runtime-to-permanent
|
||||
|
||||
To view numbered UFW rules, use:
|
||||
|
||||
sudo ufw status numbered
|
||||
|
||||
To block SSH using simple UFW syntax, use:
|
||||
|
||||
sudo ufw deny 22/tcp
|
||||
|
||||
For this book item, remember DenyHosts and Fail2Ban as IDS tools.
|
||||
The expected answer associates them with /etc/hosts.deny.
|
||||
|
||||
Exam wording trap:
|
||||
|
||||
\"Make firewalld runtime rules permanent\" points to:
|
||||
|
||||
firewall-cmd --runtime-to-permanent
|
||||
|
||||
\"Numbered UFW rules\" points to:
|
||||
|
||||
sudo ufw status numbered
|
||||
|
||||
\"Deny SSH with simple UFW syntax\" points to:
|
||||
|
||||
sudo ufw deny 22/tcp
|
||||
|
||||
DenyHosts is strongly associated with /etc/hosts.deny.
|
||||
|
||||
Fail2Ban commonly blocks through firewall actions in real-world
|
||||
configurations, but for the book question, memorize the book's
|
||||
expected wording.
|
||||
|
||||
Commands/files to remember:
|
||||
|
||||
sudo firewall-cmd --runtime-to-permanent
|
||||
sudo ufw status numbered
|
||||
sudo ufw deny 22/tcp
|
||||
/etc/hosts.deny
|
||||
|
||||
Future lesson topic:
|
||||
|
||||
104-firewall-review-lesson.el
|
||||
105-firewall-review-quiz.el
|
||||
|
||||
Status:
|
||||
|
||||
Solid")
|
||||
|
||||
(defun linuxplus-update--firewall-score-note ()
|
||||
"Return the firewall score note for STUDY.md."
|
||||
"Date:
|
||||
|
||||
2026-07-09
|
||||
|
||||
Lesson:
|
||||
|
||||
104-firewall-review-lesson.el
|
||||
|
||||
Quiz:
|
||||
|
||||
105-firewall-review-quiz.el
|
||||
|
||||
Score:
|
||||
|
||||
20/20
|
||||
|
||||
Missed:
|
||||
|
||||
None
|
||||
|
||||
Next action:
|
||||
|
||||
Do a quick review later to keep the commands fresh:
|
||||
|
||||
sudo firewall-cmd --runtime-to-permanent
|
||||
sudo ufw status numbered
|
||||
sudo ufw deny 22/tcp
|
||||
/etc/hosts.deny")
|
||||
|
||||
(defun linuxplus-apply-firewall-score-update ()
|
||||
"Update STUDY.md with the firewall review score and Solid status."
|
||||
(interactive)
|
||||
(let ((study-file (linuxplus-update--file "STUDY.md")))
|
||||
(linuxplus-update-replace-section
|
||||
study-file
|
||||
"## Weak Area: Firewall Review"
|
||||
(linuxplus-update--firewall-section))
|
||||
(let ((text (linuxplus-update--read-file study-file)))
|
||||
(unless (string-match-p "Score:\n\n 20/20" text)
|
||||
(linuxplus-update-insert-before-heading
|
||||
study-file
|
||||
"## Current Notes"
|
||||
(linuxplus-update--firewall-score-note)))))
|
||||
(message "Firewall review score update complete."))
|
||||
|
||||
(defun linuxplus-update-help ()
|
||||
"Show help for Linux+ project update helpers."
|
||||
(interactive)
|
||||
(with-help-window "*Linux+ Update Help*"
|
||||
(princ "Linux+ project update helpers\n\n")
|
||||
(princ "Useful commands:\n\n")
|
||||
(princ " M-x linuxplus-apply-firewall-score-update\n")
|
||||
(princ " Update STUDY.md for the 20/20 firewall quiz score.\n\n")
|
||||
(princ " M-x linuxplus-update-replace-section-with-region\n")
|
||||
(princ " Replace a Markdown section with selected text.\n\n")
|
||||
(princ " M-x linuxplus-update-insert-region-before-heading\n")
|
||||
(princ " Insert selected text before a Markdown heading.\n\n")
|
||||
(princ "Each update creates a numbered .bak.N backup first.\n")))
|
||||
|
||||
(provide '07-linuxplus-update-files)
|
||||
|
||||
;;; 07-linuxplus-update-files.el ends here
|
||||
32
RECOVERY.md
32
RECOVERY.md
@@ -313,3 +313,35 @@ Lesson content is based on missed Linux+ firewall book questions:
|
||||
* Fail2Ban
|
||||
|
||||
The lesson preserves the real-world note that DenyHosts is strongly associated with `/etc/hosts.deny`, while Fail2Ban commonly blocks through firewall actions. For the book question, memorize the book's expected wording.
|
||||
|
||||
## RECOVERY maintenance update - 2026-07-09
|
||||
|
||||
Added project-native Emacs Lisp helpers for updating existing files.
|
||||
|
||||
New file:
|
||||
|
||||
* `07-linuxplus-update-files.el`
|
||||
|
||||
Reason:
|
||||
|
||||
* Hand-written patch hunks proved fragile.
|
||||
* Some updates need to modify existing Markdown files safely.
|
||||
* The helper file is numbered so `linuxplus-load-all` loads it with
|
||||
the rest of the project.
|
||||
* Each update creates a numbered `.bak.N` backup before writing.
|
||||
|
||||
Important commands:
|
||||
|
||||
M-x linuxplus-update-help
|
||||
M-x linuxplus-apply-firewall-score-update
|
||||
M-x linuxplus-update-replace-section-with-region
|
||||
M-x linuxplus-update-insert-region-before-heading
|
||||
|
||||
The firewall score updater modifies `STUDY.md` by:
|
||||
|
||||
* replacing the `## Weak Area: Firewall Review` section
|
||||
* setting its status to `Solid`
|
||||
* inserting the 20/20 score note before `## Current Notes`
|
||||
|
||||
Future assistants should prefer these project update helpers over
|
||||
hand-written shell patch hunks when updating existing project files.
|
||||
|
||||
20
STUDY.md
20
STUDY.md
@@ -1553,3 +1553,23 @@ Score:
|
||||
Missed:
|
||||
Next action:
|
||||
```
|
||||
## Project maintenance note: file update helpers
|
||||
|
||||
The project now includes an Emacs Lisp helper file for safe updates to
|
||||
existing project files:
|
||||
|
||||
07-linuxplus-update-files.el
|
||||
|
||||
Use this when updating existing Markdown files such as `STUDY.md` and
|
||||
`RECOVERY.md`.
|
||||
|
||||
Useful commands:
|
||||
|
||||
M-x linuxplus-update-help
|
||||
M-x linuxplus-apply-firewall-score-update
|
||||
M-x linuxplus-update-replace-section-with-region
|
||||
M-x linuxplus-update-insert-region-before-heading
|
||||
|
||||
The update helpers create numbered `.bak.N` backup files before writing
|
||||
changes. This is preferred over hand-written patch hunks for small
|
||||
project documentation updates.
|
||||
|
||||
Reference in New Issue
Block a user