I am a bit shocked Linux does not support building .NET MAUI apps unless they are
android .NET SDK Workload for building Android applications.
macos .NET SDK Workload for building macOS applications.
maui-android .NET MAUI SDK for Android
maui-tizen .NET MAUI SDK for Tizen
maui-windows .NET MAUI SDK for Windows
runtimes-windows Windows Runtime Packs
runtimes-windows-net6 Windows Runtime Packs
wasm-experimental .NET WebAssembly experimental tooling
wasm-tools .NET WebAssembly build tools
wasm-tools-net6 .NET WebAssembly build tools for net6.0
In any case, I'd like to know how do .NET Maui developers automate CI/CD in GitLab.
Is there any built-in mechanism like there is in GitHub or Azure Pipelines? Or does it have to be configured manually? Could I see an example?
This is what I've been trying so far, considering that the base image mcr.microsoft.com/dotnet/sdk:7.0 won't be able to be used for certain platforms unfortunately..
I have created a custom Docker image. Let's call it dotnet-maui
FROM mcr.microsoft.com/dotnet/sdk:7.0
# Install workload maui
RUN dotnet workload search
RUN dotnet workload install android maui-android --ignore-failed-sources
Is there any better alternative? Maybe an image that uses MacOs or windows where MAUI is supported?
And now I use that image as the GitLab base image for my MAUI apps. Specifically for one that targets android only, but I am still unable to even compile..
dotnet build -f:net7.0-android -c:Release
throws an error
dotnet build -f:net7.0-android -c:Release
MSBuild version 17.4.0+18d5aef85 for .NET
Determining projects to restore...
Restored /builds/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj (in 16.86 sec).
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(70,5): error XA5300: The Android SDK directory could not be found. Check that the Android SDK Manager in Visual Studio shows a valid installation. To use a custom SDK path for a command line build, set the 'AndroidSdkDirectory' MSBuild property to the custom path. [/builds/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
Build FAILED.
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(70,5): error XA5300: The Android SDK directory could not be found. Check that the Android SDK Manager in Visual Studio shows a valid installation. To use a custom SDK path for a command line build, set the 'AndroidSdkDirectory' MSBuild property to the custom path. [/builds/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.58
Cleaning up project directory and file based variables 00:01
ERROR: Job failed: exit code 1
How should I install and configure this Android SDK?
PS: The fact that the errors show details about Visual Studio (which is a graphical user interface IDE) is a bit concerning.
UPDATE 1 (2023-01-02)
I've attempted to build in Ubuntu 22.04 with CLI and I am having problems. The goal is to have all this automated in a Docker image.
First I install maui-android
dotnet workload install maui-android --ignore-failed-sources
Then I install Android SDK. 2 Options:
sudo apt update && sudo apt install -y android-sdk
or
sudo snap install androidsdk
Either way, the following attempt to build, fails
dotnet build -f net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk
with error about API level 33 not installed
MSBuild version 17.4.0+18d5aef85 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(100,5): error XA5207: Could not find android.jar for API level 33. This means the Android SDK platform for API level 33 is not installed. Either install it in the Android SDK Manager (Tools > Open Android SDK Manager...), or change the Xamarin.Android project to target an API version that is installed. (/usr/lib/android-sdk/platforms/android-33/android.jar missing.) [/media/diegosasw/data/src/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
I have attempted to explicitly install API 33
yes | androidsdk "platform-tools" "platforms;android-33"
which creates a ~/AndroidSDK/platforms/android-33 with a jar inside
But
dotnet build -f net7.0-android /p:AndroidSdkDirectory=/home/diegosasw/AndroidSDK/platforms/android-33
also fails
MSBuild version 17.4.0+18d5aef85 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
/usr/share/dotnet/packs/Microsoft.Android.Sdk.Linux/33.0.4/tools/Xamarin.Android.Tooling.targets(70,5): error XA5300: The Android SDK directory could not be found. Check that the Android SDK Manager in Visual Studio shows a valid installation. To use a custom SDK path for a command line build, set the 'AndroidSdkDirectory' MSBuild property to the custom path. [/media/diegosasw/data/src/roundev/roundev-easy-qr/src/EasyQr/EasyQr.csproj::TargetFramework=net7.0-android]
I would appreciate a working sample that I could use both locally in Linux Ubuntu, and in GitLab CI/CD
UPDATE 2 (2023-01-02)
I got something working locally, but still unable to find a good solution for GitLab CI/CD (too large base image is causing problems)
I have the following *.csproj
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFrameworks>net7.0-android</TargetFrameworks>
<OutputType>Exe</OutputType>
<RootNamespace>MySample</RootNamespace>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<ImplicitUsings>enable</ImplicitUsings>
<EnableDefaultCssItems>false</EnableDefaultCssItems>
<!-- Display name -->
<ApplicationTitle>MySample</ApplicationTitle>
<!-- App Identifier -->
<ApplicationId>com.roundev.mysample</ApplicationId>
<ApplicationIdGuid>840AB3E3-0460-46FE-BDEA-94EEBB053DE2</ApplicationIdGuid>
<!-- Versions -->
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<ApplicationVersion>1</ApplicationVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">24.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.Contains('-android')) and '$(Configuration)' == 'Release'">
<AndroidKeyStore>True</AndroidKeyStore>
<AndroidSigningKeyStore>myapp.keystore</AndroidSigningKeyStore>
<AndroidSigningKeyAlias>myalias</AndroidSigningKeyAlias>
<AndroidSigningKeyPass></AndroidSigningKeyPass>
<AndroidSigningStorePass></AndroidSigningStorePass>
</PropertyGroup>
<ItemGroup>
<!-- App Icon -->
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
<!-- Splash Screen -->
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
<!-- Images -->
<MauiImage Include="Resources\Images\*" />
<MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
<!-- Custom Fonts -->
<MauiFont Include="Resources\Fonts\*" />
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
</ItemGroup>
</Project>
At localhost (Ubuntu 22.04)
At localhost, I managed to compile and publish signed APK/AAB with following steps:
Assuming SDK .NET 7 is already installed.
Install workload maui-android on the top of SDK .NET
dotnet workload install maui-android --ignore-failed-sources
Installed Android SDK (Api level 33) through the Jetbrains Toolbox by installing Android Studio, with Android SDK build-tools also. Everything is under /home/diegosasw/Android/Sdk
Created keystore
keytool -genkey -v -keystore myapp.keystore -alias myalias -keyalg RSA -keysize 2048 -validity 36500
and entered password and details. The generated myapp.keystore, I place it at the same level as the *.csproj (Ideally it should not be versioned)
Compile
dotnet build -f net7.0-android /p:AndroidSdkDirectory=/home/diegosasw/Android/Sdk
Publish
dotnet publish -f:net7.0-android -c:Release -o dist /p:AndroidSigningKeyPass=<the_password> /p:AndroidSigningStorePass=<the_password> /p:AndroidSdkDirectory=/home/diegosasw/Android/Sdk
and the dist folder will contain the AAB, signed AAB and signed APK.
The process for CI/CD automation would have to be similar, but with automated Android SDK installation, plus other tools,
At GitLab CI/CD
Firstly I create an image with the following Dockerfile. It's very heavy, and possibly it could be parametized, and greatly improved.
ARG REPO=mcr.microsoft.com/dotnet/aspnet
FROM $REPO:7.0.1-jammy-amd64 AS platform
ENV \
# Unset ASPNETCORE_URLS from aspnet base image
ASPNETCORE_URLS= \
# Do not generate certificate
DOTNET_GENERATE_ASPNET_CERTIFICATE=false \
# Do not show first run text
DOTNET_NOLOGO=true \
# SDK version
DOTNET_SDK_VERSION=7.0.101 \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful within an image/container - helps performance
NUGET_XMLDOC_MODE=skip \
# PowerShell telemetry for docker image usage
POWERSHELL_DISTRIBUTION_CHANNEL=PSDocker-DotnetSDK-Ubuntu-22.04
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
git \
wget \
&& rm -rf /var/lib/apt/lists/*
# Install .NET SDK
RUN curl -fSL --output dotnet.tar.gz https://dotnetcli.azureedge.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz \
&& dotnet_sha512='cf289ad0e661c38dcda7f415b3078a224e8347528448429d62c0f354ee951f4e7bef9cceaf3db02fb52b5dd7be987b7a4327ca33fb9239b667dc1c41c678095c' \
&& echo "$dotnet_sha512 dotnet.tar.gz" | sha512sum -c - \
&& mkdir -p /usr/share/dotnet \
&& tar -oxzf dotnet.tar.gz -C /usr/share/dotnet ./packs ./sdk ./sdk-manifests ./templates ./LICENSE.txt ./ThirdPartyNotices.txt \
&& rm dotnet.tar.gz \
# Trigger first run experience by running arbitrary cmd
&& dotnet help
# Install PowerShell global tool
RUN powershell_version=7.3.0 \
&& curl -fSL --output PowerShell.Linux.x64.$powershell_version.nupkg https://pwshtool.blob.core.windows.net/tool/$powershell_version/PowerShell.Linux.x64.$powershell_version.nupkg \
&& powershell_sha512='c4a72142e2bfae0c2a64a662f1baa27940f1db8a09384c90843163e339581d8d41824145fb9f79c680f9b7906043365e870d48d751ab8809c15a590f47562ae6' \
&& echo "$powershell_sha512 PowerShell.Linux.x64.$powershell_version.nupkg" | sha512sum -c - \
&& mkdir -p /usr/share/powershell \
&& dotnet tool install --add-source / --tool-path /usr/share/powershell --version $powershell_version PowerShell.Linux.x64 \
&& dotnet nuget locals all --clear \
&& rm PowerShell.Linux.x64.$powershell_version.nupkg \
&& ln -s /usr/share/powershell/pwsh /usr/bin/pwsh \
&& chmod 755 /usr/share/powershell/pwsh \
# To reduce image size, remove the copy nupkg that nuget keeps.
&& find /usr/share/powershell -print | grep -i '.*[.]nupkg$' | xargs rm
# JAVA
RUN apt-get update && \
apt-get install -y openjdk-11-jdk && \
rm -rf /var/lib/apt/lists/*
ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/
# Install workload maui
RUN dotnet workload install maui-android --ignore-failed-sources
# Utils
RUN apt-get update && apt-get install -y \
unzip \
jq \
bzip2 \
libzip4 \
libzip-dev && \
rm -rf /var/lib/apt/lists/*
# Install Android SDK
RUN mkdir -p /usr/lib/android-sdk/cmdline-tools/latest && \
curl -k "https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip" -o commandlinetools-linux.zip && \
unzip -q commandlinetools-linux.zip -d /usr/lib/android-sdk/tmp && \
mv /usr/lib/android-sdk/tmp/cmdline-tools/* /usr/lib/android-sdk/cmdline-tools/latest && \
rm -rf /usr/lib/android-sdk/tmp/ && \
rm commandlinetools-linux.zip
ENV ANDROID_SDK_ROOT=/usr/lib/android-sdk
ENV PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$PATH
RUN yes | sdkmanager --licenses && \
sdkmanager "platform-tools" && \
sdkmanager "ndk-bundle" && \
sdkmanager "build-tools;33.0.0" "platforms;android-33"
At the repo in GitLab, I create a secure file and upload the myapp.keystore (and I remove it from the filesystem).
At the repo in GitLab, I create a CI/CD environment variable KEYSTORE_PASSWORD with the password value
The idea is to have a .gitlab-ci.yml similar to this
image: registry.gitlab.com/your_image_generated_out_of_Dockerfile:latest
variables:
PUBLISH_OUTPUT_DIR: dist
ANDROID_SIGNING_KEY_PASS: $KEYSTORE_PASSWORD
ANDROID_SIGNING_STORE_PASS: $KEYSTORE_PASSWORD
stages:
- build
- publish
- delivery
build:
stage: build
script:
- dotnet build -f net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk
artifacts:
paths:
- test
expire_in: 8 hour
rules:
- if: '$CI_COMMIT_TAG == null'
publish_android:
stage: publish
variables:
SECURE_FILES_DOWNLOAD_PATH: './'
before_script:
- APP_VERSION=$(cat ./version/semver)
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
script:
- dotnet publish
-f:net7.0-android
-c:Release
-o $PUBLISH_OUTPUT_DIR
/p:AndroidSigningKeyPass=$ANDROID_SIGNING_KEY_PASS
/p:AndroidSigningStorePass=$ANDROID_SIGNING_STORE_PASS
/p:AndroidSdkDirectory=/usr/lib/android-sdk
-p:Version=$APP_VERSION
allow_failure: false
artifacts:
paths:
- $PUBLISH_OUTPUT_DIR/
expire_in: 8 hour
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: on_success
- if: '$CI_COMMIT_TAG == null'
but unfortunately I cannot even generate the Docker image to use as base image because I run out of space due to the huge image size I'm attempting to build.
The question remains:
Is there any easy/good way (working sample) to use GitLab CI/CD to build and publish AAB/APK with .NET 7 Maui targeting Android?
Is there any official Docker image which I could directly use at GitLab CI for my dotnet build and dotnet publish of .NET 7 Maui projects (image with .NET 7 SDK, Android SDK, maui-android workload, JDK and other required tooling)?
I got it working and with a smaller custom image built on the top of the "official" Microsoft's SDK .NET 7, without adding Android NDK (which I don't think it was needed).
This is my docker image
FROM mcr.microsoft.com/dotnet/sdk:7.0
MAINTAINER roundev
# JAVA
RUN apt-get update && \
apt-get install -y openjdk-11-jdk && \
rm -rf /var/lib/apt/lists/*
ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64/
# Install workload maui
RUN dotnet workload install maui-android --ignore-failed-sources
# Utils
RUN apt-get update && apt-get install -y \
unzip && \
rm -rf /var/lib/apt/lists/*
# Install Android SDK
RUN mkdir -p /usr/lib/android-sdk/cmdline-tools/latest && \
curl -k "https://dl.google.com/android/repository/commandlinetools-linux-9123335_latest.zip" -o commandlinetools-linux.zip && \
unzip -q commandlinetools-linux.zip -d /usr/lib/android-sdk/tmp && \
mv /usr/lib/android-sdk/tmp/cmdline-tools/* /usr/lib/android-sdk/cmdline-tools/latest && \
rm -rf /usr/lib/android-sdk/tmp/ && \
rm commandlinetools-linux.zip
ENV ANDROID_SDK_ROOT=/usr/lib/android-sdk
ENV PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$PATH
RUN yes | sdkmanager --licenses && \
sdkmanager "platform-tools" && \
#sdkmanager "ndk-bundle" && \ # I Don't think this was needed at all
sdkmanager "build-tools;33.0.0" "platforms;android-33"
This, generated and stored in GitLab container registry, takes "only" 2.5GB.
So now the pipeline is successful with this .gitlab-ci.yml
image: registry.gitlab.com/roundev/devops/dotnet-maui:latest
variables:
PUBLISH_OUTPUT_DIR: dist
ANDROID_SIGNING_KEY_PASS: $KEYSTORE_PASSWORD
ANDROID_SIGNING_STORE_PASS: $KEYSTORE_PASSWORD
stages:
- build
- publish
- delivery
build:
stage: build
script:
- dotnet build -f net7.0-android /p:AndroidSdkDirectory=/usr/lib/android-sdk
rules:
- if: '$CI_COMMIT_TAG == null'
publish_android:
stage: publish
variables:
SECURE_FILES_DOWNLOAD_PATH: './'
before_script:
- curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
script:
- dotnet publish
-f:net7.0-android
-c:Release
-o $PUBLISH_OUTPUT_DIR
/p:AndroidSigningKeyPass=$ANDROID_SIGNING_KEY_PASS
/p:AndroidSigningStorePass=$ANDROID_SIGNING_STORE_PASS
/p:AndroidSdkDirectory=/usr/lib/android-sdk
allow_failure: false
artifacts:
paths:
- $PUBLISH_OUTPUT_DIR/
expire_in: 8 hour
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: on_success
- if: '$CI_COMMIT_TAG == null'
Notice it is using a secure file where I upload my myapp.keystore
and a CI/CD variable KEYSTORE_PASSWORD with the signing key pass and store pass (it's the same in my case).
Related
I am creating a custom Builder Image using S2i dotnet core. This will run in OpenShift linux container
I have modified the custom builder image and included few lines to copy few dlls and ".so" files
When running the container in OpenShift I am facing the below error
error says
"unable to load shared library 'CustomCppWrapper' or one of its dependencies. In order to help diagnose loading problems,
consider setting the LD_DEBUG environment variable: libWrapperName: cannot open shared object file: No such file or directory"
I have set the LD_DEBUG environment variable and found below few errors
/lib64/libstdc++.so.6: error: version lookup error: version `CXXABI_1.3.8' not found (required by /opt/app-root/app/libCWrappeNamer.so) (fatal)
/lib64/libstdc++.so.6: error: version lookup error: version `CXXABI_1.3.8' not found (required by ./libCWrappeNamer.so) (fatal)
I did below command and found below
ldd libCWrappeNamer.so
./libCWrappeNamer.so: /lib64/libstdc++.so.6: version `CXXABI_1.3.8' not found (required by ./libCWrappeNamer.so)
./libCWrappeNamer.so: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /ab/sdk/customlib/gcc540/lib/libabc.so)
./libCWrappeNamer.so: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /ab/sdk/customlib/gcc540/lib/libxmlc.so)
Below is my Custom Docker file builder image
FROM dotnet/dotnet-31-runtime-rhel7
# This image provides a .NET Core 3.1 environment you can use to run your .NET
# applications.
ENV PATH=/opt/app-root/src/.local/bin:/opt/app-root/src/bin:/opt/app-root/node_modules/.bin:${PATH} \
STI_SCRIPTS_PATH=/usr/libexec/s2i
LABEL io.k8s.description="Platform for building and running .NET Core 3.1 applications" \
io.openshift.tags="builder,.net,dotnet,dotnetcore,rh-dotnet31"
# Labels consumed by Red Hat build service
LABEL name="dotnet/dotnet-31-rhel7" \
com.redhat.component="rh-dotnet31-container" \
version="3.1" \
release="1" \
architecture="x86_64"
#-------------------------- COPY CPP LIBS
COPY CustomCppWrapper.lib /opt/app-root/app
COPY libCWrappeNamer.so /opt/app-root/app
#----------------------------------
# Labels consumed by Eclipse JBoss OpenShift plugin
LABEL com.redhat.dev-mode="DEV_MODE:false" \
com.redhat.deployments-dir="/opt/app-root/src"
# Switch to root for package installs
USER 0
# Copy the S2I scripts from the specific language image to $STI_SCRIPTS_PATH.
COPY ./s2i/bin/ /usr/libexec/s2i
RUN INSTALL_PKGS="rh-nodejs10-npm rh-nodejs10-nodejs-nodemon rh-dotnet31-dotnet-sdk-3.1 rsync" && \
yum install -y --setopt=tsflags=nodocs --disablerepo=\* \
--enablerepo=rhel-7-server-rpms,rhel-server-rhscl-7-rpms,rhel-7-server-dotnet-rpms \
$INSTALL_PKGS && \
rpm -V $INSTALL_PKGS && \
yum clean all -y && \
# yum cache files may still exist (and quite large in size)
rm -rf /var/cache/yum/*
# Directory with the sources is set as the working directory.
RUN mkdir /opt/app-root/src
WORKDIR /opt/app-root/src
# Trigger first time actions.
RUN scl enable rh-dotnet31 'dotnet help'
# Build the container tool.
RUN /usr/libexec/s2i/container-tool build-tool
# Since $HOME is set to /opt/app-root, the yum install may have created config
# directories (such as ~/.pki/nssdb) there. These will be owned by root and can
# cause actions that work on all of /opt/app-root to fail. So we need to fix
# the permissions on those too.
RUN chown -R 1001:0 /opt/app-root && fix-permissions /opt/app-root
ENV ENABLED_COLLECTIONS="$ENABLED_COLLECTIONS rh-nodejs10" \
# Needed for the `dotnet watch` to detect changes in a container.
DOTNET_USE_POLLING_FILE_WATCHER=true
# Run container by default as user with id 1001 (default)
USER 1001
# Set the default CMD to print the usage of the language image.
CMD /usr/libexec/s2i/usage
Your code depends on libstdc++.so.6 but it would seem that version isn't installed
In your Dockerfile, add the yum install command that should do it. It would depend on what operating system you're using, but for RHEL 7, for example, you could do:
RUN yum install -y libstdc++
With more details of the operating system I can give a more specific command
In this specific examples the Dockerfile could look something like this:
FROM centos:7
RUN yum install -y libstdc++
CMD ["/bin/bash"]
I'm trying to deploy a simple single endpoint quarkus app to Google Cloud -> App Engine flex environment. I managed to deploy an uber-jar to standard environment, according to documentation
But I'm struggling to deploy with flex environment, as my understating, from the same documentation link as above, is that GCP will create a docker container based on the Dockerfile; In the end, my intention is to deploy a native image to GCP App Engine.
I followed the steps in the link above:
Copy the JVM Dockerfile to the root directory of your project: cp src/main/docker/Dockerfile.jvm Dockerfile
Build your application using mvn clean package
src/main/appengine/app.yaml has the following:
runtime: custom
env: flex
gcloud app deploy
The gcloud log returns the same error, both for native and normal docker file, respectively:
starting build "502dd964-0abf-470e-a4c1-44c89a67a96e"
FETCHSOURCE
Fetching storage object: gs://staging.os-xxxx-quarkus.appspot.com/eu.gcr.io/os-xxxx-quarkus/appengine/default.20210322t183731:latest#1616431057582767
Copying gs://staging.os-xxxx-quarkus.appspot.com/eu.gcr.io/os-xxxx-quarkus/appengine/default.20210322t183731:latest#1616431057582767...
/ [0 files][ 0.0 B/ 230.0 B]
/ [1 files][ 230.0 B/ 230.0 B]
Operation completed over 1 objects/230.0 B.
BUILD
Already have image (with digest): gcr.io/cloud-builders/docker
unable to prepare context: unable to evaluate symlinks in Dockerfile path: lstat /Users: no such file or directory
ERROR
ERROR: build step 0 "gcr.io/cloud-builders/docker" failed: step exited with non-zero status: 1
For brevity, I will include the dockerfile, but it is the default generated without any changes.
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.3
ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
&& microdnf update \
&& microdnf clean all \
&& mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments \
&& curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
&& chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/
COPY --chown=1001 target/quarkus-app/*.jar /deployments/
COPY --chown=1001 target/quarkus-app/app/ /deployments/app/
COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080
USER 1001
ENTRYPOINT [ "/deployments/run-java.sh" ]
Thank you for your time
It seems i polluted the space with an embarrassing mistake; for Google Cloud App Engine ,flex environment, the app.yaml file should be placed in the root folder; it is also documented
Moreover, for some reason, the max_num_instances should be explicitly set to a value of max 8, according to account quota google documentation otherwise a EXAHUSTED_RESOURCE exception is thrown.
I built a simple language server following this tutorial:
http://www.eclipse.org/Xtext/documentation/102_domainmodelwalkthrough.html
In this server, I have 5 folders,
org.example.domainmodel
org.example.domainmodel.ide
org.example.domainmodel.tests
org.example.domainmodel.ui
org.example.domainmodel.ui.tests
Now I need to run this language server inside a Docker image, and need the binary directory for it. Which binary directory out of the above 5 folders should I use? The actual language servers I've explored so far are quite different from what I've created.
The above project that I had created is used to develop Eclipse plugins. To create a LS to run in a Dockerfile, it is required to create a binary file (I used fatJar) from the Eclipse Xtext project that should be created as explained in this tutorial (Should only complete up to 'Packaging the LS', inclusive). Note that when packaging the LS, it is enough to add the following code snippet in the *.ide project's build.gradle file : -
shadowJar {
baseName = 'dsl-language-server'
classifier = null
version = null
}
Afterwards, the parent project should be built by gradle shadowJar command in the command prompt. Once it is done, a .jar file will be created in the following path: *.ide/build/libs
Then you'll need to create a separate directory and copy and paste the build folder into it, and create a file named dockerfile (no file extension) and include the following code in it: -
FROM barais/eclipse-xtend
ADD build/libs/dsl-language-server-ls.jar dsl-language-server-ls.jar
RUN sudo apt-get install socat
RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y software-properties-common && \
add-apt-repository ppa:webupd8team/java -y && \
apt-get update && \
echo oracle-java7-installer shared/accepted-oracle-license-v1-1 select true | /usr/bin/debconf-set-selections && \
apt-get install -y oracle-java8-installer && \
apt-get clean
CMD socat TCP4-LISTEN:4417,reuseaddr,fork EXEC:"java -jar dsl-language-server-ls.jar"
Then build this file using Docker, and push it to your Docker Hub account as a repo. Use the following recipe to create a new stack in Eclipse Che to use the pushed repo from Docker Hub as a language server: -
services:
dsl-language-server-ls:
image : <youraccountname>/mydsl
mem_limit : 1073741824
dev-machine:
image : eclipse/ubuntu_jdk8
mem_limit : 2147483648
depends_on:
- dsl-language-server-ls
Edit the raw config and update it with the following code at the respective location in the config (Read the che doc for more information: -
"id": "mydsl-ls",
"internal": "true",
"type": "ls",
"languageRegexes": "[ {\"languageId\":\"mydsllang\", \"regex\":\".*\\\\.mydsl$\"}]"
Create a file in a new project in Eclipse Che with the file extension you added (.mydsl) and the LS will start to run, allowing you to write code in your DSL.
Yay!
Trying to create a docker image that has sbt installed and can build sbt projects but, when building, will not be running as the root user (this is all in the context of running Jenkins inside docker).
Dockerfile sets up sbt
ENV SBT_VERSION=1.1.6
RUN \
curl -L -o sbt-$SBT_VERSION.deb http://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \
dpkg -i sbt-$SBT_VERSION.deb && \
rm sbt-$SBT_VERSION.deb && \
apt-get update && \
apt-get install sbt && \
sbt sbtVersion
And if I then run sbt as the root user, all works ok
docker exec -u root myjenkins sbt sbtVersion
produces
[warn] No sbt.version set in project/build.properties, base directory: /
[info] Set current project to root (in build file:/)
[info] 1.1.6
But when I run sbt as the jenkins user, it tries to download sbt 1.1.6 again and eventually fails when it tries to modify an apt system file.
docker exec -u jenkins myjenkins sbt sbtVersion
produces:
Getting org.scala-sbt sbt 1.1.6 (this may take some time)...
downloading https://repo1.maven.org/maven2/org/scala-sbt/sbt/1.1.6/sbt-1.1.6.jar ...
[SUCCESSFUL ] org.scala-sbt#sbt;1.1.6!sbt.jar (68ms)
.
.
.
[warn] No sbt.version set in project/build.properties, base directory: /
[error] java.io.FileNotFoundException: /var/cache/apt/archives/lock (Permission denied)
I understand that all of the "RUN" commands in your Dockerfile are as a root user.
SBT downloading Scala: Check where it is downloading. SBT by default downloads dependencies on ~/.ivy2 (and/or ~/.m2). If you change user, your home also changes, so it will look for dependencies in /home/jenkins/.ivy2, then on .ivy2 (double-check on this), which do not have those dependencies downloaded already, so it tries to download them.
About the var/cache/apt/archives/lock, it is trying to install via SBT via apt with your jenkins user, when you need to be privileged user to use apt. Your app-user should not need to install anything (or anything that requires root access), but rather build an image with all required installs and then use it as a separate user. Also, if apt gives you headaches, you can just install via download into folder, something like:
RUN \
curl -fsL http://downloads.typesafe.com/scala/$SCALA_VERSION/scala-$SCALA_VERSION.tgz | tar xfz - -C /usr/local && \
ln -s /usr/local/scala-$SCALA_VERSION/bin/* /usr/local/bin/
PS: You may want to run your container always as jenkins user, in that case you can use USER jenkins after you finished installations and do any additional unprivileged operations there.
I am using automated builds on Docker cloud to compile a C++ app and provide it in an image.
Compilation is quite long (range 2-3 hours) and commits on github are frequent (~10 to 30 per day).
Is there a way to keep the building cache (using ccache) somehow?
As far as I understand it, docker caching is useless since the compilation layer producing the ccache will not be used due to the source code changes.
Or can we tweak to bring some data back to first layer?
Any other solution? Pushing it somewhere?
Here is the Dockerfile:
# CACHE_TAG is provided by Docker cloud
# see https://docs.docker.com/docker-cloud/builds/advanced/
# using ARG in FROM requires min v17.05.0-ce
ARG CACHE_TAG=latest
FROM qgis/qgis3-build-deps:${CACHE_TAG}
MAINTAINER Denis Rouzaud <denis.rouzaud#gmail.com>
ENV CC=/usr/lib/ccache/clang
ENV CXX=/usr/lib/ccache/clang++
ENV QT_SELECT=5
COPY . /usr/src/QGIS
WORKDIR /usr/src/QGIS/build
RUN cmake \
-GNinja \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBINDINGS_GLOBAL_INSTALL=ON \
-DWITH_STAGED_PLUGINS=ON \
-DWITH_GRASS=ON \
-DSUPPRESS_QT_WARNINGS=ON \
-DENABLE_TESTS=OFF \
-DWITH_QSPATIALITE=ON \
-DWITH_QWTPOLAR=OFF \
-DWITH_APIDOC=OFF \
-DWITH_ASTYLE=OFF \
-DWITH_DESKTOP=ON \
-DWITH_BINDINGS=ON \
-DDISABLE_DEPRECATED=ON \
.. \
&& ninja install \
&& rm -rf /usr/src/QGIS
WORKDIR /
You should try saving and restoring your cache data from a third party service:
- an online object storage like Amazon S3
- a simple FTP server
- an Internet available machine with ssh to make a scp
I'm assuming that your cache data is stored inside the ´~/.ccache´ directory
Using Docker multistage build
From some time, Docker supports Multi-stage builds and you can try using it to implement the solution with a single Dockerfile:
Warning: I've not tested it
# STAGE 1 - YOUR ORIGINAL DOCKER FILE CUSTOMIZED
# CACHE_TAG is provided by Docker cloud
# see https://docs.docker.com/docker-cloud/builds/advanced/
# using ARG in FROM requires min v17.05.0-ce
ARG CACHE_TAG=latest
FROM qgis/qgis3-build-deps:${CACHE_TAG} as builder
MAINTAINER Denis Rouzaud <denis.rouzaud#gmail.com>
ENV CC=/usr/lib/ccache/clang
ENV CXX=/usr/lib/ccache/clang++
ENV QT_SELECT=5
COPY . /usr/src/QGIS
WORKDIR /usr/src/QGIS/build
# restore cache
RUN curl -o ccache.tar.bz2 http://my-object-storage/ccache.tar.bz2
RUN tar -xjvf ccache.tar.bz2
COPY --from=downloader /.ccache ~/.ccache
RUN cmake \
-GNinja \
-DCMAKE_INSTALL_PREFIX=/usr \
-DBINDINGS_GLOBAL_INSTALL=ON \
-DWITH_STAGED_PLUGINS=ON \
-DWITH_GRASS=ON \
-DSUPPRESS_QT_WARNINGS=ON \
-DENABLE_TESTS=OFF \
-DWITH_QSPATIALITE=ON \
-DWITH_QWTPOLAR=OFF \
-DWITH_APIDOC=OFF \
-DWITH_ASTYLE=OFF \
-DWITH_DESKTOP=ON \
-DWITH_BINDINGS=ON \
-DDISABLE_DEPRECATED=ON \
.. \
&& ninja install
# save the current cache online
WORKDIR ~/
RUN tar -cvjSf ccache.tar.bz2 .ccache
RUN curl -T ccache.tar.bz2 -X PUT http://my-object-storage/ccache.tar.bz2
# STAGE 2
FROM alpine:latest
# YOUR CUSTOM LOGIC TO CREATE THE FINAL IMAGE WITH ONLY REQUIRED BINARIES
# USE THE FROM IMAGE YOU NEED, this is only an example
# E.g.:
# COPY --from=builder /usr/src/QGIS/build/YOUR_EXECUTABLE /usr/bin
# ...
In the stage 2 you will build the final image that will be pushed to your repository.
Using Docker cloud hooks
Another, but less clear, approach could be using a Docker Cloud pre_build hook file to download cache data:
#!/bin/bash
echo "=> Downloading build cache data"
curl -o ccache.tar.bz2 http://my-object-storage/ccache.tar.bz2 # e.g. Amazon S3 like service
cd /
tar -xjvf ccache.tar.bz2
Obviously you can use dedicate docker images to run curl or tar mounting the local directory as a volume in this script.
Then, copy the .ccache extracted folder inside your container during the build, using a COPY command before your cmake call:
WORKDIR /usr/src/QGIS/build
COPY /.ccache ~/.ccache
RUN cmake ...
In order to make this you should find a way to upload your cache data after the build and you could make this easily using a post_build hook file:
#!/bin/bash
echo "=> Uploading build cache data"
tar -cvjSf ccache.tar.bz2 ~/.ccache
curl -T ccache.tar.bz2 -X PUT http://my-object-storage/ccache.tar.bz2
But your compilation data aren't available from the outside, because they live inside the container. So you should upload the cache after the cmake command inside your main Dockerfile:
RUN cmake...
&& tar ...
&& curl ...
&& ninja ...
&& rm ...
If curl or tar aren't available, just add them to your container using the package manager (qgis/qgis3-build-deps is based on Ubuntu 16.04, so they should be available).