Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions .github/workflows/projecttests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Project Tests

permissions:
contents: read
actions: write
checks: write

on:
pull_request:
push:
branches:
- main

jobs:
test-go-projecttests:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- uses: actions/setup-go@v4
with:
go-version-file: "go.mod"
- name: Run Go project tests
run: |
go test -v -race -timeout 10m ./projecttests/... 2>&1 | \
go run github.com/jstemmer/go-junit-report/v2@latest \
-set-exit-code -iocopy -out junit-projecttests-go.xml
Comment on lines +24 to +27
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should probably just be two separate steps

- name: Publish Test Results
uses: mikepenz/action-junit-report@v5.6.2
if: failure()
with:
report_paths: "junit-projecttests-go.xml"
check_name: "Go Project Test Failures"
detailed_summary: true
check_annotations: false
annotate_only: true
skip_annotations: true

build-go-projecttest-image:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build project test base image
run: |
docker build \
-f projecttests/dockerfiles/projecttest-go-base.Dockerfile \
-t omes-projecttest-go-base:ci \
.
- name: Build helloworld project overlay
run: |
docker build \
-f projecttests/dockerfiles/projecttest-go.Dockerfile \
--build-arg BASE_IMAGE=omes-projecttest-go-base:ci \
--build-arg TEST_PROJECT=projecttests/go/tests/helloworld \
-t omes-projecttest-helloworld:ci \
.
- name: Smoke test overlay image
run: |
docker run --rm omes-projecttest-helloworld:ci --help
Comment on lines +60 to +62
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure I'd call that a smoke test?


check-project-protos:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Install buf
uses: bufbuild/buf-action@v1
with:
setup_only: true
- name: Generate project protos
run: |
cd projecttests/proto && buf generate
- name: Lint project protos
run: |
cd projecttests/proto && buf lint
- name: Check for uncommitted changes
run: |
git diff --exit-code projecttests/go/harness/api/ || \
(echo "Project proto generated code is out of date. Run 'cd projecttests/proto && buf generate'" && exit 1)
12 changes: 10 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.vscode
.env
.venv
.mypy_cache
go.work
Expand All @@ -23,7 +24,14 @@ workers/dotnet/Temporalio.Omes.temp.csproj
workers/python/**/__pycache__/
workers/*/omes-temp-*/
workers/*/prepared/

tsconfig.tsbuildinfo

temporal-omes
.gocache/

# Generated program directories (test workers)
projecttests/*/tests/**/program-*/

# Test directory build artifacts
projecttests/*/tests/**/dist/
projecttests/*/tests/**/node_modules/
projecttests/*/tests/**/package-lock.json
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ go run ./cmd run-worker --language python --run-id my-run \
When using the Prometheus instance (`--prom-instance-addr`), sidecar metrics can be exported to parquet files on shutdown. The export includes SDK version, build ID, and language from the `/info` endpoint.

- `--prom-export-worker-metrics` - Path to export metrics parquet file
- `--prom-export-process-job` - Prometheus job name for process metrics (default: `omes-worker-process`)
- `--prom-export-process-job` - Prometheus job name for process metrics (default: `omes_worker_process`)
- `--prom-export-worker-info-address` - Address to fetch `/info` from during export (e.g., `localhost:9091`)

## Usage
Expand Down Expand Up @@ -279,11 +279,6 @@ A scenario can only fail if an `Execute` method returns an error, that means the
authors's hands. For enforcing a timeout for a scenario, use options like workflow execution timeouts or write a
workflow that waits for a signal for a configurable amount of time.

## TODO

- Nicer output that includes resource utilization for the worker (when running all-in-one)
- Ruby worker

## Development

Use the dev command for development tasks:
Expand All @@ -307,3 +302,8 @@ All versions are defined in `versions.env`.
* Core cancel-before-start on abandon activities: [PR](https://github.com/temporalio/sdk-core/pull/652)
* Core panic on evicting run with buffered tasks: [PR](https://github.com/temporalio/sdk-core/pull/660)
* Out of order replay for local activity + cancel: [PR](https://github.com/temporalio/sdk-core/issues/803)

## TODO

- Nicer output that includes resource utilization for the worker (when running all-in-one)
- Ruby worker
129 changes: 129 additions & 0 deletions cmd/cli/exec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package cli

import (
"context"
"fmt"
"syscall"

"github.com/spf13/cobra"
"github.com/temporalio/omes/cmd/clioptions"
"github.com/temporalio/omes/internal/programbuild"
"go.uber.org/zap"
)

func execCmd() *cobra.Command {
var r execRunner

cmd := &cobra.Command{
Use: "exec --language <lang> -- <program-args>",
Short: "Build a project and run it as a subprocess",
Long: `Build a test project and run the resulting binary as a subprocess.
Arguments after "--" are passed directly to the subprocess.

Signal forwarding and graceful shutdown are handled automatically.

Examples:
# Start a worker on a specific task queue
omes exec --language go --project-dir ./projecttests/go/tests/helloworld -- worker --task-queue my-queue

# Start a project gRPC server on port 8080
omes exec --language go --project-dir ./projecttests/go/tests/helloworld -- project-server --port 8080

# Start a worker with process metrics sidecar for CPU/memory monitoring
omes exec --language go --project-dir ./projecttests/go/tests/helloworld --process-monitor-addr :9091 -- worker --task-queue my-queue`,
PreRunE: func(cmd *cobra.Command, args []string) error {
r.preRun()
return r.validate()
},
RunE: func(cmd *cobra.Command, args []string) error {
ctx, cancel := withCancelOnInterrupt(cmd.Context())
defer cancel()
return r.run(ctx, args)
},
}

r.sdkOpts.AddCLIFlags(cmd.Flags())
cmd.Flags().Lookup("language").Usage = "Language to use for workflow tests (go only)"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It won't be Go only except currently though?

Also, seemingly the language flag is redundant, since the projecttests path includes the language

r.programOpts.AddFlags(cmd.Flags())
cmd.Flags().AddFlagSet(r.loggingOpts.FlagSet())
cmd.Flags().StringVar(&r.processMonitorAddr, "process-monitor-addr", "", "Address for process metrics sidecar (e.g. :9091)")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should this be just a port number? Seems like it would not make sense to ever bind to anything other than 0.0.0.0?


cmd.MarkFlagRequired("language")
cmd.MarkFlagRequired("project-dir")

return cmd
}

type execRunner struct {
sdkOpts clioptions.SdkOptions
programOpts clioptions.ProgramOptions
loggingOpts clioptions.LoggingOptions
processMonitorAddr string

logger *zap.SugaredLogger
}

func (r *execRunner) preRun() {
r.logger = r.loggingOpts.MustCreateLogger()
}

func (r *execRunner) validate() error {
if r.sdkOpts.Language != clioptions.LangGo {
return fmt.Errorf("--language must be go for workflow tests (got %q)", r.sdkOpts.Language)
}
if r.programOpts.ProgramDir == "" {
return fmt.Errorf("--project-dir is required")
}
return nil
}

func (r *execRunner) run(ctx context.Context, args []string) error {
builder := &programbuild.ProgramBuilder{
Language: r.sdkOpts.Language.String(),
ProjectDir: r.programOpts.ProgramDir,
BuildDir: r.programOpts.BuildDir,
Logger: r.logger,
}

prog, err := builder.BuildProgram(ctx, r.sdkOpts.Version)
if err != nil {
return fmt.Errorf("failed to build program: %w", err)
}

cmd, err := programbuild.StartProgramProcess(ctx, prog, args)
if err != nil {
return fmt.Errorf("failed to start program: %w", err)
}

r.logger.Infof("Started subprocess (PID %d): %v", cmd.Process.Pid, args)

// Start process metrics sidecar (if requested)
if r.processMonitorAddr != "" {
sidecar := clioptions.StartProcessMetricsSidecar(
r.logger,
r.processMonitorAddr,
cmd.Process.Pid,
r.sdkOpts.Version,
"",
r.sdkOpts.Language.String(),
)
defer func() {
if err := sidecar.Shutdown(context.Background()); err != nil {
r.logger.Warnf("Failed to stop process metrics sidecar: %v", err)
}
}()
}

err = cmd.Wait()
if err != nil {
if cmd.ProcessState != nil {
if status, ok := cmd.ProcessState.Sys().(syscall.WaitStatus); ok {
return fmt.Errorf("process exited with code %d", status.ExitStatus())
}
return fmt.Errorf("process exited with code 1")
}
return fmt.Errorf("failed waiting for process: %w", err)
}

return nil
}
2 changes: 2 additions & 0 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ func Main() {
}

rootCmd.AddCommand(cleanupScenarioCmd())
rootCmd.AddCommand(execCmd())
rootCmd.AddCommand(listScenariosCmd())
rootCmd.AddCommand(prepareWorkerCmd())
rootCmd.AddCommand(runScenarioCmd())
rootCmd.AddCommand(runScenarioWithWorkerCmd())
rootCmd.AddCommand(runWorkerCmd())
rootCmd.AddCommand(projectCmd())

if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
Expand Down
Loading
Loading