Commit fc8a4e5755
Unregistered key
Layout: unified · split
.github/FUNDING.yml added +1
| @@ -0,0 +1 @@ | ||
| 1 | github: [ccleberg] | |
.github/dependabot.yml added +6
| @@ -0,0 +1,6 @@ | ||
| 1 | version: 2 | |
| 2 | updates: | |
| 3 | - package-ecosystem: "uv" | |
| 4 | directory: "/" | |
| 5 | schedule: | |
| 6 | interval: "weekly" | |
.github/workflows/codeql.yml added +46
| @@ -0,0 +1,46 @@ | ||
| 1 | name: "CodeQL Advanced" | |
| 2 | ||
| 3 | on: | |
| 4 | push: | |
| 5 | branches: [ "main" ] | |
| 6 | pull_request: | |
| 7 | branches: [ "main" ] | |
| 8 | ||
| 9 | jobs: | |
| 10 | analyze: | |
| 11 | name: Analyze (${{ matrix.language }}) | |
| 12 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} | |
| 13 | permissions: | |
| 14 | security-events: write | |
| 15 | packages: read | |
| 16 | actions: read | |
| 17 | contents: read | |
| 18 | ||
| 19 | strategy: | |
| 20 | fail-fast: false | |
| 21 | matrix: | |
| 22 | include: | |
| 23 | - language: python | |
| 24 | build-mode: none | |
| 25 | steps: | |
| 26 | - name: Checkout repository | |
| 27 | uses: actions/checkout@v4 | |
| 28 | - name: Initialize CodeQL | |
| 29 | uses: github/codeql-action/init@v4 | |
| 30 | with: | |
| 31 | languages: ${{ matrix.language }} | |
| 32 | build-mode: ${{ matrix.build-mode }} | |
| 33 | - if: matrix.build-mode == 'manual' | |
| 34 | shell: bash | |
| 35 | run: | | |
| 36 | echo 'If you are using a "manual" build mode for one or more of the' \ | |
| 37 | 'languages you are analyzing, replace this with the commands to build' \ | |
| 38 | 'your code, for example:' | |
| 39 | echo ' make bootstrap' | |
| 40 | echo ' make release' | |
| 41 | exit 1 | |
| 42 | ||
| 43 | - name: Perform CodeQL Analysis | |
| 44 | uses: github/codeql-action/analyze@v4 | |
| 45 | with: | |
| 46 | category: "/language:${{matrix.language}}" | |
.github/workflows/deploy.yml added +59
| @@ -0,0 +1,59 @@ | ||
| 1 | name: Build and Deploy | |
| 2 | ||
| 3 | on: | |
| 4 | push: | |
| 5 | branches: [ "main" ] | |
| 6 | pull_request: | |
| 7 | branches: [ "main" ] | |
| 8 | ||
| 9 | jobs: | |
| 10 | build-job: | |
| 11 | runs-on: ubuntu-latest | |
| 12 | container: | |
| 13 | image: ghcr.io/ccleberg/cleberg.net:github-actions | |
| 14 | ||
| 15 | steps: | |
| 16 | - name: Checkout code | |
| 17 | uses: actions/checkout@v4 | |
| 18 | ||
| 19 | - name: Run Build | |
| 20 | run: | | |
| 21 | echo "Environment is ready. Running build..." | |
| 22 | echo "r" | ENV=prod uv run build.py | |
| 23 | ||
| 24 | - name: Upload Build Artifacts | |
| 25 | uses: actions/upload-artifact@v4 | |
| 26 | with: | |
| 27 | name: build-output | |
| 28 | path: ${{ github.workspace }}/.build/ | |
| 29 | include-hidden-files: true | |
| 30 | ||
| 31 | deploy-job: | |
| 32 | runs-on: ubuntu-latest | |
| 33 | needs: build-job | |
| 34 | environment: production | |
| 35 | container: | |
| 36 | image: ghcr.io/ccleberg/cleberg.net:github-actions | |
| 37 | ||
| 38 | steps: | |
| 39 | - name: Checkout code | |
| 40 | uses: actions/checkout@v4 | |
| 41 | ||
| 42 | - name: Download Build Artifacts | |
| 43 | uses: actions/download-artifact@v4 | |
| 44 | with: | |
| 45 | name: build-output | |
| 46 | path: ${{ github.workspace }}/.build/ | |
| 47 | ||
| 48 | - name: Setup SSH and Deploy | |
| 49 | env: | |
| 50 | SERVER_IP: ${{ secrets.SERVER_IP }} | |
| 51 | SERVER_USER: ${{ secrets.SERVER_USER }} | |
| 52 | SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
| 53 | run: | | |
| 54 | eval $(ssh-agent -s) | |
| 55 | echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - | |
| 56 | rsync -avz --delete \ | |
| 57 | -e "ssh -p 2169 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \ | |
| 58 | .build/ \ | |
| 59 | $SERVER_USER@$SERVER_IP:/var/www/cleberg.net/ | |
| \ No newline at end of file | ||
.github/workflows/docker-build.yml added +47
| @@ -0,0 +1,47 @@ | ||
| 1 | name: Build and Push Docker Image | |
| 2 | ||
| 3 | on: | |
| 4 | push: | |
| 5 | branches: [ "main" ] | |
| 6 | paths: | |
| 7 | - 'Dockerfile' | |
| 8 | - 'requirements.txt' | |
| 9 | - '.github/workflows/docker-build.yml' | |
| 10 | pull_request: | |
| 11 | branches: [ "main" ] | |
| 12 | paths: | |
| 13 | - 'Dockerfile' | |
| 14 | - 'requirements.txt' | |
| 15 | - '.github/workflows/docker-build.yml' | |
| 16 | ||
| 17 | jobs: | |
| 18 | build: | |
| 19 | runs-on: ubuntu-latest | |
| 20 | permissions: | |
| 21 | contents: read | |
| 22 | packages: write | |
| 23 | ||
| 24 | steps: | |
| 25 | - name: Checkout repository | |
| 26 | uses: actions/checkout@v4 | |
| 27 | ||
| 28 | - name: Log in to GHCR | |
| 29 | uses: docker/login-action@v3 | |
| 30 | with: | |
| 31 | registry: ghcr.io | |
| 32 | username: ${{ github.actor }} | |
| 33 | password: ${{ secrets.GITHUB_TOKEN }} | |
| 34 | ||
| 35 | - name: Extract metadata | |
| 36 | id: meta | |
| 37 | uses: docker/metadata-action@v5 | |
| 38 | with: | |
| 39 | images: ghcr.io/${{ github.repository }} | |
| 40 | ||
| 41 | - name: Build and push | |
| 42 | uses: docker/build-push-action@v5 | |
| 43 | with: | |
| 44 | context: . | |
| 45 | push: true | |
| 46 | tags: ${{ steps.meta.outputs.tags }} | |
| 47 | labels: ${{ steps.meta.outputs.labels }} | |
.github/workflows/ruff.yml added +42
| @@ -0,0 +1,42 @@ | ||
| 1 | name: Ruff | |
| 2 | ||
| 3 | on: | |
| 4 | push: | |
| 5 | branches: [ "main" ] | |
| 6 | paths: | |
| 7 | - '**.py' | |
| 8 | pull_request: | |
| 9 | branches: [ "main" ] | |
| 10 | paths: | |
| 11 | - '**.py' | |
| 12 | ||
| 13 | jobs: | |
| 14 | build: | |
| 15 | runs-on: ubuntu-latest | |
| 16 | strategy: | |
| 17 | matrix: | |
| 18 | python-version: ["3.x"] | |
| 19 | steps: | |
| 20 | - uses: actions/checkout@v4 | |
| 21 | - name: Set up Python ${{ matrix.python-version }} | |
| 22 | uses: actions/setup-python@v5 | |
| 23 | with: | |
| 24 | python-version: ${{ matrix.python-version }} | |
| 25 | ref: ${{ github.event.pull_request.head.ref }} | |
| 26 | - name: Install dependencies | |
| 27 | run: | | |
| 28 | python -m pip install --upgrade pip | |
| 29 | pip install pandas dash plotly.express | |
| 30 | - name: Install Ruff | |
| 31 | uses: astral-sh/ruff-action@v3.2.2 | |
| 32 | - name: Ruff Actions | |
| 33 | run: | | |
| 34 | ruff check --fix | |
| 35 | ruff format | |
| 36 | - name: Add and Commit | |
| 37 | uses: EndBug/add-and-commit@v9 | |
| 38 | env: | |
| 39 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| 40 | with: | |
| 41 | default_author: github_actions | |
| 42 | pathspec_error_handling: ignore | |
.gitlab-ci.yml deleted −34
| @@ -1,34 +0,0 @@ | ||
| 1 | image: registry.gitlab.com/ccleberg/cleberg-net:latest | |
| 2 | ||
| 3 | stages: | |
| 4 | - build | |
| 5 | - deploy | |
| 6 | ||
| 7 | build-job: | |
| 8 | stage: build | |
| 9 | script: | |
| 10 | - echo "Environment is ready. Running build..." | |
| 11 | - ENV=prod SKIP_DEPLOY=true uv run build.py <<< "r" | |
| 12 | artifacts: | |
| 13 | paths: | |
| 14 | - .build/ | |
| 15 | ||
| 16 | deploy-job: | |
| 17 | stage: deploy | |
| 18 | environment: production | |
| 19 | dependencies: | |
| 20 | - build-job | |
| 21 | script: | |
| 22 | - mkdir -p $HOME/.ssh | |
| 23 | - | | |
| 24 | echo "Host homelab-remote | |
| 25 | HostName $SERVER_IP | |
| 26 | User $SERVER_USER | |
| 27 | Port 2169 | |
| 28 | StrictHostKeyChecking no | |
| 29 | UserKnownHostsFile /dev/null" > $HOME/.ssh/config | |
| 30 | - chmod 600 $HOME/.ssh/config | |
| 31 | - eval $(ssh-agent -s) | |
| 32 | - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - | |
| 33 | - echo "Deploying via Python script..." | |
| 34 | - ENV=prod uv run build.py <<< "r" | |
build.py +46 −61
| @@ -86,9 +86,7 @@ def update_index_html(html_snippet, template_path="./.build/index.html"): | ||
| 86 | 86 | end_line_start = end_index |
| 87 | 87 | |
| 88 | 88 | # Construct the new content |
| 89 | new_content = ( | |
| 90 | content[:insert_start] + indented_snippet + content[end_line_start:] | |
| 91 | ) | |
| 89 | new_content = content[:insert_start] + indented_snippet + content[end_line_start:] | |
| 92 | 90 | |
| 93 | 91 | # Write back to index.html |
| 94 | 92 | with open(template_path, "w", encoding="utf-8") as f: |
| @@ -190,9 +188,7 @@ def get_recent_posts_html(content_dir="./content/blog", num_posts=3): | ||
| 190 | 188 | lines.append( |
| 191 | 189 | f'\t\t<time datetime="{post["date_str"]}">{post["date_full"]}</time>' |
| 192 | 190 | ) |
| 193 | lines.append( | |
| 194 | f'\t\t\t<a href="/blog/{post["slug"]}.html">{post["title"]}</a>' | |
| 195 | ) | |
| 191 | lines.append(f'\t\t\t<a href="/blog/{post["slug"]}.html">{post["title"]}</a>') | |
| 196 | 192 | lines.append("\t</div>") |
| 197 | 193 | |
| 198 | 194 | return "\n".join(lines) |
| @@ -267,7 +263,9 @@ def run_emacs_publish(dev_mode=True): | ||
| 267 | 263 | if annoying_file.exists(): |
| 268 | 264 | os.remove(annoying_file) |
| 269 | 265 | else: |
| 270 | print("Warning: .build/cleberg-net.html not found, but Emacs exited successfully.") | |
| 266 | print( | |
| 267 | "Warning: .build/cleberg-net.html not found, but Emacs exited successfully." | |
| 268 | ) | |
| 271 | 269 | |
| 272 | 270 | |
| 273 | 271 | def generate_sitemap(build_dir=".build", base_url="https://cleberg.net"): |
| @@ -290,9 +288,9 @@ def generate_sitemap(build_dir=".build", base_url="https://cleberg.net"): | ||
| 290 | 288 | loc = f"{base_url}{url_path}" |
| 291 | 289 | |
| 292 | 290 | # Last modified time |
| 293 | lastmod = datetime.fromtimestamp( | |
| 294 | os.path.getmtime(full_path) | |
| 295 | ).strftime("%Y-%m-%d") | |
| 291 | lastmod = datetime.fromtimestamp(os.path.getmtime(full_path)).strftime( | |
| 292 | "%Y-%m-%d" | |
| 293 | ) | |
| 296 | 294 | |
| 297 | 295 | sitemap_entries.append(f""" <url> |
| 298 | 296 | <loc>{loc}</loc> |
| @@ -308,9 +306,7 @@ def generate_sitemap(build_dir=".build", base_url="https://cleberg.net"): | ||
| 308 | 306 | sitemap_path = os.path.join(build_dir, "sitemap.xml") |
| 309 | 307 | with open(sitemap_path, "w", encoding="utf-8") as f: |
| 310 | 308 | f.write(sitemap_xml) |
| 311 | print( | |
| 312 | f"Sitemap generated at {sitemap_path} with {len(sitemap_entries)} entries." | |
| 313 | ) | |
| 309 | print(f"Sitemap generated at {sitemap_path} with {len(sitemap_entries)} entries.") | |
| 314 | 310 | |
| 315 | 311 | |
| 316 | 312 | def deploy_to_server(build_dir, server): |
| @@ -333,9 +329,7 @@ def start_dev_server(build_dir): | ||
| 333 | 329 | os.chdir(build_dir) |
| 334 | 330 | # This will run until interrupted (Ctrl+C) |
| 335 | 331 | try: |
| 336 | subprocess.run( | |
| 337 | [sys.executable, "-m", "http.server", "8000"], check=True | |
| 338 | ) | |
| 332 | subprocess.run([sys.executable, "-m", "http.server", "8000"], check=True) | |
| 339 | 333 | except KeyboardInterrupt: |
| 340 | 334 | print("\nDevelopment server stopped.") |
| 341 | 335 | except subprocess.CalledProcessError as e: |
| @@ -352,66 +346,57 @@ def main(): | ||
| 352 | 346 | css_min = theme_dir / "styles.min.css" |
| 353 | 347 | |
| 354 | 348 | env = os.environ.get("ENV", "").casefold() |
| 355 | skip_deploy = os.environ.get("SKIP_DEPLOY", "").casefold() == "true" | |
| 356 | ||
| 357 | if env == "prod": | |
| 358 | print("Environment: Production") | |
| 359 | method = prompt("Publishing on remote or LAN? [r|l] ").lower() | |
| 360 | if method == "r": | |
| 361 | homelab_server = "homelab-remote" | |
| 362 | elif method == "l": | |
| 363 | homelab_server = "homelab" | |
| 364 | else: | |
| 365 | print("Invalid input. Assuming LAN (homelab)") | |
| 366 | homelab_server = "homelab" | |
| 349 | deploy = os.environ.get("DEPLOY", "").casefold() == "true" | |
| 367 | 350 | |
| 368 | # Remove previous build | |
| 369 | remove_build_directory(build_dir) | |
| 351 | if deploy: | |
| 352 | print("Deploying to production...") | |
| 353 | deploy_to_server(build_dir, "homelab-remote") | |
| 354 | return | |
| 355 | else: | |
| 356 | if env == "prod": | |
| 357 | print("Environment: Production") | |
| 370 | 358 | |
| 371 | # Minify CSS | |
| 372 | minify_css(css_src, css_min) | |
| 359 | # Remove previous build | |
| 360 | remove_build_directory(build_dir) | |
| 373 | 361 | |
| 374 | # Run publishing script (silenced output) | |
| 375 | run_emacs_publish(dev_mode=False) | |
| 362 | # Minify CSS | |
| 363 | minify_css(css_src, css_min) | |
| 376 | 364 | |
| 377 | # Update index page with latest posts | |
| 378 | update_index_html(html_snippet) | |
| 365 | # Run publishing script (silenced output) | |
| 366 | run_emacs_publish(dev_mode=False) | |
| 379 | 367 | |
| 380 | # Minify index page | |
| 381 | minify_html("./.build/index.html", "./.build/index.html") | |
| 368 | # Update index page with latest posts | |
| 369 | update_index_html(html_snippet) | |
| 382 | 370 | |
| 383 | # Generate sitemap | |
| 384 | generate_sitemap() | |
| 371 | # Minify index page | |
| 372 | minify_html("./.build/index.html", "./.build/index.html") | |
| 385 | 373 | |
| 386 | # Deploy changes | |
| 387 | if not skip_deploy: | |
| 388 | deploy_to_server(build_dir, homelab_server) | |
| 389 | else: | |
| 390 | print("SKIP_DEPLOY is true. Skipping rsync step.") | |
| 374 | # Generate sitemap | |
| 375 | generate_sitemap() | |
| 391 | 376 | |
| 392 | else: | |
| 393 | print("Environment: Development") | |
| 377 | else: | |
| 378 | print("Environment: Development") | |
| 394 | 379 | |
| 395 | # Remove previous build | |
| 396 | remove_build_directory(build_dir) | |
| 380 | # Remove previous build | |
| 381 | remove_build_directory(build_dir) | |
| 397 | 382 | |
| 398 | # Minify CSS | |
| 399 | minify_css(css_src, css_min) | |
| 383 | # Minify CSS | |
| 384 | minify_css(css_src, css_min) | |
| 400 | 385 | |
| 401 | # Run publishing script (with console output) | |
| 402 | run_emacs_publish(dev_mode=True) | |
| 386 | # Run publishing script (with console output) | |
| 387 | run_emacs_publish(dev_mode=True) | |
| 403 | 388 | |
| 404 | # Update index page with latest posts | |
| 405 | update_index_html(html_snippet) | |
| 389 | # Update index page with latest posts | |
| 390 | update_index_html(html_snippet) | |
| 406 | 391 | |
| 407 | # Minify index page | |
| 408 | minify_html("./.build/index.html", "./.build/index.html") | |
| 392 | # Minify index page | |
| 393 | minify_html("./.build/index.html", "./.build/index.html") | |
| 409 | 394 | |
| 410 | # Generate sitemap | |
| 411 | generate_sitemap() | |
| 395 | # Generate sitemap | |
| 396 | generate_sitemap() | |
| 412 | 397 | |
| 413 | # Launch development web server | |
| 414 | start_dev_server(build_dir) | |
| 398 | # Launch development web server | |
| 399 | start_dev_server(build_dir) | |
| 415 | 400 | |
| 416 | 401 | |
| 417 | 402 | if __name__ == "__main__": |