Skip to content

Added optional path to the codegen file for configuring the acceptance tests #502

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 15, 2025
76 changes: 75 additions & 1 deletion acceptance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,82 @@ sequenceDiagram

## Usage

Add to your `.github/workflows` folder:
Add a file named `acceptance.yml` to the `.github/workflows` folder in your repository.
This file defines a GitHub Actions workflow for running acceptance tests on pull requests.
The workflow sets up the environment, installs dependencies, executes tests, and uploads artifacts for further analysis.

Example `acceptance.yml` for Python projects:

```yaml
name: acceptance

on:
pull_request:
types: [ opened, synchronize, ready_for_review ]
merge_group:
types: [ checks_requested ]

permissions:
id-token: write
contents: read
pull-requests: write

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
acceptance:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Python
uses: actions/setup-python@v5
with:
cache: 'pip'
cache-dependency-path: '**/pyproject.toml'
python-version: '3.10'

- name: Install hatch
run: pip install hatch==1.9.4

- name: Run integration tests
uses: databrickslabs/sandbox/acceptance@acceptance/v0.4.4
with:
vault_uri: ${{ secrets.VAULT_URI }}
timeout: 2h
# optional path to codegen file containing configuration for the tests
# by default first `codegen.json` file found in the repository is used
# codegen_path: tests/integration/.codegen.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

The project must include a `.codegen.json` file, which defines the toolchain configuration for the action.
This configuration specifies details such as versioning, required tools, and the paths to acceptance tests.
If a `codegen_path` field is not explicitly provided, the action will automatically search the project for the `.codegen.json` file and use the first one it locates.

Example `.codegen.json`:
```json
{
"version": {
"src/databricks/labs/project_name/__about__.py": "__version__ = \"$VERSION\""
},
"toolchain": {
"required": ["python3", "hatch"],
"pre_setup": ["hatch env create"],
"prepend_path": ".venv/bin",
"acceptance_path": "tests/integration"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding Readme.md

I like this change. Today we handle this with this plumbing approach, which simplifies things drastically https://github.com/databrickslabs/lakebridge/blob/main/tests/integration/conftest.py#L31

Tagging @asnare for any impact on UCX

}
}
```

Note: if `acceptance_path` is not provided in the `codegen.json`, the action will execute all tests in the project by default.

Example for uploading artifacts to GitHub Actions:
```yaml
name: acceptance

Expand Down
3 changes: 3 additions & 0 deletions acceptance/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ inputs:
description: 'Create issues in the repository for failed tests'
required: false
default: false
codegen_path:
description: 'Relative path to the .codegen.json file to use for configuring the acceptance tests'
required: false # by default the first .codegen.json found in the project is used
outputs:
sample:
description: 'Sample output'
Expand Down
3 changes: 2 additions & 1 deletion acceptance/ecosystem/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ func (py *pyContext) Start(script string, reply *localHookServer) chan error {
}

func (r pyTestRunner) prepare(ctx context.Context, redact redaction.Redaction, logfile string) (*pyContext, error) {
tc, err := toolchain.FromFileset(r.files)
codegenPath := env.Get(ctx, "codegen_path")
tc, err := toolchain.FromFileset(r.files, &codegenPath)
if err != nil {
return nil, fmt.Errorf("detect: %w", err)
}
Expand Down
3 changes: 3 additions & 0 deletions acceptance/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func (a *acceptance) trigger(ctx context.Context) (*notify.Notification, error)
defer stop()
// make sure that test logs leave their artifacts somewhere we can pickup
ctx = env.Set(ctx, ecosystem.LogDirEnv, artifactDir)
// set codegen path file used for configuring the acceptance tests
codegenPath := a.Action.GetInput("codegen_path")
ctx = env.Set(ctx, "codegen_path", codegenPath)
redact := loaded.Redaction()
report, err := a.runWithTimeout(ctx, redact, directory)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion go-libs/linkdev/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (r Repo) Retest(ctx context.Context, upstreamFolder string) error {
return fmt.Errorf("fileset: %w", err)
}
// defer cleanup()
tc, err := toolchain.FromFileset(downstreamFiles)
tc, err := toolchain.FromFileset(downstreamFiles, nil)
if err != nil {
return fmt.Errorf("toolchain: %w", err)
}
Expand Down
32 changes: 24 additions & 8 deletions go-libs/toolchain/toolchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,33 @@ import (
"github.com/databrickslabs/sandbox/go-libs/process"
)

var ErrNotExist = fmt.Errorf("no .codegen.json found. %w", fs.ErrNotExist)
func FromFileset(files fileset.FileSet, codegenPath *string) (*Toolchain, error) {
var raw []byte
var err error

func FromFileset(files fileset.FileSet) (*Toolchain, error) {
configs := files.Filter(".codegen.json")
if len(configs) == 0 {
return nil, ErrNotExist
// Helper function to filter files and retrieve raw content
getFileContent := func(filter string) ([]byte, error) {
filteredFiles := files.Filter(filter)
if len(filteredFiles) == 0 {
return nil, fmt.Errorf("file not found in fileset: %s", filter)
}
return filteredFiles[0].Raw()
}
raw, err := configs[0].Raw()
if err != nil {
return nil, fmt.Errorf("read: %w", err)

// Check if codegenPath is provided and retrieve content
if codegenPath != nil && *codegenPath != "" {
raw, err = getFileContent(*codegenPath)
if err != nil {
return nil, fmt.Errorf("provided 'codegen_path' does not exist in the project: %w", fs.ErrNotExist)
}
} else {
raw, err = getFileContent(".codegen.json")
if err != nil {
return nil, fmt.Errorf("no .codegen.json found. %w", fs.ErrNotExist)
}
}

// Unmarshal JSON content into dotCodegen struct
var dc dotCodegen
err = json.Unmarshal(raw, &dc)
if err != nil {
Expand Down
Loading
Loading