Add tmux to devcontainer for worktree support #189
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR Tests | |
| # This workflow validates PRs for the OpenClaw agent project. | |
| # No Rust compilation — validates scripts, docs, and configuration. | |
| # | |
| # Security model: | |
| # - READ-ONLY permissions (contents: read, pull-requests: read) | |
| # - No secrets exposed to PR builds | |
| # - Fork PRs are blocked from running on self-hosted runners | |
| # - External PRs get tests but NOT Claude reviews (handled by claude-code-review.yml) | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - master | |
| - '**' | |
| workflow_dispatch: | |
| inputs: | |
| ref: | |
| description: 'Git ref to test (branch name or SHA)' | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| env: | |
| DEVCONTAINER_IMAGE: ghcr.io/nickborgersprobably/hide-my-list-devcontainer | |
| jobs: | |
| changes: | |
| name: Detect Changes | |
| runs-on: [self-hosted, homelab] | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | |
| outputs: | |
| scripts: ${{ steps.filter.outputs.scripts }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| docs_files: ${{ steps.filter.outputs.docs_files }} | |
| workflows: ${{ steps.filter.outputs.workflows }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Check for changes | |
| uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| list-files: shell | |
| filters: | | |
| scripts: | |
| - 'scripts/**' | |
| docs: | |
| - 'docs/**' | |
| - 'design/**' | |
| - 'README.md' | |
| - 'AGENTS.md' | |
| workflows: | |
| - '.github/workflows/**' | |
| # Build devcontainer from main branch for security | |
| build-devcontainer: | |
| runs-on: [self-hosted, homelab] | |
| needs: changes | |
| if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| # SECURITY: Checkout main branch, NOT the PR branch | |
| - name: Checkout main branch (security measure) | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| - name: Log in to GHCR | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push devcontainer | |
| uses: devcontainers/ci@v0.3 | |
| continue-on-error: true | |
| with: | |
| imageName: ${{ env.DEVCONTAINER_IMAGE }} | |
| cacheFrom: ${{ env.DEVCONTAINER_IMAGE }} | |
| push: always | |
| script-validation: | |
| name: Script Validation | |
| needs: [changes, build-devcontainer] | |
| if: >- | |
| needs.changes.outputs.scripts == 'true' && | |
| (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| runs-on: [self-hosted, homelab] | |
| steps: | |
| - name: Checkout code | |
| 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: Run script validation in devcontainer | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| imageName: ${{ env.DEVCONTAINER_IMAGE }} | |
| cacheFrom: ${{ env.DEVCONTAINER_IMAGE }} | |
| push: never | |
| runCmd: | | |
| echo "=== Linting shell scripts ===" | |
| find scripts/ -name '*.sh' -exec shellcheck {} + | |
| echo "=== Checking script permissions ===" | |
| find scripts/ -name '*.sh' | while read f; do | |
| if [ ! -x "$f" ]; then | |
| echo "ERROR: $f is not executable" | |
| exit 1 | |
| fi | |
| done | |
| echo "All scripts have execute permission" | |
| doc-validation: | |
| name: Documentation Validation | |
| needs: [changes, build-devcontainer] | |
| if: >- | |
| needs.changes.outputs.docs == 'true' && | |
| (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| runs-on: [self-hosted, homelab] | |
| steps: | |
| - name: Checkout code | |
| 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: Run doc validation in devcontainer | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| imageName: ${{ env.DEVCONTAINER_IMAGE }} | |
| cacheFrom: ${{ env.DEVCONTAINER_IMAGE }} | |
| push: never | |
| env: | | |
| CHANGED_FILES=${{ needs.changes.outputs.docs_files }} | |
| runCmd: | | |
| echo "=== Checking for broken internal links ===" | |
| ERRORS=0 | |
| # Check markdown files for internal links and verify targets exist | |
| find docs/ design/ -name '*.md' | while read f; do | |
| # Extract relative links like [text](./file.md) or [text](../docs/file.md) | |
| grep -oP '\[.*?\]\(\K[^)]+' "$f" 2>/dev/null | grep -v '^http' | while read link; do | |
| # Resolve relative to the file's directory | |
| dir=$(dirname "$f") | |
| target="$dir/$link" | |
| # Strip anchors | |
| target=$(echo "$target" | cut -d'#' -f1) | |
| if [ -n "$target" ] && [ ! -e "$target" ]; then | |
| echo "BROKEN LINK in $f: $link (resolved to $target)" | |
| ERRORS=$((ERRORS + 1)) | |
| fi | |
| done | |
| done | |
| if [ "$ERRORS" -gt 0 ]; then | |
| echo "$ERRORS broken links found" | |
| exit 1 | |
| fi | |
| echo "No broken internal links found" | |
| echo "=== Linting Mermaid diagrams ===" | |
| if [ -n "$CHANGED_FILES" ]; then | |
| # shellcheck disable=SC2086 | |
| ./scripts/lint-mermaid-rendering.sh $CHANGED_FILES | |
| else | |
| echo "No changed doc files to lint" | |
| fi | |
| echo "=== Validating Mermaid diagrams ===" | |
| if [ -n "$CHANGED_FILES" ]; then | |
| # shellcheck disable=SC2086 | |
| ./scripts/validate-mermaid.sh $CHANGED_FILES | |
| else | |
| echo "No changed doc files to validate" | |
| fi | |
| workflow-validation: | |
| name: Workflow YAML Validation | |
| needs: [changes, build-devcontainer] | |
| if: >- | |
| needs.changes.outputs.workflows == 'true' && | |
| (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| runs-on: [self-hosted, homelab] | |
| steps: | |
| - name: Checkout code | |
| 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: Run workflow validation in devcontainer | |
| uses: devcontainers/ci@v0.3 | |
| with: | |
| imageName: ${{ env.DEVCONTAINER_IMAGE }} | |
| cacheFrom: ${{ env.DEVCONTAINER_IMAGE }} | |
| push: never | |
| runCmd: | | |
| yamllint -d "{extends: default, rules: {line-length: disable, truthy: disable, comments-indentation: disable}}" \ | |
| .github/workflows/*.yml | |
| all-tests-passed: | |
| name: All Required Tests | |
| runs-on: [self-hosted, homelab] | |
| needs: [changes, script-validation, doc-validation, workflow-validation] | |
| if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| steps: | |
| - name: Check test results | |
| run: | | |
| check_result() { | |
| local result=$1 | |
| [[ "$result" == "success" || "$result" == "skipped" ]] | |
| } | |
| failed=false | |
| if ! check_result "${{ needs.script-validation.result }}"; then | |
| echo "Script Validation: ${{ needs.script-validation.result }}" | |
| failed=true | |
| fi | |
| if ! check_result "${{ needs.doc-validation.result }}"; then | |
| echo "Doc Validation: ${{ needs.doc-validation.result }}" | |
| failed=true | |
| fi | |
| if ! check_result "${{ needs.workflow-validation.result }}"; then | |
| echo "Workflow Validation: ${{ needs.workflow-validation.result }}" | |
| failed=true | |
| fi | |
| if [ "$failed" = true ]; then | |
| echo "" | |
| echo "Some required tests failed" | |
| exit 1 | |
| fi | |
| echo "All required tests passed" | |
| echo "" | |
| echo "Job Results:" | |
| echo " Script Validation: ${{ needs.script-validation.result }}" | |
| echo " Doc Validation: ${{ needs.doc-validation.result }}" | |
| echo " Workflow Validation: ${{ needs.workflow-validation.result }}" |