Add Docker support for Goose in CI/CD pipelines (#4434)

Adds a production-ready Docker image for Goose that enables using Goose itself in CI pipelines to improve the codebase.
This commit is contained in:
tlongwell-block
2025-09-03 10:14:41 -04:00
committed by GitHub
parent a40a9aa8f8
commit 7034d1593c
5 changed files with 703 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
name: Publish Docker Image
on:
push:
branches:
- main
tags:
- 'v*.*.*'
- 'v*.*.*-*' # For pre-releases like v1.2.3-beta
paths-ignore:
- 'documentation/**'
- '*.md'
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # pin@v3.11.1
- name: Log in to GitHub Container Registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # pin@v3.5.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # pin@v5.8.0
with:
images: ghcr.io/${{ github.repository_owner }}/goose
tags: |
# For main branch: latest, main, and sha
type=ref,event=branch
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
# For tags: v1.2.3 -> 1.2.3, 1.2, 1, latest (if not pre-release)
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
# For pre-release tags: keep the full version
type=raw,value={{tag}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
- name: Build and push Docker image
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # pin@v6.18.0
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
+143
View File
@@ -0,0 +1,143 @@
name: Daily Test Coverage Finder
on:
schedule:
# Run daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (no PR creation)'
required: false
default: false
type: boolean
permissions:
contents: write
pull-requests: write
jobs:
find-untested-code:
runs-on: ubuntu-latest
container:
image: ghcr.io/block/goose:latest
options: --user root
env:
GOOSE_PROVIDER: ${{ vars.GOOSE_PROVIDER || 'openai' }}
GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'gpt-4o' }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4
with:
fetch-depth: 0
- name: Install analysis tools
run: |
apt-get update
apt-get install -y jq ripgrep
- name: Find untested code and create working test
id: find_untested
run: |
# Create analysis and test creation script
cat << 'EOF' > /tmp/create_working_test.txt
Your task is to find ONE untested function in the Rust codebase and create a working test for it.
Requirements:
1. The function MUST be in the crates/ directory
2. It MUST have actual logic (not just a simple getter/setter)
3. It MUST not already have a test
4. Prefer functions with complexity but that are still testable in isolation
5. Focus on the goose crate first, then goose-cli, then others
Process:
1. Find a suitable untested function
2. Write a comprehensive unit test for it
3. Apply your changes to the codebase
4. Run the test with: cargo test --test <test_name>
5. If the test fails:
- Read and understand the error message
- Fix the test code
- Apply the fix and run the test again
- Repeat up to 3 times until the test passes
6. Once the test passes (or after 3 attempts), save the final changes as a git diff to /tmp/test_addition.patch
7. Report the outcome:
- If successful: write "SUCCESS" to /tmp/test_result.txt
- If no suitable function found: write "NO_FUNCTION_FOUND" to /tmp/test_result.txt
- If test couldn't be fixed: write "TEST_FAILED" to /tmp/test_result.txt
Important:
- Only add ONE test for ONE function
- The test MUST compile and pass before creating the patch
- Keep changes minimal and focused
- Include a descriptive test name that explains what is being tested
EOF
goose run -i /tmp/create_working_test.txt
# Check the result
if [ -f /tmp/test_result.txt ]; then
RESULT=$(cat /tmp/test_result.txt)
echo "Test creation result: $RESULT"
if [ "$RESULT" = "SUCCESS" ] && [ -f /tmp/test_addition.patch ]; then
echo "patch_created=true" >> $GITHUB_OUTPUT
# Extract function name from patch for PR title
FUNC_NAME=$(grep -E "fn test_|#\[test\]" /tmp/test_addition.patch | head -1 | sed 's/.*test_//' | sed 's/(.*//' || echo "function")
echo "function_name=${FUNC_NAME}" >> $GITHUB_OUTPUT
else
echo "patch_created=false" >> $GITHUB_OUTPUT
echo "Reason: $RESULT"
fi
else
echo "patch_created=false" >> $GITHUB_OUTPUT
echo "No result file created"
fi
- name: Create Pull Request
if: steps.find_untested.outputs.patch_created == 'true' && github.event.inputs.dry_run != 'true'
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # pin@v7.0.8
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "test: add test for ${{ steps.find_untested.outputs.function_name }}"
title: "test: add test coverage for ${{ steps.find_untested.outputs.function_name }}"
body: |
## 🤖 Automated Test Addition
This PR was automatically generated by Goose to improve test coverage.
### What changed?
Added a unit test for a previously untested function.
### Why?
Part of our daily automated test coverage improvement initiative. Goose analyzes the codebase to find untested but important functions and creates focused unit tests for them.
### Review checklist
- [ ] Test is meaningful and actually tests the function
- [ ] Test name is descriptive
- [ ] Test passes locally
- [ ] No unnecessary changes included
---
*Generated by the Daily Test Coverage Finder workflow*
branch: goose/test-coverage-${{ github.run_number }}
delete-branch: true
labels: |
goose-generated
test
automated
- name: Summary
if: always()
env:
PATCH_CREATED: ${{ steps.find_untested.outputs.patch_created }}
FUNCTION_NAME: ${{ steps.find_untested.outputs.function_name }}
run: |
if [ "$PATCH_CREATED" == "true" ]; then
echo "✅ Successfully found untested code and created a test"
echo "📝 Function tested: $FUNCTION_NAME"
else
echo "️ No suitable untested code found today"
fi