Continuous Integration
Liferay Cloud uses Jenkins to power its continuous integration infrastructure service. When you send a pull request or push a commit to one of your pre-configured GitHub branches, an automatic and configurable build is triggered.
By default, your project’s Jenkins instance is reachable from any IP address on the internet, with access controlled by the Jenkins login. See Restricting Access to Your Project’s Jenkins by IP to limit access to your organization’s networks.
Liferay Cloud customers (using the customer login) have permissions to manage and review their builds, but do not have full administrative privileges.
By default, this automated build compiles code and can be configured to execute tests. Liferay Cloud builds your services and shows their status on your environment’s Builds page. If the tests fail, you can check the Jenkins dashboard and logs at https://ci-companyname-infra.lfr.cloud.
Continuous integration only works if you deploy from GitHub, GitLab, or Bitbucket, not the CLI.
See the CI service limitations for more information.
Setting the JDK Version
By default, the CI service uses JDK 8. Jenkins uses the configured JDK version to execute any tasks or compile any code in your builds.
With CI version 6.0.0+, if your builds require a specific Java version (other than the default), you can update the LCP_CI_GRADLE_JDK environment variable, in the console or the CI service’s LCP.json file.
Here are the allowed values for LCP_CI_GRADLE_JDK:
jdk8jdk11jdk17jdk21
Using the Default Jenkinsfile
The CI service includes a default Jenkinsfile used for your project’s builds. The default Jenkinsfile encapsulates all the logic that was previously stored on the Jenkinsfile and moves it to a Jenkins plugin. This means that all bug fixes, security fixes, and improvements can be applied without requiring any CI configuration.
Additionally, extension points are available to customize every step of the CI pipeline.
Extending the Default Jenkinsfile
To extend the default Jenkinsfile, you can add the following files to the ci folder in your project repository:
Jenkinsfile-before-allJenkinsfile-before-cloud-buildJenkinsfile-before-cloud-deployJenkinsfile-after-allJenkinsfile-post-always
Here is a basic overview of the steps in the CI build process:
-
Load
ci/Jenkinsfile-before-all, if it exists. -
Build the Liferay Workspace.
-
Load
ci/Jenkinsfile-before-cloud-build, if it exists. -
Create the Liferay Cloud build that you see in console.
-
Load
ci/Jenkinsfile-before-cloud-deploy, if it exists. -
If the current branch is the deploy branch, you can deploy the build to an environment in the cloud. The
LCP_CI_DEPLOY_BRANCHenvironment variable sets the deploy branch, whileLCP_CI_DEPLOY_TARGETspecifies the deployment environment. -
Load
ci/Jenkinsfile-after-all, if it exists. This runs when all build steps are completed. -
Load
ci/Jenkinsfile-post-always, if it exists. This runs whether the build succeeds or fails.
To see how they are used in the default pipeline, monitor the Jenkins service startup logs. The full default Jenkinsfile is printed out in the startup logs.
Extra Pipeline Customization and External Calls
You can use the additional steps in your pipeline to call external services. For example, you may call a third-party monitoring service through REST API, or call a script to run during the build process.
You can also create your own pipeline by defining your own Jenkinsfile in your repository’s ci/ folder. See the Jenkins website for more information.
External services or custom pipelines should be used with discretion and are outside the scope of Liferay Cloud Support. Custom Jenkins plugins are not supported.
Reusing Code Between Different Extension Points
Sharing code between these extension points can help simplify their structure. One way is to load a groovy script.
For example, you could create a groovy file in the ci/ folder called util.groovy with these contents:
def sendSlackMessage(message) {
println(message)
}
return this
Then you could insert the following in ci/Jenkinsfile-before-cloud-build:
def util = load("ci/util.groovy")
util.sendSlackMessage("About to create Liferay Cloud build...")
Restricting Access to Your Project’s Jenkins by IP
CI service version 6.7.0+
Restrict which IP addresses can reach your project’s Jenkins instance to reduce its exposure to the public internet. That exposure covers every part of Jenkins: the login page, job pages, build logs, and the JSON API. Enable the allowlist on your project’s CI service.
Once you enable the allowlist, requests from IPs outside the list get HTTP 403 Access denied instead of reaching the Jenkins UI or API.
Some IPs are always allowed and you never add them:
- In-cluster and loopback traffic
- Liferay support staff
- Load balancer health checks
- Your source control management (SCM) provider’s webhook source IPs
Add only your own organization’s IPs. Builds still trigger normally, because your SCM provider’s webhook IPs are allowed automatically.
Enabling the Allowlist
The allowlist governs access to Jenkins, including its login page. If it omits the public IP address you browse from, you cannot reach Jenkins from that network. Confirm your own address is in the list before you save.
-
Set the
LCP_CI_ALLOWED_IPSenvironment variable on the CI service, in the console or the CI service’sLCP.jsonfile, to a comma-separated list of CIDR ranges (IP address blocks like203.0.113.0/24).A single IP address uses a
/32range:LCP_CI_ALLOWED_IPS=203.0.113.42/32Separate multiple ranges with commas. Both IPv4 and IPv6 CIDRs are supported; whitespace around commas and empty entries are ignored:
LCP_CI_ALLOWED_IPS=203.0.113.0/24, 198.51.100.42/32, 2001:db8::/48
-
Save the change. If you set the variable in
LCP.json, commit and deploy it instead.The CI service restarts and may stop receiving requests for some minutes. Check the service’s status on the environment’s Services page; the allowlist takes effect once the service is back up.
-
Open your Jenkins URL from an allowlisted network and confirm the login page loads.
From a network that is not on the list, the same URL returns
HTTP 403 Access denied.
To disable the allowlist, remove LCP_CI_ALLOWED_IPS or set it to an empty string, then save. The CI service restarts again, and access is restored once it is back up.
What to Allowlist
Allowlist IPs that sit inside your organization’s security boundary: networks and systems that you (or a trusted security team) control, audit, and patch.
Ask your network team for the public IP addresses your office networks, VPN, and build systems use to reach the internet. To check the address you are browsing from, visit any “what is my IP” service from that network. If your provider assigns a dynamic address, ask for a static address or range before relying on it, and revisit the value whenever your network changes.
Add IPs from these categories:
- CI/CD systems and bastion hosts you operate
- Corporate office egress IPs
- Corporate VPN exit IPs
- Security operations tooling
Avoid these IP types, which weaken the protection the allowlist provides:
0.0.0.0/0or::/0(these match the entire internet and turn off the protection)- Broad public cloud provider ranges
- Individual developers’ home IPs
Tuning the SCM IP Refresh Frequency
When your SCM provider publishes new webhook IP ranges, the CI service honors them at its next refresh, so builds triggered from a new range can fail to start until then. Set the LCP_CI_ALLOWLIST_REFRESH_HOURS environment variable to a number from 1 to 24 to control how often, in hours, that refresh runs. The default is 24 hours; values outside that range, and non-numeric values, are ignored and the interval falls back to the default.
Environment Variables Reference
Set these environment variables on the CI service, in the console or the service’s LCP.json file.
| Name | Default Value | Description |
|---|---|---|
LCP_CI_ALLOWED_IPS | Comma-separated CIDR ranges allowed to reach Jenkins. Setting a value restricts access by IP. | |
LCP_CI_ALLOWLIST_REFRESH_HOURS | 24 | How often, in hours (1 to 24), the CI service refreshes SCM provider IPs for the allowlist. |
LCP_CI_ARTIFACT_DAYS_TO_KEEP | -1 | The number of days artifacts are stored |
LCP_CI_ARTIFACT_NUM_TO_KEEP | 1 | Set the number of recent builds for which artifacts and stashes are preserved. |
LCP_CI_BUILD_DAYS_TO_KEEP | 14 | The number of days builds are stored |
LCP_CI_BUILD_NUM_TO_KEEP | 10 | The number of builds stored |
LCP_CI_BUILD_TIMEOUT_MINUTES | 30 | A time limit for the Pipeline run, after which Jenkins aborts the pipeline. |
LCP_CI_CLI_LOG_LEVEL | If set to verbose, the CI service uses the --verbose flag when performing lcp commands. This provides more information for debugging when the commands are run. | |
LCP_CI_DEPLOY_BRANCH | develop | Specify the branch to use for automatic deployment. If not set to a valid branch name, automatic deployment is disabled. |
LCP_CI_DEPLOY_TARGET | dev | Sets the environment where automatic deployment deploys. Only used if LCP_CI_DEPLOY_BRANCH is set. |
LCP_CI_GRADLE_JDK | jdk8 | Set the CI service’s Java version used for builds. |
LCP_CI_LIFERAY_DXP_HOTFIXES_[ENV] | The name of a hotfix (without the .zip extension) for CI to apply automatically when deploying the Liferay service. Replace [ENV] with the environment name (in all-caps), or COMMON. | |
LCP_CI_NUM_EXECUTORS | 2 | The number of executors used in the build executor queue. Reducing this to 1 restricts builds from executing in parallel, which may reduce build failures in some instances. |
LCP_CI_PRESERVE_STASHES_BUILD_COUNT | 20 | Set the number of recent builds for which stashes are preserved. Stashes cannot be preserved for more builds than allowed by the LCP_CI_ARTIFACT_NUM_TO_KEEP variable. |
LCP_CI_SCM_MANAGE_HOOKS | true | Enables or disables automatic web hook management for code hosting platforms (such as GitHub). |
LCP_CI_SCM_PROVIDER | github | Sets which source control management service is used for retrieving builds. Accepted values are bitbucket, github, and gitlab. |
LCP_CI_SCM_REPOSITORY_NAME | Sets the repository name used to retrieve builds (from GitHub, Bitbucket, or GitLab). | |
LCP_CI_SCM_REPOSITORY_OWNER | The repository owner used to retrieve builds. | |
LCP_CI_SCM_TOKEN | The access token used to retrieve builds. For Bitbucket, use an Atlassian API token. For GitHub or GitLab, use a personal access token. | |
LCP_CI_SCM_USERNAME | The Atlassian account email associated with LCP_CI_SCM_TOKEN (required for Bitbucket). | |
LCP_CI_USE_DEFAULT_JENKINSFILE | true | Enables or disables the Default Jenkinsfile. |
LCP_DATABASE_SERVICE | The host name of the Database service. |