diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml new file mode 100644 index 0000000..8aad74c --- /dev/null +++ b/.github/workflows/sonar.yml @@ -0,0 +1,31 @@ +name: SonarQube + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +concurrency: + group: sonar-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + analysis: + name: SonarQube analysis + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + fetch-depth: 0 + persist-credentials: false + - uses: SonarSource/sonarqube-scan-action@d209202bc7d53ff1cc128f7f907dac145c9d6ae9 # v8.3.0 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + with: + args: -Dsonar.qualitygate.wait=true -Dsonar.qualitygate.timeout=300 diff --git a/README.md b/README.md index de32f59..d0734d8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Durable Workflow AI +[](https://sonarcloud.io/summary/new_code?id=durable-workflow_ai) +
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000..71ac316
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,14 @@
+sonar.organization=durable-workflow
+sonar.projectKey=durable-workflow_ai
+sonar.projectName=ai
+sonar.sourceEncoding=UTF-8
+sonar.sources=.
+sonar.tests=.
+sonar.test.inclusions=tests/**,**/tests/**,**/*.test.*,**/*.spec.*,**/test_*.py,benchmarks/**,scripts/conformance/**
+# Dependencies, generated assets and disposable runtime/build output are not source.
+sonar.exclusions=**/vendor/**,**/node_modules/**,**/target/**,**/dist/**,**/build/**,**/storage/**,**/.venv/**,**/__pycache__/**,**/coverage/**,**/.scannerwork/**,**/.git/**,static/vendor/**,public/vendor/**,resources/js/vendor/**
+sonar.test.exclusions=**/vendor/**,**/node_modules/**,**/target/**,**/.venv/**
+# This CI job performs static analysis. Repository test/coverage gates stay required.
+# No coverage report is collected here; leave that metric unmeasured in Sonar.
+sonar.coverage.exclusions=**/*
+sonar.sca.enabled=false
diff --git a/src/Providers/E2bSandboxProvider.php b/src/Providers/E2bSandboxProvider.php
index 36ff858..0855d42 100644
--- a/src/Providers/E2bSandboxProvider.php
+++ b/src/Providers/E2bSandboxProvider.php
@@ -106,9 +106,7 @@ public function provision(array $options = []): SandboxHandle
id: $id,
provider: $this->name(),
metadata: [
- 'template_id' => is_array($body)
- ? (string) ($body['templateID'] ?? $this->templateId)
- : $this->templateId,
+ 'template_id' => (string) ($body['templateID'] ?? $this->templateId),
],
);
}
diff --git a/tests/Unit/E2bSandboxProviderTest.php b/tests/Unit/E2bSandboxProviderTest.php
index c9af17b..961d553 100644
--- a/tests/Unit/E2bSandboxProviderTest.php
+++ b/tests/Unit/E2bSandboxProviderTest.php
@@ -17,6 +17,7 @@
use Illuminate\Http\Client\Factory;
use Illuminate\Http\Client\Request;
use InvalidArgumentException;
+use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use RuntimeException;
use Throwable;
@@ -64,6 +65,44 @@ public function test_provision_uses_documented_management_contract_and_bounded_t
});
}
+ #[DataProvider('invalidProvisionResponses')]
+ public function test_provision_refuses_a_response_without_a_sandbox_identity(string $body): void
+ {
+ $this->http->fake([
+ 'https://api.e2b.app/sandboxes' => $this->http->response($body, 201, [
+ 'Content-Type' => 'application/json',
+ ]),
+ ]);
+
+ $this->expectException(SandboxProvisionException::class);
+ $this->expectExceptionMessage('did not include sandboxID');
+ $this->provider->provision();
+ }
+
+ /** @return array