Skip to content
Merged
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
4 changes: 1 addition & 3 deletions .github/workflows/reusable-odoo-artifact-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,7 @@ permissions:

jobs:
artifact-publish:
runs-on:
- self-hosted
- ${{ vars.LAUNCHPLANE_RUNNER_LABEL }}
runs-on: ubuntu-latest
outputs:
artifact_id: ${{ steps.launchplane.outputs.artifact_id }}
image_digest: ${{ steps.launchplane.outputs.image_digest }}
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/reusable-odoo-post-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ jobs:
${{ steps.lp.outputs.website_bootstrap_included }}
applied_at: ${{ steps.lp.outputs.applied_at }}
error_message: ${{ steps.lp.outputs.error_message }}
runs-on:
- self-hosted
- ${{ vars.LAUNCHPLANE_RUNNER_LABEL }}
runs-on: ubuntu-latest
steps:
- name: Resolve Launchplane product
id: product
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/reusable-odoo-prod-promotion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ permissions:

jobs:
prod-promotion:
runs-on:
- self-hosted
- ${{ vars.LAUNCHPLANE_RUNNER_LABEL }}
runs-on: ubuntu-latest
steps:
- name: Resolve Launchplane product
id: product
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/reusable-odoo-prod-rollback.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ permissions:

jobs:
prod-rollback:
runs-on:
- self-hosted
- ${{ vars.LAUNCHPLANE_RUNNER_LABEL }}
runs-on: ubuntu-latest
steps:
- name: Resolve Launchplane product
id: product
Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/reusable-odoo-testing-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ jobs:
canonical_status: ${{ steps.poll.outputs.canonical_status }}
logo_status: ${{ steps.poll.outputs.logo_status }}
error_message: ${{ steps.poll.outputs.error_message }}
runs-on:
- self-hosted
- ${{ vars.LAUNCHPLANE_RUNNER_LABEL }}
runs-on: ubuntu-latest
steps:
- name: Resolve Launchplane product
id: product
Expand Down
5 changes: 4 additions & 1 deletion docs/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -787,7 +787,10 @@ context only, and `context_instance` has both context and instance.
`cm_website` resolves to product `odoo-tenant-cm-website`. Post-deploy
workflow outputs include `website_bootstrap_included` so callers can prove
whether the typed website bootstrap payload was rendered into the remote data
workflow request.
workflow request. These reusable workflow jobs use GitHub-hosted runners so
tenant repositories do not need direct access to Launchplane self-hosted
runners; privileged provider mutations still execute inside the deployed
Launchplane service.
- `POST /v1/drivers/odoo/prod-rollback` rolls a prod-named Odoo lane back to
the DB-backed `testing` release tuple for the same context. The driver owns
rollback intent and promotion-record annotation, but the provider mutation runs
Expand Down
23 changes: 9 additions & 14 deletions docs/product-repo-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,15 @@ artifact-record request, idempotency keys, and response mapping. The tenant
workflow should not duplicate `/v1/drivers/odoo/artifact-publish-inputs` or
`/v1/drivers/odoo/artifact-publish` wiring once it uses that workflow. The
reusable workflow defaults the Launchplane product key to
`odoo-tenant-${context}` so publish metadata resolves through the tenant product
profile that owns the image repository and stable lanes. Reusable Odoo workflows
read the Launchplane service URL from `LAUNCHPLANE_PUBLIC_URL` by default and
derive the GitHub OIDC audience from that URL host unless the caller passes an
explicit `launchplane_audience` input.

When a product repo does not have an eligible self-hosted runner lane, operators
may dispatch `reusable-odoo-artifact-publish.yml` directly from Launchplane with
an explicit `product_repository`, `source_git_ref`, product, context, instance,
and confirmation phrase. That path checks out the product source on a
Launchplane-owned runner and uses Launchplane-owned publish/source secrets, while
still sending the same Odoo driver requests and recording the artifact through
Launchplane. It is an operator execution path, not a reason for product repos to
own provider runtime state or duplicate Launchplane request wiring.
`odoo-tenant-${context}` with underscores normalized to dashes, so publish
metadata resolves through the tenant product profile that owns the image
repository and stable lanes. Reusable Odoo workflows read the Launchplane service
URL from `LAUNCHPLANE_PUBLIC_URL` by default and derive the GitHub OIDC audience
from that URL host unless the caller passes an explicit `launchplane_audience`
input. The reusable jobs run on GitHub-hosted runners because they call the
deployed Launchplane service over HTTPS; product repos do not need direct access
to Launchplane self-hosted runners, and privileged provider mutations still run
inside the Launchplane service boundary.

Odoo testing deploys follow the same ownership shape. Tenant repos own the
manual dispatch confirmation and pass an explicit stored `artifact_id` plus
Expand Down
16 changes: 16 additions & 0 deletions tests/test_product_onboarding.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,22 @@ def test_reusable_odoo_prod_workflows_use_tenant_product_scope(self) -> None:
self.assertIn("${{ steps.product.outputs.idempotency_key }}", workflow_text)
self.assertNotIn('"product":"odoo"', workflow_text)

def test_reusable_odoo_workflows_use_caller_visible_runner(self) -> None:
workflow_paths = (
Path(".github/workflows/reusable-odoo-artifact-publish.yml"),
Path(".github/workflows/reusable-odoo-testing-deploy.yml"),
Path(".github/workflows/reusable-odoo-post-deploy.yml"),
Path(".github/workflows/reusable-odoo-prod-promotion.yml"),
Path(".github/workflows/reusable-odoo-prod-rollback.yml"),
)

for workflow_path in workflow_paths:
with self.subTest(workflow=workflow_path.name):
workflow_text = workflow_path.read_text(encoding="utf-8")
self.assertIn("runs-on: ubuntu-latest", workflow_text)
self.assertNotIn("LAUNCHPLANE_RUNNER_LABEL", workflow_text)
self.assertNotIn("runs-on:\n - self-hosted", workflow_text)

def test_odoo_website_bootstrap_override_requires_explicit_target_inputs(self) -> None:
workflow_text = Path(".github/workflows/odoo-website-bootstrap-override.yml").read_text(
encoding="utf-8"
Expand Down