From fc8a4e57555ac0064a85c0598ccf20a5bda38e9d Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Thu, 12 Feb 2026 00:23:06 -0600 Subject: test github actions for docker build and site deploy (#1) * test github actions for docker build and site deploy * fix typo in codeQL * test actions on this branch * fix typo in actions path * update pkg tag * update command for sh syntax * attempt to fix build-job path * only run docker-build when relevant files are modified * update pkg tag * debug failing upload * try again to fix artifact build path * try again to fix artifact build path * upgrade codeql to v4 * optimize deployment to reduce building site twice * tweak deployment steps * remove deployment method options * try heredoc for ssh config file * try heredoc for ssh config file * move deployment step out of python entirely for github actions * fix rsync options * last tweak to polish off github workflows --- .github/FUNDING.yml | 1 + .github/dependabot.yml | 6 +++ .github/workflows/codeql.yml | 46 ++++++++++++++++ .github/workflows/deploy.yml | 59 ++++++++++++++++++++ .github/workflows/docker-build.yml | 47 ++++++++++++++++ .github/workflows/ruff.yml | 42 +++++++++++++++ .gitlab-ci.yml | 34 ------------ build.py | 107 ++++++++++++++++--------------------- 8 files changed, 247 insertions(+), 95 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/docker-build.yml create mode 100644 .github/workflows/ruff.yml delete mode 100644 .gitlab-ci.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..4b822e5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [ccleberg] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6483b1d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "uv" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..f857dda --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,46 @@ +name: "CodeQL Advanced" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + security-events: write + packages: read + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..85eeff7 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,59 @@ +name: Build and Deploy + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build-job: + runs-on: ubuntu-latest + container: + image: ghcr.io/ccleberg/cleberg.net:github-actions + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run Build + run: | + echo "Environment is ready. Running build..." + echo "r" | ENV=prod uv run build.py + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: build-output + path: ${{ github.workspace }}/.build/ + include-hidden-files: true + + deploy-job: + runs-on: ubuntu-latest + needs: build-job + environment: production + container: + image: ghcr.io/ccleberg/cleberg.net:github-actions + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: build-output + path: ${{ github.workspace }}/.build/ + + - name: Setup SSH and Deploy + env: + SERVER_IP: ${{ secrets.SERVER_IP }} + SERVER_USER: ${{ secrets.SERVER_USER }} + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + run: | + eval $(ssh-agent -s) + echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + rsync -avz --delete \ + -e "ssh -p 2169 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \ + .build/ \ + $SERVER_USER@$SERVER_IP:/var/www/cleberg.net/ \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..6172cc4 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,47 @@ +name: Build and Push Docker Image + +on: + push: + branches: [ "main" ] + paths: + - 'Dockerfile' + - 'requirements.txt' + - '.github/workflows/docker-build.yml' + pull_request: + branches: [ "main" ] + paths: + - 'Dockerfile' + - 'requirements.txt' + - '.github/workflows/docker-build.yml' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..1c0fbea --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,42 @@ +name: Ruff + +on: + push: + branches: [ "main" ] + paths: + - '**.py' + pull_request: + branches: [ "main" ] + paths: + - '**.py' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.x"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + ref: ${{ github.event.pull_request.head.ref }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pandas dash plotly.express + - name: Install Ruff + uses: astral-sh/ruff-action@v3.2.2 + - name: Ruff Actions + run: | + ruff check --fix + ruff format + - name: Add and Commit + uses: EndBug/add-and-commit@v9 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + default_author: github_actions + pathspec_error_handling: ignore diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index e6a1137..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,34 +0,0 @@ -image: registry.gitlab.com/ccleberg/cleberg-net:latest - -stages: - - build - - deploy - -build-job: - stage: build - script: - - echo "Environment is ready. Running build..." - - ENV=prod SKIP_DEPLOY=true uv run build.py <<< "r" - artifacts: - paths: - - .build/ - -deploy-job: - stage: deploy - environment: production - dependencies: - - build-job - script: - - mkdir -p $HOME/.ssh - - | - echo "Host homelab-remote - HostName $SERVER_IP - User $SERVER_USER - Port 2169 - StrictHostKeyChecking no - UserKnownHostsFile /dev/null" > $HOME/.ssh/config - - chmod 600 $HOME/.ssh/config - - eval $(ssh-agent -s) - - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - - echo "Deploying via Python script..." - - ENV=prod uv run build.py <<< "r" diff --git a/build.py b/build.py index 7b19123..44b77d0 100644 --- a/build.py +++ b/build.py @@ -86,9 +86,7 @@ def update_index_html(html_snippet, template_path="./.build/index.html"): end_line_start = end_index # Construct the new content - new_content = ( - content[:insert_start] + indented_snippet + content[end_line_start:] - ) + new_content = content[:insert_start] + indented_snippet + content[end_line_start:] # Write back to index.html 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): lines.append( f'\t\t' ) - lines.append( - f'\t\t\t{post["title"]}' - ) + lines.append(f'\t\t\t{post["title"]}') lines.append("\t") return "\n".join(lines) @@ -267,7 +263,9 @@ def run_emacs_publish(dev_mode=True): if annoying_file.exists(): os.remove(annoying_file) else: - print("Warning: .build/cleberg-net.html not found, but Emacs exited successfully.") + print( + "Warning: .build/cleberg-net.html not found, but Emacs exited successfully." + ) 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"): loc = f"{base_url}{url_path}" # Last modified time - lastmod = datetime.fromtimestamp( - os.path.getmtime(full_path) - ).strftime("%Y-%m-%d") + lastmod = datetime.fromtimestamp(os.path.getmtime(full_path)).strftime( + "%Y-%m-%d" + ) sitemap_entries.append(f""" {loc} @@ -308,9 +306,7 @@ def generate_sitemap(build_dir=".build", base_url="https://cleberg.net"): sitemap_path = os.path.join(build_dir, "sitemap.xml") with open(sitemap_path, "w", encoding="utf-8") as f: f.write(sitemap_xml) - print( - f"Sitemap generated at {sitemap_path} with {len(sitemap_entries)} entries." - ) + print(f"Sitemap generated at {sitemap_path} with {len(sitemap_entries)} entries.") def deploy_to_server(build_dir, server): @@ -333,9 +329,7 @@ def start_dev_server(build_dir): os.chdir(build_dir) # This will run until interrupted (Ctrl+C) try: - subprocess.run( - [sys.executable, "-m", "http.server", "8000"], check=True - ) + subprocess.run([sys.executable, "-m", "http.server", "8000"], check=True) except KeyboardInterrupt: print("\nDevelopment server stopped.") except subprocess.CalledProcessError as e: @@ -352,66 +346,57 @@ def main(): css_min = theme_dir / "styles.min.css" env = os.environ.get("ENV", "").casefold() - skip_deploy = os.environ.get("SKIP_DEPLOY", "").casefold() == "true" - - if env == "prod": - print("Environment: Production") - method = prompt("Publishing on remote or LAN? [r|l] ").lower() - if method == "r": - homelab_server = "homelab-remote" - elif method == "l": - homelab_server = "homelab" - else: - print("Invalid input. Assuming LAN (homelab)") - homelab_server = "homelab" + deploy = os.environ.get("DEPLOY", "").casefold() == "true" - # Remove previous build - remove_build_directory(build_dir) + if deploy: + print("Deploying to production...") + deploy_to_server(build_dir, "homelab-remote") + return + else: + if env == "prod": + print("Environment: Production") - # Minify CSS - minify_css(css_src, css_min) + # Remove previous build + remove_build_directory(build_dir) - # Run publishing script (silenced output) - run_emacs_publish(dev_mode=False) + # Minify CSS + minify_css(css_src, css_min) - # Update index page with latest posts - update_index_html(html_snippet) + # Run publishing script (silenced output) + run_emacs_publish(dev_mode=False) - # Minify index page - minify_html("./.build/index.html", "./.build/index.html") + # Update index page with latest posts + update_index_html(html_snippet) - # Generate sitemap - generate_sitemap() + # Minify index page + minify_html("./.build/index.html", "./.build/index.html") - # Deploy changes - if not skip_deploy: - deploy_to_server(build_dir, homelab_server) - else: - print("SKIP_DEPLOY is true. Skipping rsync step.") + # Generate sitemap + generate_sitemap() - else: - print("Environment: Development") + else: + print("Environment: Development") - # Remove previous build - remove_build_directory(build_dir) + # Remove previous build + remove_build_directory(build_dir) - # Minify CSS - minify_css(css_src, css_min) + # Minify CSS + minify_css(css_src, css_min) - # Run publishing script (with console output) - run_emacs_publish(dev_mode=True) + # Run publishing script (with console output) + run_emacs_publish(dev_mode=True) - # Update index page with latest posts - update_index_html(html_snippet) + # Update index page with latest posts + update_index_html(html_snippet) - # Minify index page - minify_html("./.build/index.html", "./.build/index.html") + # Minify index page + minify_html("./.build/index.html", "./.build/index.html") - # Generate sitemap - generate_sitemap() + # Generate sitemap + generate_sitemap() - # Launch development web server - start_dev_server(build_dir) + # Launch development web server + start_dev_server(build_dir) if __name__ == "__main__": -- cgit v1.2.3