A reference repository demonstrating GitHub Actions workflows for Go and Python applications. It includes two sample apps:
sample-go-app/— a Go application with a Dockerfilesample-py-app/— a Python application
.
├── .github/
│ ├── copilot/
│ │ └── mcp.json
│ ├── dependabot.yml
│ └── workflows/
│ ├── auto-update-pr-branches.yaml
│ ├── copilot-setup-steps.yml
│ ├── code-scanning.yaml
│ ├── dependabot-auto-approve.yaml
│ ├── go.yaml
│ ├── image.yaml
│ ├── main.yaml
│ ├── pull-request.yaml
│ └── py.yaml
├── sample-go-app/
│ ├── Dockerfile
│ ├── go.mod
│ ├── go.sum
│ ├── internal/
│ └── main.go
├── sample-py-app/
│ ├── main.py
│ └── requirements.txt
└── README.md
push to main ──► main.yaml ──► go.yaml
└──► py.yaml
└──► image.yaml (build only, no push)
code-scanning.yaml (go linting + SARIF upload)
auto-update-pr-branches.yaml (rebase open PRs)
copilot-setup-steps.yml (prepare Copilot cloud agent)
pull request ──► pull-request.yaml ──► go.yaml
└──► py.yaml
└──► image.yaml (build only, no push)
code-scanning.yaml (go linting + SARIF upload)
copilot-setup-steps.yml (prepare Copilot cloud agent)
release published ──► image.yaml (build + push to ghcr.io)
dependabot PR ─► dependabot-auto-approve.yaml
Trigger: push to main
Orchestrates the core CI pipeline on every commit to main. Calls the three reusable workflows below:
go.yaml— build and test the Go apppy.yaml— set up Python and install dependenciesimage.yaml— build the Docker image (does not push)
Trigger: pull request targeting main
Runs the same pipeline as main.yaml to validate every PR before it can be merged:
go.yaml— build and test the Go apppy.yaml— set up Python and install dependenciesimage.yaml— build the Docker image (does not push)
Trigger: push to main and pull requests targeting main
Runs golangci-lint (v2.12.0) against sample-go-app/ and uploads the results as a SARIF report to GitHub code scanning (under Security → Code scanning). The upload step runs even if linting fails (if: always()).
Trigger: push to main
After every merge to main, iterates over all open PRs targeting main and rebases them onto the latest main using gh pr update-branch --rebase. This keeps PR branches up to date automatically.
Uses a GitHub App (Jeeves) for authentication so that the update triggers other required checks. Only runs in the maansaake/github-actions-help repository.
Trigger: pull_request_target (opened, synchronize, reopened)
Automates merging of Dependabot PRs:
- Minor and patch updates — approves the PR and enables auto-merge (squash). If the PR is force-updated after a previous approval, re-approves it.
- Major updates — leaves a comment asking for manual review.
Uses a GitHub App (Jeeves) for authentication. Only runs for PRs authored by dependabot[bot] in the maansaake/github-actions-help repository.
Trigger: manual dispatch, updates to the workflow file itself
Prepares the Copilot cloud agent environment for this mixed Go/Python repository:
- Checks out the repository.
- Sets up Go from
sample-go-app/go.mod. - Sets up Python 3.14 with pip caching.
- Caches Go modules and the Go build cache.
- Downloads Go dependencies for
sample-go-app/and itstools/module, plus Python dependencies forsample-py-app/. - Installs
golangci-lintand warms upgovulncheck.
.github/copilot/mcp.json configures the GitHub MCP server so the Copilot agent can use GitHub tools while working in this repository.
These workflows are not triggered directly; they are called by the trigger workflows above using workflow_call.
Builds and tests sample-go-app/ against both the stable and oldstable Go releases (matrix strategy), ensuring compatibility with the current and previous minor versions. Steps:
- Check out the repository.
- Set up Go (caching disabled at the action level — see below).
- Cache Go modules (
~/go/pkg/mod) and the build cache (~/.cache/go-build) with ago-build-prefix so older cache entries are reused when the exact SHA key misses. - Download dependencies (
go mod download). - Run tests with coverage (
go test ./... -coverprofile=coverage.out). - Generate an HTML coverage report.
- Upload the coverage report as a workflow artifact (
go-coverage-report-<stable|oldstable>). - Build the binary (
go build -o sample-go-app).
Why is built-in caching disabled? The
actions/setup-gobuilt-in cache keys on the hash ofgo.sum. That file only changes when dependencies change, not when application code changes, which can cause stale build-cache hits. This workflow instead usesactions/cachewith agithub.sha-based key and a sharedgo-build-restore prefix, so the build cache is always fresh for the current commit while still benefiting from earlier runs.
Sets up sample-py-app/ for the configured Python version (currently 3.14). Steps:
- Check out the repository.
- Set up Python with
pipcaching enabled. - Install dependencies from
sample-py-app/requirements.txt.
Builds the Docker image for sample-go-app/ and optionally pushes it to the GitHub Container Registry (ghcr.io).
Triggers: workflow_call (from main.yaml and pull-request.yaml) or directly on release: published. When triggered by the release event, push is automatically set to true.
Inputs (only applicable when called via workflow_call):
| Input | Type | Default | Description |
|---|---|---|---|
version |
string | "latest" |
Tag to apply to the image |
push |
boolean | false |
Whether to push the image to the registry |
Steps:
- Check out the repository.
- Set up Docker Buildx.
- Log in to
ghcr.iousingGITHUB_TOKEN. - Generate OCI-compliant image metadata (tags and labels) via
docker/metadata-action. - Build (and push if
push: true) the image fromsample-go-app/Dockerfile, passingVERSION=${{ github.sha }}as a build argument.
A concurrency group (image-build-<version>) ensures that parallel image builds for the same version do not interfere with each other.
GitHub code scanning treats pull request scans and branch scans as separate result streams, so both are needed for full coverage. Running on pull requests gives review-time feedback before merge, and running on main ensures the default branch always has fresh, authoritative alerts for Security reporting and branch-level visibility.
Keeping both triggers in the same workflow file ensures the exact same linting and SARIF upload logic is used in both contexts, which prevents drift and avoids mismatched scan behavior between pre-merge and post-merge checks.
.github/dependabot.yml configures Dependabot to open weekly grouped PRs for:
| Ecosystem | Directory |
|---|---|
Go modules (gomod) |
/sample-go-app |
| Python pip | /sample-py-app |
| Docker | /sample-go-app |
| GitHub Actions | / |
Updates are grouped into minor and patch buckets to reduce PR noise.