Bitbucket Pipelines: client-extensions ZIP not generated ( clean deploy and buildClientExtensionZip don’t pick up CE )

Hi everyone,

I am trying to build client-extensions in Bitbucket Pipelines the same way they work locally, but the ZIP never gets produced in CI.

Environment

  • Liferay Workspace plugin: com.liferay.gradle.plugins.workspace: 11.0.2

  • Gradle / Java: Gradle (8.5 wrapper), openjdk:21 container

  • DXP: (2025.q1.0-lts)

  • CI: Bitbucket Pipelines

  • Node/Angular: Node 22.12, Angular CLI builds run before Gradle

Workspace setup

settings.gradle dynamically includes CE projects (parallel to modules/):

apply plugin: "com.liferay.workspace"

file("client-extensions").listFiles()?.each { dir ->
    if (dir.isDirectory() && file("${dir}/client-extension.yaml").exists()) {
        include "client-extensions:${dir.name}"
    }
}

Example CE YAML (custom element):

# client-extensions/post-form-ce/client-extension.yaml
assemble:
  - from: ../dist/post-form/browser
    into: static

post-form:
  type: customElement
  htmlElementName: post-form
  friendlyURLMapping: post-form
  useESM: true
  urls:
    - polyfills.js
    - main.js
  cssURLs:
    - styles.css
  instanceable: false
  portletCategoryName: category.client-extensions
  name: Post Form

What works locally

Running from workspace root:

./gradlew clean deploy

→ CE ZIP is generated and appears in the deploy folder (works fine on my machine).

What I’m doing in Bitbucket

  1. Build Angular first so client-extensions/dist/... exists.

  2. Pre-seed ~/.liferay/workspace/releases.json.

  3. Run Gradle like local:

./gradlew --no-daemon buildService assemble
./gradlew --no-daemon test
./gradlew --no-daemon clean deploy -Pliferay.workspace.home.dir="$PWD/build/liferay-home"

(I also tried the module task:)

./gradlew --no-daemon :client-extensions:post-form-ce:buildClientExtensionZip

Symptoms in CI

  • ./gradlew clean deploy completes without error, but I don’t see any CE ZIP at:

    • build/liferay-home/deploy/*.zip, or

    • build/client-extensions/*.zip, or

    • client-extensions/*-ce/build/*.zip

  • When I call the module task directly, either:

    • Gradle says task not found for buildClientExtensionZip, or

    • it runs but still no zip is emitted.

  • Listing projects in CI sometimes doesn’t show the CE modules as included.

Minimal CI snippet (Bitbucket)

image: openjdk:21
script:
  - set -e
  # Node for Angular
  - apt-get update && apt-get install -y xz-utils curl unzip
  - NODE_V=22.12.0
  - curl -fsSLO https://nodejs.org/dist/v$NODE_V/node-v$NODE_V-linux-x64.tar.xz
  - xz -dc node-v$NODE_V-linux-x64.tar.xz | tar -x -C /usr/local --strip-components=1
  - cd client-extensions && npm ci && npm run build:all && cd ..
  # Workspace releases.json
  - mkdir -p /root/.liferay/workspace
  - curl -fsSL https://releases.liferay.com/releases.json -o /root/.liferay/workspace/releases.json
  # Build like local
  - chmod +x gradlew
  - ./gradlew --no-daemon clean deploy -Pliferay.workspace.home.dir="$PWD/build/liferay-home"
  # Debug inventories
  - ./gradlew -q projects
  - ls -la build/liferay-home/deploy || true
  - ls -la build/client-extensions || true
  - ls -la client-extensions/*-ce/build || true

Questions

  1. Which Gradle task is the canonical one (on Workspace plugin 11.0.2) to produce the client-extension ZIP in CI? Is clean deploy still the recommended path, or should I call a specific CE task (e.g. buildClientExtensions / assembleClientExtensions / per-module buildClientExtensionZip)?

  2. Is it supported to assemble assets from a sibling dist/ (as in from: ../dist/... in client-extension.yaml), or should the build artifacts be copied inside the CE module before packaging?

  3. Are there known gotchas in CI where CEs are not included by the settings.gradle dynamic include? (e.g., working directory assumptions, filename must be exactly client-extension.yaml, etc.)

  4. Any additional property needed when using -Pliferay.workspace.home.dir in CI for deploy to emit CE zips?

  5. If buildClientExtensionZip is the correct task, is there a minimal example CE project where that task is present on plugin 11.0.2 so I can compare?

Log excerpts

(please see attached, or let me know which specific lines you need; happy to paste the output of ./gradlew -q projects and :client-extensions:<my-ce>:tasks --all from CI)

Thanks in advance! Any guidance or a small reproducible example would be greatly appreciated.

I can’t answer on the same detail level as your question, but maybe contribute some low hanging fruits to look at:

You’re using workspace 11.0.2 from August 24. Consider using a newer version. Even on 11 there are newer patches, but you can also try the newer versions.

if this low hanging fruit tastes good, we don’t need to climb higher and hunt for a bug that might potentially already be fixed.

@Waseef thanks for bringing this up!

So my very first question is : Why is it necessary to manually include the client-extensions/* projects in settings.gradle the way you shared? Liferay Workspace should pick those up automatically when the project contains a client-extension.yaml file. I’d like to start here because I fear that manually including them may interfere with the plugin adding necessary tasks like createClientExtensionConfig, or:

Gradle says task not found for buildClientExtensionZip

as you mentioned.

Thanks for all the other details!

I’d want to verify all of the commands that CI is doing work locally…

As soon as CI calls gradlew …., it is passing control to Gradle to complete the steps. You indicate that gradlew clean deploy works locally, but then you have CI invoking a bunch of different commands, almost like you’re assuming they all should work on CI because the one command works for you locally.

@Waseef Did you ever find a solution for this?
We’re trying to deploy using Bitbucket Pipelines and have the same issue - nothing in /client-extensions or /modules is picked up, while everything works fine locally and when run with the same commands as in the bitbucket-pipelines.yml in a local Docker container :person_shrugging:

Hi @ChrisHof ,
Sorry for the late response — I was out of town.

By the way, I’ve resolved the issue, not technically but through a different approach. I restructured the project as follows:

  • New Folder

    • Actual Folder (moved Bitbucket pipeline files and other pipeline-related scripts outside; kept the client-extensions and modules inside this folder)
  • bitbucket-pipelines.yml (updated the build path to point to the Actual folder)

I hope this helps.

Kind regards,
Waseef