docs: add developer-workflow.org and language examples
Complete workflow guide for Perl, Python, Go, Ansible, Terraform, Podman. Includes working example files in examples/ directory.
This commit is contained in:
59
examples/ansible/playbook.yml
Normal file
59
examples/ansible/playbook.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
# playbook.yml — Demonstrates core Ansible features for Doom Emacs workflow.
|
||||
#
|
||||
# Navigate: SPC s i (imenu for tasks), SPC s p (grep in project)
|
||||
# Run: SPC m r (ansible-playbook), Lint: flycheck (ansible-lint)
|
||||
|
||||
- name: Configure web server
|
||||
hosts: webservers
|
||||
become: true
|
||||
|
||||
vars:
|
||||
app_user: webapp
|
||||
app_port: 8080
|
||||
packages:
|
||||
- nginx
|
||||
- python3
|
||||
- python3-pip
|
||||
|
||||
handlers:
|
||||
- name: Restart nginx
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: restarted
|
||||
|
||||
tasks:
|
||||
- name: Install required packages
|
||||
ansible.builtin.apt:
|
||||
name: "{{ packages }}"
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Create application user
|
||||
ansible.builtin.user:
|
||||
name: "{{ app_user }}"
|
||||
shell: /bin/bash
|
||||
create_home: true
|
||||
state: present
|
||||
|
||||
- name: Deploy nginx config from template
|
||||
ansible.builtin.template:
|
||||
src: templates/nginx.conf.j2
|
||||
dest: /etc/nginx/sites-available/app.conf
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Enable site symlink
|
||||
ansible.builtin.file:
|
||||
src: /etc/nginx/sites-available/app.conf
|
||||
dest: /etc/nginx/sites-enabled/app.conf
|
||||
state: link
|
||||
notify: Restart nginx
|
||||
|
||||
- name: Ensure nginx is running
|
||||
ansible.builtin.service:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: true
|
||||
44
examples/docker/Dockerfile
Normal file
44
examples/docker/Dockerfile
Normal file
@@ -0,0 +1,44 @@
|
||||
# Dockerfile — Multi-stage build for a Python application.
|
||||
#
|
||||
# Navigate: SPC s i (imenu), Lint: flycheck (hadolint)
|
||||
# Build: SPC m b (podman build), Run: SPC m r (podman run)
|
||||
#
|
||||
# Build: podman build -t myapp:latest -f Dockerfile .
|
||||
# Run: podman run --rm -p 8080:8080 myapp:latest
|
||||
|
||||
# --- Stage 1: Build dependencies ---
|
||||
FROM python:3.12-slim AS builder
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Install dependencies first (layer caching)
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
|
||||
|
||||
# --- Stage 2: Runtime image ---
|
||||
FROM python:3.12-slim AS runtime
|
||||
|
||||
# Security: non-root user
|
||||
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy installed packages from builder
|
||||
COPY --from=builder /install /usr/local
|
||||
|
||||
# Copy application code
|
||||
COPY app/ ./app/
|
||||
COPY main.py .
|
||||
|
||||
# Set ownership
|
||||
RUN chown -R appuser:appuser /app
|
||||
|
||||
USER appuser
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
# Healthcheck: verify the app responds
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8080/health')" || exit 1
|
||||
|
||||
ENTRYPOINT ["python", "main.py"]
|
||||
105
examples/go/main.go
Normal file
105
examples/go/main.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// main.go — Demonstrates core Go features for Doom Emacs workflow.
|
||||
//
|
||||
// Navigate: C-M-a/e (function boundaries), SPC s i (imenu), gd (definition)
|
||||
// Format: SPC m f (goimports), Run: SPC m r, Test: SPC m t, Build: SPC m b
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Task represents a work item with priority.
|
||||
type Task struct {
|
||||
Title string
|
||||
Priority int
|
||||
Done bool
|
||||
}
|
||||
|
||||
// Summary returns a human-readable one-line description.
|
||||
func (t *Task) Summary() string {
|
||||
status := "TODO"
|
||||
if t.Done {
|
||||
status = "DONE"
|
||||
}
|
||||
return fmt.Sprintf("[%s] %s (p%d)", status, t.Title, t.Priority)
|
||||
}
|
||||
|
||||
// MarkDone marks the task as completed.
|
||||
func (t *Task) MarkDone() {
|
||||
t.Done = true
|
||||
}
|
||||
|
||||
// filterByPriority returns undone tasks with priority >= minPriority.
|
||||
func filterByPriority(tasks []*Task, minPriority int) []*Task {
|
||||
var result []*Task
|
||||
for _, t := range tasks {
|
||||
if !t.Done && t.Priority >= minPriority {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// countWords demonstrates a goroutine + channel pattern.
|
||||
// It counts words in each input string concurrently.
|
||||
func countWords(inputs []string) map[string]int {
|
||||
type result struct {
|
||||
input string
|
||||
count int
|
||||
}
|
||||
|
||||
ch := make(chan result, len(inputs))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, s := range inputs {
|
||||
wg.Add(1)
|
||||
go func(text string) {
|
||||
defer wg.Done()
|
||||
// Simulate work
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
ch <- result{input: text, count: len(strings.Fields(text))}
|
||||
}(s)
|
||||
}
|
||||
|
||||
// Close channel after all goroutines finish
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
counts := make(map[string]int)
|
||||
for r := range ch {
|
||||
counts[r.input] = r.count
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
func main() {
|
||||
tasks := []*Task{
|
||||
{Title: "Write tests", Priority: 2},
|
||||
{Title: "Update docs", Priority: 1},
|
||||
{Title: "Deploy v2", Priority: 2},
|
||||
}
|
||||
|
||||
tasks[0].MarkDone()
|
||||
|
||||
fmt.Println("Open tasks (priority >= 2):")
|
||||
for _, t := range filterByPriority(tasks, 2) {
|
||||
fmt.Println(" ", t.Summary())
|
||||
}
|
||||
|
||||
// Goroutine demo
|
||||
sentences := []string{
|
||||
"Go is a compiled language",
|
||||
"Goroutines are lightweight threads",
|
||||
"Channels enable safe communication",
|
||||
}
|
||||
counts := countWords(sentences)
|
||||
fmt.Println("\nWord counts:")
|
||||
for s, c := range counts {
|
||||
fmt.Printf(" %q → %d words\n", s, c)
|
||||
}
|
||||
}
|
||||
65
examples/perl/example.pl
Normal file
65
examples/perl/example.pl
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env perl
|
||||
# example.pl — Demonstrates core Perl features for Doom Emacs workflow.
|
||||
# Navigate: C-M-a/e (sub boundaries), SPC s i (imenu), gd (definition)
|
||||
# Format: SPC m f (perltidy), Run: SPC m r, Debug: SPC m d
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Carp qw(croak);
|
||||
|
||||
# --- Subroutine with documentation ---
|
||||
|
||||
=head2 parse_config($filename)
|
||||
|
||||
Reads a simple key=value config file. Returns a hashref.
|
||||
|
||||
=cut
|
||||
|
||||
sub parse_config {
|
||||
my ($filename) = @_;
|
||||
croak "Filename required" unless defined $filename;
|
||||
|
||||
my %config;
|
||||
open my $fh, '<', $filename
|
||||
or die "Cannot open '$filename': $!\n";
|
||||
|
||||
while (my $line = <$fh>) {
|
||||
chomp $line;
|
||||
next if $line =~ /^\s*(?:#|$)/; # skip comments and blanks
|
||||
if ($line =~ /^(\w+)\s*=\s*(.*)$/) {
|
||||
$config{$1} = $2;
|
||||
}
|
||||
}
|
||||
close $fh;
|
||||
return \%config;
|
||||
}
|
||||
|
||||
# --- Array, hash, references ---
|
||||
|
||||
my @languages = qw(Perl Python Go);
|
||||
my %scores = (Perl => 95, Python => 90, Go => 88);
|
||||
|
||||
my $lang_ref = \@languages;
|
||||
my $score_ref = \%scores;
|
||||
|
||||
printf "Languages: %s\n", join(', ', @{$lang_ref});
|
||||
printf "Perl score: %d\n", $score_ref->{Perl};
|
||||
|
||||
# --- Regex demo ---
|
||||
|
||||
my $text = 'Error: connection refused at 2024-01-15 10:30:00';
|
||||
if ($text =~ /^(Error|Warning):\s+(.+?)\s+at\s+(\d{4}-\d{2}-\d{2})/) {
|
||||
printf "Level: %s, Message: %s, Date: %s\n", $1, $2, $3;
|
||||
}
|
||||
|
||||
# --- Error handling with eval/die ---
|
||||
|
||||
eval {
|
||||
my $cfg = parse_config('/tmp/nonexistent.cfg');
|
||||
print "Loaded config\n";
|
||||
};
|
||||
if ($@) {
|
||||
warn "Caught error: $@";
|
||||
}
|
||||
|
||||
print "Done.\n";
|
||||
70
examples/python/example.py
Normal file
70
examples/python/example.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""example.py — Demonstrates core Python features for Doom Emacs workflow.
|
||||
|
||||
Navigate: C-M-a/e (function boundaries), SPC s i (imenu), gd (definition)
|
||||
Format: SPC m f (ruff format), Run: SPC m r
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
|
||||
@dataclass
|
||||
class Task:
|
||||
"""A simple task with priority and tags."""
|
||||
|
||||
title: str
|
||||
priority: int = 0
|
||||
tags: list[str] = field(default_factory=list)
|
||||
done: bool = False
|
||||
|
||||
def mark_done(self) -> None:
|
||||
self.done = True
|
||||
|
||||
def summary(self) -> str:
|
||||
status = "DONE" if self.done else "TODO"
|
||||
tag_str = ", ".join(self.tags) if self.tags else "none"
|
||||
return f"[{status}] {self.title} (p{self.priority}, tags: {tag_str})"
|
||||
|
||||
|
||||
def filter_tasks(tasks: list[Task], *, min_priority: int = 0) -> list[Task]:
|
||||
"""Return undone tasks with priority >= min_priority."""
|
||||
return [t for t in tasks if not t.done and t.priority >= min_priority]
|
||||
|
||||
|
||||
def load_tasks_from_file(path: str) -> list[Task]:
|
||||
"""Load tasks from a simple text file (one title per line)."""
|
||||
tasks: list[Task] = []
|
||||
try:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
for i, line in enumerate(fh):
|
||||
title = line.strip()
|
||||
if title:
|
||||
tasks.append(Task(title=title, priority=i % 3))
|
||||
except FileNotFoundError:
|
||||
print(f"File not found: {path}, using demo tasks")
|
||||
tasks = [
|
||||
Task("Write tests", priority=2, tags=["dev"]),
|
||||
Task("Update docs", priority=1, tags=["docs"]),
|
||||
Task("Deploy v2", priority=2, tags=["ops", "dev"]),
|
||||
]
|
||||
return tasks
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Task manager demo")
|
||||
parser.add_argument("-f", "--file", default="tasks.txt", help="Task file")
|
||||
parser.add_argument("-p", "--priority", type=int, default=1, help="Min priority")
|
||||
args = parser.parse_args()
|
||||
|
||||
tasks = load_tasks_from_file(args.file)
|
||||
tasks[0].mark_done()
|
||||
|
||||
important = filter_tasks(tasks, min_priority=args.priority)
|
||||
print(f"Open tasks (priority >= {args.priority}):")
|
||||
for task in important:
|
||||
print(f" {task.summary()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
39
examples/terraform/main.tf
Normal file
39
examples/terraform/main.tf
Normal file
@@ -0,0 +1,39 @@
|
||||
# main.tf — Demonstrates core Terraform features for Doom Emacs workflow.
|
||||
#
|
||||
# Navigate: SPC s i (imenu), SPC s p (grep in project)
|
||||
# Format: SPC m f (terraform fmt), Init: SPC m i, Plan: SPC m p
|
||||
#
|
||||
# Uses null_resource for a provider-free demo. Replace with real providers
|
||||
# (aws, azurerm, google) for actual infrastructure.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5"
|
||||
required_providers {
|
||||
null = {
|
||||
source = "hashicorp/null"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# A null resource that runs a local command — useful for demos and provisioning
|
||||
resource "null_resource" "hello" {
|
||||
triggers = {
|
||||
message = var.greeting_message
|
||||
}
|
||||
|
||||
provisioner "local-exec" {
|
||||
command = "echo '${var.greeting_message}' > /tmp/terraform-hello.txt"
|
||||
}
|
||||
}
|
||||
|
||||
# Output the greeting message for verification
|
||||
output "greeting" {
|
||||
description = "The greeting message written to file"
|
||||
value = var.greeting_message
|
||||
}
|
||||
|
||||
output "environment" {
|
||||
description = "Current deployment environment"
|
||||
value = var.environment
|
||||
}
|
||||
18
examples/terraform/variables.tf
Normal file
18
examples/terraform/variables.tf
Normal file
@@ -0,0 +1,18 @@
|
||||
# variables.tf — Input variables for the Terraform demo.
|
||||
|
||||
variable "greeting_message" {
|
||||
description = "Message to write to the output file"
|
||||
type = string
|
||||
default = "Hello from Terraform!"
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Deployment environment name"
|
||||
type = string
|
||||
default = "dev"
|
||||
|
||||
validation {
|
||||
condition = contains(["dev", "staging", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, staging, or prod."
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user