diff --git a/.github/actions/docker-publish/action.yml b/.github/actions/docker-publish/action.yml deleted file mode 100644 index b577fdf01..000000000 --- a/.github/actions/docker-publish/action.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: 'Docker Publish' -description: 'Build and optionally publish a Docker image to a registry' - -inputs: - registry: - description: 'Docker registry (e.g., ghcr.io)' - required: true - namespace: - description: 'Image namespace (e.g., owner/repo)' - required: true - image_name: - description: 'Image name (e.g., linux-amd64)' - required: true - context: - description: 'Build context path (e.g., ./skiko/docker/linux-amd64)' - required: true - platforms: - description: 'Target platforms (e.g., linux/amd64 or linux/amd64,linux/arm64)' - required: true - tag: - description: 'Image tag (e.g., ubuntu-2004)' - required: true - should_publish: - description: 'Whether to push the image to the registry' - required: true - github_token: - description: 'GitHub token for authentication' - required: true - -runs: - using: 'composite' - steps: - - name: 'Set up Docker Buildx' - uses: docker/setup-buildx-action@v3 - - - name: 'Log into registry' - if: inputs.should_publish == 'true' - uses: docker/login-action@v3 - with: - registry: ${{ inputs.registry }} - username: ${{ github.actor }} - password: ${{ inputs.github_token }} - - - name: 'Extract metadata' - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ inputs.registry }}/${{ inputs.namespace }}/${{ inputs.image_name }} - tags: | - type=raw,value=${{ inputs.tag }} - - - name: 'Build and push' - uses: docker/build-push-action@v5 - with: - context: ${{ inputs.context }} - platforms: ${{ inputs.platforms }} - push: ${{ inputs.should_publish == 'true' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/actions/docker-skiko-publish/action.yml b/.github/actions/docker-skiko-publish/action.yml new file mode 100644 index 000000000..3ce4d3f18 --- /dev/null +++ b/.github/actions/docker-skiko-publish/action.yml @@ -0,0 +1,68 @@ +name: 'Docker Skiko Publish' +description: 'Build and optionally publish a Docker image to ghcr.io for skiko' + +inputs: + image_name: + description: 'Image name (e.g., linux-compat)' + required: true + platforms: + description: 'Target platforms (e.g., linux/amd64 or linux/amd64,linux/arm64). If not specified, uses runner architecture.' + required: false + tag: + description: 'Image tag (e.g., latest)' + required: true + load: + description: 'Whether to load the image into the local Docker daemon' + required: false + default: 'false' + should_publish: + description: 'Whether to push the image to the registry' + required: true + github_token: + description: 'GitHub token for authentication (required only if should_publish is true)' + required: false + +runs: + using: 'composite' + steps: + - name: 'Set Variables' + id: vars + shell: bash + run: | + echo "image_namespace=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT + + # Normalize tag: replace invalid docker tag characters + TAG="${{ inputs.tag }}" + TAG="${TAG//\//-}" + echo "tag=${TAG}" >> $GITHUB_OUTPUT + + - name: 'Set up Docker Buildx' + uses: docker/setup-buildx-action@v3 + + - name: 'Log into registry' + if: inputs.should_publish == 'true' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ inputs.github_token }} + + - name: 'Extract metadata' + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ steps.vars.outputs.image_namespace }}/${{ inputs.image_name }} + tags: | + type=raw,value=${{ steps.vars.outputs.tag }} + + - name: 'Build and push' + uses: docker/build-push-action@v5 + with: + context: ./skiko/docker/${{ inputs.image_name }} + platforms: ${{ inputs.platforms }} + push: ${{ inputs.should_publish == 'true' }} + load: ${{ inputs.load == 'true' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/actions/docker-skiko-run/action.yml b/.github/actions/docker-skiko-run/action.yml new file mode 100644 index 000000000..10e367699 --- /dev/null +++ b/.github/actions/docker-skiko-run/action.yml @@ -0,0 +1,96 @@ +name: 'Docker Skiko Run' +description: 'Build Docker image locally if needed and run commands inside it' + +inputs: + image_name: + description: 'Image name (e.g., linux-compat)' + required: true + command: + description: 'Command to run inside the container' + required: true + working_directory: + description: 'Working directory relative to project root (e.g., samples/SkiaAndroidSample)' + required: false + default: '.' + virtual-display: + description: 'Enable virtual display for UI testing (enables e2e UI interaction tests with real windows)' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: 'Detect Docker changes' + id: filter + uses: dorny/paths-filter@v3 + with: + base: ${{ github.event.pull_request.base.sha || github.event.before || 'master' }} + filters: | + docker_changed: + - '.github/actions/docker-skiko-publish/**' + - '.github/workflows/docker-publish.yml' + - 'skiko/docker/${{ inputs.image_name }}/**' + + - name: 'Set Variables' + id: vars + shell: bash + run: | + IMAGE_NAMESPACE="${GITHUB_REPOSITORY,,}" + echo "image_namespace=${IMAGE_NAMESPACE}" >> $GITHUB_OUTPUT + + # Determine which tag to use + # github.base_ref is set for PRs regardless of event type (pull_request or workflow_call) + if [[ -n "${{ github.base_ref }}" && "${{ steps.filter.outputs.docker_changed }}" != "true" ]]; then + # For PRs without docker changes, use the base (target) branch name as the docker tag. + # Most of the time it points to an already built-image. + TAG="${{ github.base_ref }}" + else + # For PRs with docker changes or non-PRs, use the current branch name as the docker tag + TAG="${GITHUB_REF_NAME}" + fi + TAG="${TAG//\//-}" + + # Try to pull image from GHCR + # This checks both if the image exists in the registry and loads it into the local daemon + if docker pull "ghcr.io/${IMAGE_NAMESPACE}/${{ inputs.image_name }}:${TAG}" >/dev/null 2>&1; then + IMAGE_EXISTS="true" + else + IMAGE_EXISTS="false" + fi + + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "image_exists=${IMAGE_EXISTS}" >> $GITHUB_OUTPUT + + - name: 'Build Docker image locally (if needed)' + if: steps.filter.outputs.docker_changed == 'true' || steps.vars.outputs.image_exists == 'false' + uses: ./.github/actions/docker-skiko-publish + with: + image_name: ${{ inputs.image_name }} + tag: ${{ steps.vars.outputs.tag }} + load: true + should_publish: false + + - name: 'Prepare command' + id: cmd + shell: bash + run: | + CMD="${{ inputs.command }}" + if [[ "${{ inputs.virtual-display }}" == "true" ]]; then + CMD="Xvfb :1 -screen 0 1920x1080x24 -extension RANDR +extension GLX & export DISPLAY=:1.0 && ${CMD}" + fi + { + echo "command<> $GITHUB_OUTPUT + + - name: 'Run command in container' + shell: bash + run: | + docker run --rm \ + -v "${{ github.workspace }}":/workspace \ + -v "${HOME}/.gradle":/gradle-cache \ + -e GRADLE_USER_HOME=/gradle-cache \ + -w /workspace/${{ inputs.working_directory }} \ + "ghcr.io/${{ steps.vars.outputs.image_namespace }}/${{ inputs.image_name }}:${{ steps.vars.outputs.tag }}" \ + bash -c '${{ steps.cmd.outputs.command }}' diff --git a/.github/actions/setup-prerequisites/action.yml b/.github/actions/setup-prerequisites/action.yml index f6e50e1c7..5b4e7917c 100644 --- a/.github/actions/setup-prerequisites/action.yml +++ b/.github/actions/setup-prerequisites/action.yml @@ -1,7 +1,19 @@ name: 'Setup Prerequisites' +description: 'Shared steps to prepare build environment' + runs: using: "composite" steps: + - name: Free up space on GitHub runner + if: runner.os == 'Linux' + shell: bash + run: | + df -h + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + df -h + - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 with: diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 615f1910f..2d2be1c0a 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,188 +1,159 @@ name: Docker Build and Publish on: - workflow_dispatch: + workflow_dispatch: # Allow manual triggering with an option to publish inputs: publish: description: 'Publish to registry (if false, only dry-run build)' required: false type: boolean default: true - push: - branches: - - master - paths: - - 'skiko/docker/**' - - '.github/workflows/docker-publish.yml' - pull_request: - paths: - - 'skiko/docker/**' - - '.github/workflows/docker-publish.yml' - -env: - REGISTRY: ghcr.io + workflow_call: # Allow being called by other workflows + inputs: + publish: + description: 'Publish to registry (if false, only dry-run build)' + required: true + type: boolean jobs: - # Determine if we should publish (only on push to master or manual trigger with publish=true) - config: - name: 'Configuration' + changes: + name: 'Detect Docker Changes' runs-on: ubuntu-24.04 outputs: - should_publish: ${{ steps.vars.outputs.should_publish }} - image_namespace: ${{ steps.vars.outputs.image_namespace }} - steps: - - id: vars - name: 'Set Variables' - run: | - if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/master" ]]; then - echo "should_publish=true" >> $GITHUB_OUTPUT - elif [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.publish }}" == "true" ]]; then - echo "should_publish=true" >> $GITHUB_OUTPUT - else - echo "should_publish=false" >> $GITHUB_OUTPUT - fi - echo "image_namespace=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT - - linux-amd64: - name: 'Docker Linux (x64)' - runs-on: ubuntu-24.04 - needs: config - permissions: - contents: read - packages: write + linux_amd64: ${{ steps.filter.outputs.linux_amd64 }} + linux_compat: ${{ steps.filter.outputs.linux_compat }} + linux_emscripten_amd64: ${{ steps.filter.outputs.linux_emscripten_amd64 }} + windows: ${{ steps.filter.outputs.windows }} + docker_workflow: ${{ steps.filter.outputs.docker_workflow }} steps: - uses: actions/checkout@v4 name: 'Check out code' - - uses: ./.github/actions/docker-publish - name: 'Build and Publish Docker Image' + - uses: dorny/paths-filter@v3 + id: filter with: - registry: ${{ env.REGISTRY }} - namespace: ${{ needs.config.outputs.image_namespace }} - image_name: linux-amd64 - context: ./skiko/docker/linux-amd64 - platforms: linux/amd64 - tag: ubuntu-2004 - should_publish: ${{ needs.config.outputs.should_publish }} - github_token: ${{ secrets.GITHUB_TOKEN }} + base: ${{ github.event.pull_request.base.sha || github.event.before || 'master' }} + filters: | + docker_workflow: + - '.github/actions/docker-skiko-publish/**' + - '.github/workflows/docker-publish.yml' + linux_amd64: + - 'skiko/docker/linux-amd64/**' + linux_compat: + - 'skiko/docker/linux-compat/**' + linux_emscripten_amd64: + - 'skiko/docker/linux-emscripten-amd64/**' + windows: + - 'skiko/docker/windows/**' - linux-arm64: - name: 'Docker Linux (arm64)' + linux-amd64: + name: 'Docker Linux (x64)' runs-on: ubuntu-24.04 - needs: config + needs: changes + if: | + needs.changes.outputs.linux_amd64 == 'true' || + needs.changes.outputs.docker_workflow == 'true' permissions: contents: read packages: write + steps: - uses: actions/checkout@v4 name: 'Check out code' - - uses: ./.github/actions/docker-publish + - uses: ./.github/actions/docker-skiko-publish name: 'Build and Publish Docker Image' + id: docker-publish with: - registry: ${{ env.REGISTRY }} - namespace: ${{ needs.config.outputs.image_namespace }} - image_name: linux-arm64 - context: ./skiko/docker/linux-arm64 - platforms: linux/arm64 - tag: ubuntu-2004 - should_publish: ${{ needs.config.outputs.should_publish }} + image_name: linux-amd64 + tag: ${{ github.ref_name }} + should_publish: ${{ inputs.publish }} github_token: ${{ secrets.GITHUB_TOKEN }} - linux-android-amd64: - name: 'Docker Linux with Android SDK (x64)' + linux-compat: + name: 'Docker Linux (Compatibility)' runs-on: ubuntu-24.04 - needs: config + needs: changes + if: | + needs.changes.outputs.linux_compat == 'true' || + needs.changes.outputs.docker_workflow == 'true' permissions: contents: read packages: write + steps: - uses: actions/checkout@v4 name: 'Check out code' - - uses: ./.github/actions/docker-publish + - uses: ./.github/actions/docker-skiko-publish name: 'Build and Publish Docker Image' + id: docker-publish with: - registry: ${{ env.REGISTRY }} - namespace: ${{ needs.config.outputs.image_namespace }} - image_name: linux-android-amd64 - context: ./skiko/docker/linux-android-amd64 - platforms: linux/amd64 - tag: ubuntu-2004 - should_publish: ${{ needs.config.outputs.should_publish }} + image_name: linux-compat + platforms: linux/amd64,linux/arm64 + tag: ${{ github.ref_name }} + should_publish: ${{ inputs.publish }} github_token: ${{ secrets.GITHUB_TOKEN }} linux-emscripten-amd64: name: 'Docker Linux with Emscripten (x64)' runs-on: ubuntu-24.04 - needs: config + needs: changes + if: | + needs.changes.outputs.linux_emscripten_amd64 == 'true' || + needs.changes.outputs.docker_workflow == 'true' permissions: contents: read packages: write - steps: - - uses: actions/checkout@v4 - name: 'Check out code' - - - uses: ./.github/actions/docker-publish - name: 'Build and Publish Docker Image' - with: - registry: ${{ env.REGISTRY }} - namespace: ${{ needs.config.outputs.image_namespace }} - image_name: linux-emscripten-amd64 - context: ./skiko/docker/linux-emscripten-amd64 - platforms: linux/amd64 - tag: ubuntu-2004 - should_publish: ${{ needs.config.outputs.should_publish }} - github_token: ${{ secrets.GITHUB_TOKEN }} - linux-compat: - name: 'Docker Linux (Compatibility)' - runs-on: ubuntu-24.04 - needs: config - permissions: - contents: read - packages: write steps: - uses: actions/checkout@v4 name: 'Check out code' - - uses: ./.github/actions/docker-publish + - uses: ./.github/actions/docker-skiko-publish name: 'Build and Publish Docker Image' + id: docker-publish with: - registry: ${{ env.REGISTRY }} - namespace: ${{ needs.config.outputs.image_namespace }} - image_name: linux-compat - context: ./skiko/docker/linux-compat - platforms: linux/amd64,linux/arm64 - tag: amazonlinux2-latest - should_publish: ${{ needs.config.outputs.should_publish }} + image_name: linux-emscripten-amd64 + tag: ${{ github.ref_name }} + should_publish: ${{ inputs.publish }} github_token: ${{ secrets.GITHUB_TOKEN }} windows: name: 'Docker Windows' runs-on: windows-2022 - needs: config + needs: changes + if: | + needs.changes.outputs.windows == 'true' || + needs.changes.outputs.docker_workflow == 'true' permissions: contents: read packages: write + steps: - uses: actions/checkout@v4 name: 'Check out code' - - name: 'Log in to GitHub Container Registry' - if: needs.config.outputs.should_publish == 'true' + - name: 'Set Variables' shell: pwsh run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ${{ env.REGISTRY }} -u ${{ github.actor }} --password-stdin + "IMAGE_REPOSITORY=$($env:GITHUB_REPOSITORY.ToLower())" >> $env:GITHUB_ENV + $tag = "${{ github.ref_name }}".Replace('/', '-') + "TAG=$tag" >> $env:GITHUB_ENV + - name: 'Log in to GitHub Container Registry' + if: inputs.publish == true + shell: pwsh + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - name: 'Build Image' shell: pwsh working-directory: ./skiko/docker/windows run: | - docker build -t ${{ env.REGISTRY }}/${{ needs.config.outputs.image_namespace }}/windows-amd64:ltsc2022 -m 2G . + docker build -t "ghcr.io/$($env:IMAGE_REPOSITORY)/windows-amd64:$($env:TAG)" -m 2G . - name: 'Push Image' - if: needs.config.outputs.should_publish == 'true' + if: inputs.publish == true shell: pwsh run: | - docker push ${{ env.REGISTRY }}/${{ needs.config.outputs.image_namespace }}/windows-amd64:ltsc2022 + docker push "ghcr.io/$($env:IMAGE_REPOSITORY)/windows-amd64:$($env:TAG)" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 792515ccc..593bbe004 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,46 +1,41 @@ -# Build Skiko documentation -name: Doc +name: Skiko Documentation on: - push: - branches: [ master ] - # Temporary, for testing. Remove! - #pull_request: - # branches: [ master ] - - workflow_dispatch: + workflow_call: # Allow being called by other workflows + inputs: + publish: + description: 'Publish documentation (if false, only dry-run build)' + required: true + type: boolean + secrets: + ACCESS_TOKEN: + description: 'GitHub token for deploying to gh-pages (required when publish is true)' + required: false jobs: dokka: - runs-on: ubuntu-22.04 + name: 'Build and Publish Documentation' + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - - uses: actions/setup-java@v3 - name: 'Set up JDK 21' - with: - distribution: 'adopt' - java-version: '21' - cache: 'gradle' - - - shell: bash - name: 'Set up Linux build environment' - run: | - sudo apt-get update -y - sudo apt-get install libglu1-mesa-dev libxrandr-dev libdbus-1-dev multistrap -y - sudo apt-get install gcc-9 g++-9 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9 - sudo update-alternatives --config gcc - sudo apt-get install gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu -y - sudo update-alternatives --install /usr/bin/aarch64-linux-gnu-gcc aarch64-linux-gnu-gcc /usr/bin/aarch64-linux-gnu-gcc-9 60 --slave /usr/bin/aarch64-linux-gnu-g++ aarch64-linux-gnu-g++ /usr/bin/aarch64-linux-gnu-g++-9 - sudo update-alternatives --config aarch64-linux-gnu-gcc - sudo Xvfb :0 -screen 0 1280x720x24 & + - uses: ./.github/actions/setup-prerequisites + name: 'Setup Prerequisites' - - name: 'Build Dokka documentation' - run: bash -c 'JAVA_OPTS="-Xmx4g" ./gradlew --no-daemon -Pskiko.native.enabled=true -Pskiko.wasm.enabled=true -Pskiko.android.enabled=true :skiko:dokkaHtml' + - uses: ./.github/actions/docker-skiko-run + name: 'Build Dokka Documentation' + with: + image_name: linux-compat + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.native.enabled=true \ + -Pskiko.wasm.enabled=true \ + -Pskiko.android.enabled=true \ + :skiko:dokkaHtml - - name: 'Publish documentation' + - name: 'Publish Documentation' + if: ${{ inputs.publish }} uses: JamesIves/github-pages-deploy-action@releases/v3 with: ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml new file mode 100644 index 000000000..4866857d4 --- /dev/null +++ b/.github/workflows/post-merge.yml @@ -0,0 +1,40 @@ +name: Post-Merge CI + +on: + workflow_dispatch: # Allow manual triggering + push: # Trigger on pushes to master and release branches + branches: + - master + - 'release/*' + +concurrency: + group: post-merge-${{ github.ref }} + cancel-in-progress: false + +jobs: + docker-publish: + name: 'Docker Publish' + uses: ./.github/workflows/docker-publish.yml + with: + publish: true + permissions: + contents: read + packages: write + + tests: + name: 'Tests' + needs: docker-publish + uses: ./.github/workflows/tests.yml + + skiko-publish: + name: 'Skiko Publish (Dry Run)' + needs: docker-publish + uses: ./.github/workflows/publish-dry-run.yml + + docs-publish: + name: 'Documentation Publish' + needs: docker-publish + uses: ./.github/workflows/docs.yml + with: + publish: true + secrets: inherit diff --git a/.github/workflows/publish-dry-run.yml b/.github/workflows/publish-dry-run.yml index 978e3e022..7b39615a9 100644 --- a/.github/workflows/publish-dry-run.yml +++ b/.github/workflows/publish-dry-run.yml @@ -1,83 +1,88 @@ name: Skiko Publish Dry Run + on: - workflow_dispatch: - pull_request: - push: - branches: - - master + workflow_call: # Allow being called by other workflows jobs: Android: + name: 'Android Publish Dry Run' runs-on: ubuntu-24.04 - container: - image: ghcr.io/jetbrains/skiko/linux-android-amd64:ubuntu-2004 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: ./.github/actions/setup-prerequisites name: 'Setup Prerequisites' - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Publish Maven Local' - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.android.enabled=true \ - :skiko:publishToMavenLocal + with: + image_name: linux-compat + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.android.enabled=true \ + :skiko:publishToMavenLocal - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Check Android Sample' - working-directory: samples/SkiaAndroidSample - run: | - ./gradlew --no-daemon --stacktrace \ - packageRelease + with: + image_name: linux-compat + working_directory: samples/SkiaAndroidSample + command: | + ./gradlew --no-daemon --stacktrace \ + packageRelease Web: + name: 'Web Publish Dry Run' runs-on: ubuntu-24.04 - container: - image: ghcr.io/jetbrains/skiko/linux-emscripten-amd64:ubuntu-2004 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: ./.github/actions/setup-prerequisites name: 'Setup Prerequisites' - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Publish Maven Local' - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.wasm.enabled=true \ - :skiko:publishToMavenLocal + with: + image_name: linux-emscripten-amd64 + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.wasm.enabled=true \ + :skiko:publishToMavenLocal Linux: + name: 'Linux Publish Dry Run' runs-on: ubuntu-24.04 - container: - image: ghcr.io/jetbrains/skiko/linux-amd64:ubuntu-2004 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: ./.github/actions/setup-prerequisites name: 'Setup Prerequisites' - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Publish Maven Local' - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.native.enabled=true \ - :skiko:publishToMavenLocal + with: + image_name: linux-compat + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.native.enabled=true \ + :skiko:publishToMavenLocal - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Check AWT Sample' - run: | - ./gradlew --no-daemon --stacktrace \ - :SkiaAwtSample:installDist + with: + image_name: linux-compat + command: | + ./gradlew --no-daemon --stacktrace \ + :SkiaAwtSample:installDist macOS: + name: 'macOS Publish Dry Run' runs-on: macos-15 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: actions/setup-java@v4 @@ -103,9 +108,10 @@ jobs: :SkiaAwtSample:installDist Windows: + name: 'Windows Publish Dry Run' runs-on: windows-2022 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: microsoft/setup-msbuild@v1 diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 000000000..b9b238a26 --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,30 @@ +name: Pull Request CI + +on: + workflow_dispatch: # Allow manual triggering + pull_request: # Trigger on all pull requests + +jobs: + docker-publish: + name: 'Docker Publish (Dry Run)' + uses: ./.github/workflows/docker-publish.yml + with: + publish: false + permissions: + contents: read + packages: write # Is not used in case of dry run, but required to avoid "The workflow is not valid" error + + tests: + name: 'Tests' + uses: ./.github/workflows/tests.yml + + skiko-publish: + name: 'Skiko Publish (Dry Run)' + uses: ./.github/workflows/publish-dry-run.yml + + docs-publish: + name: 'Documentation Publish (Dry Run)' + uses: ./.github/workflows/docs.yml + with: + publish: false + secrets: inherit diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ae5467f6..b71f10e04 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,10 +1,7 @@ name: Skiko Tests + on: - workflow_dispatch: - pull_request: - push: - branches: - - master + workflow_call: # Allow being called by other workflows jobs: macos: @@ -14,7 +11,7 @@ jobs: # TODO: Update once resolved runs-on: macos-14 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: actions/setup-java@v4 @@ -52,6 +49,7 @@ jobs: path: | ./skiko/build/reports/tests ./skiko/src/jvmTest/screenshots/*_actual.png + ./skiko/hs_err_pid*.log retention-days: 5 - uses: test-summary/action@v2 @@ -64,7 +62,7 @@ jobs: name: 'iOS' runs-on: macos-15 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: actions/setup-java@v4 @@ -122,7 +120,7 @@ jobs: name: 'tvOS' runs-on: macos-15 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: actions/setup-java@v4 @@ -161,39 +159,37 @@ jobs: linux-amd64: name: 'Linux (x64)' runs-on: ubuntu-24.04 - container: - image: ghcr.io/jetbrains/skiko/linux-amd64:ubuntu-2004 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: ./.github/actions/setup-prerequisites name: 'Setup Prerequisites' - - shell: bash - name: 'Start X Server' - run: | - Xvfb :1 -screen 0 1920x1080x24 -extension RANDR +extension GLX & - echo "DISPLAY=:1.0" >> $GITHUB_ENV - - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Run Linux (x64) Tests' - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.native.enabled=true \ - -Dskiko.test.onci=true \ - -Dskiko.test.performance.enabled=false \ - :skiko:linuxX64Test + with: + image_name: linux-compat + virtual-display: true + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.native.enabled=true \ + -Dskiko.test.onci=true \ + -Dskiko.test.performance.enabled=false \ + :skiko:linuxX64Test - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Run AWT Tests' timeout-minutes: 25 - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.native.enabled=true \ - -Dskiko.test.onci=true \ - -Dskiko.test.performance.enabled=false \ - :skiko:awtTest + with: + image_name: linux-compat + virtual-display: true + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.native.enabled=true \ + -Dskiko.test.onci=true \ + -Dskiko.test.performance.enabled=false \ + :skiko:awtTest - uses: actions/upload-artifact@v4 name: 'Upload Test Results' @@ -203,6 +199,7 @@ jobs: path: | ./skiko/build/reports/tests ./skiko/src/jvmTest/screenshots/*_actual.png + ./skiko/hs_err_pid*.log - uses: test-summary/action@v2 name: 'Test Summary' @@ -213,22 +210,22 @@ jobs: linux-cross-compile: name: 'Cross-compile Linux (arm) on x64' runs-on: ubuntu-24.04 - container: - image: ghcr.io/jetbrains/skiko/linux-amd64:ubuntu-2004 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: ./.github/actions/setup-prerequisites name: 'Setup Prerequisites' - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Compile Linux (arm64) Tests' - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.arch=x64 \ - -Pskiko.native.linux.enabled=true \ - :skiko:linkDebugTestLinuxArm64 + with: + image_name: linux-amd64 + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.arch=x64 \ + -Pskiko.native.linux.enabled=true \ + :skiko:linkDebugTestLinuxArm64 - uses: actions/upload-artifact@v4 name: 'Upload linuxArm64 test binary as artifact' @@ -242,43 +239,41 @@ jobs: name: 'Linux (arm64)' needs: linux-cross-compile runs-on: ubuntu-24.04-arm - container: - image: ghcr.io/jetbrains/skiko/linux-arm64:ubuntu-2004 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: ./.github/actions/setup-prerequisites name: 'Setup Prerequisites' - - shell: bash - name: 'Start X Server' - run: | - Xvfb :1 -screen 0 1920x1080x24 -extension RANDR +extension GLX & - echo "DISPLAY=:1.0" >> $GITHUB_ENV - - uses: actions/download-artifact@v4 name: 'Download precompiled linuxArm64 test binary' with: name: test-binary-linuxArm64 path: ./skiko/build/bin/linuxArm64 - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Run Linux (arm64) Tests' - working-directory: skiko - run: | - chmod +x ./build/bin/linuxArm64/debugTest/test.kexe - ./build/bin/linuxArm64/debugTest/test.kexe + with: + image_name: linux-compat + working_directory: skiko + virtual-display: true + command: | + chmod +x ./build/bin/linuxArm64/debugTest/test.kexe + ./build/bin/linuxArm64/debugTest/test.kexe - - shell: bash + - uses: ./.github/actions/docker-skiko-run name: 'Run AWT Tests' timeout-minutes: 25 - run: | - ./gradlew --no-daemon --stacktrace \ - -Pskiko.native.enabled=true \ - -Dskiko.test.onci=true \ - -Dskiko.test.performance.enabled=false \ - :skiko:awtTest + with: + image_name: linux-compat + virtual-display: true + command: | + ./gradlew --no-daemon --stacktrace \ + -Pskiko.native.enabled=true \ + -Dskiko.test.onci=true \ + -Dskiko.test.performance.enabled=false \ + :skiko:awtTest - name: Upload Test Results uses: actions/upload-artifact@v4 @@ -288,6 +283,7 @@ jobs: path: | ./skiko/build/reports/tests ./skiko/src/jvmTest/screenshots/*_actual.png + ./skiko/hs_err_pid*.log retention-days: 5 - name: Test Summary @@ -300,7 +296,7 @@ jobs: name: 'Windows' runs-on: windows-2022 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - uses: microsoft/setup-msbuild@v1 @@ -334,6 +330,7 @@ jobs: path: | ./skiko/build/reports/tests ./skiko/src/jvmTest/screenshots/*_actual.png + ./skiko/hs_err_pid*.log retention-days: 5 - uses: test-summary/action@v2 @@ -344,24 +341,29 @@ jobs: web-wasm: name: 'Web' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 name: 'Check out code' - - uses: actions/setup-java@v3 + # Setup environment directly instead of using docker image + # because of instability of running tests inside docker + + - uses: actions/setup-java@v4 name: 'Setup JDK 21' with: java-version: '21' - distribution: 'adopt' - cache: 'gradle' + distribution: 'corretto' + + - uses: ./.github/actions/setup-prerequisites + name: 'Setup Prerequisites' - uses: browser-actions/setup-chrome@v1 name: 'Setup Google Chrome' with: chrome-version: stable - - name: 'Install emscripten' + - name: 'Install Emscripten' shell: bash working-directory: skiko run: | diff --git a/skiko/buildSrc/src/main/kotlin/tasks/configuration/NativeTasksConfiguration.kt b/skiko/buildSrc/src/main/kotlin/tasks/configuration/NativeTasksConfiguration.kt index 8fe966bc4..820a52610 100644 --- a/skiko/buildSrc/src/main/kotlin/tasks/configuration/NativeTasksConfiguration.kt +++ b/skiko/buildSrc/src/main/kotlin/tasks/configuration/NativeTasksConfiguration.kt @@ -254,6 +254,7 @@ fun SkikoProjectContext.configureNativeTarget(os: OS, arch: Arch, target: Kotlin } OS.Linux -> { val options = mutableListOf( + "-L/usr/lib64", "-L/usr/lib/${if (arch == Arch.Arm64) "aarch64" else "x86_64"}-linux-gnu", "-lfontconfig", "-lGL", diff --git a/skiko/docker/linux-amd64/Dockerfile b/skiko/docker/linux-amd64/Dockerfile index d1c9ea8cb..b1310ad99 100644 --- a/skiko/docker/linux-amd64/Dockerfile +++ b/skiko/docker/linux-amd64/Dockerfile @@ -45,12 +45,11 @@ RUN cd /tmp && mkdir -p $ARM_TOOLCHAIN_PATH && \ ln -s $ARM_TOOLCHAIN_PATH/bin/aarch64-none-linux-gnu-ar /usr/local/bin/aarch64-linux-gnu-ar # Copy development headers to ARM64 sysroot +ENV ARM_SYSROOT_HEADERS="fontconfig freetype2 GL X11 KHR" RUN mkdir -p $ARM_TOOLCHAIN_SYSROOT/usr/include && \ - cp -r /usr/include/fontconfig $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/freetype2 $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/GL $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/X11 $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/KHR $ARM_TOOLCHAIN_SYSROOT/usr/include/ + for dir in $ARM_SYSROOT_HEADERS; do \ + cp -r /usr/include/$dir $ARM_TOOLCHAIN_SYSROOT/usr/include/ || exit 1; \ + done # Use UTF-8 by default ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8 diff --git a/skiko/docker/linux-android-amd64/Dockerfile b/skiko/docker/linux-android-amd64/Dockerfile deleted file mode 100644 index 9ad5b5e25..000000000 --- a/skiko/docker/linux-android-amd64/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM ubuntu:20.04 -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt update -y && \ - apt install binutils build-essential software-properties-common -y && \ - apt install zip unzip git python curl wget -y && \ - apt install fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev -y && \ - apt install openjdk-21-jdk -y && \ - rm -rf /var/lib/apt/lists/* - -# Install Android SDK -ENV ANDROID_SDK_ROOT=/android/sdk -ARG CMD_TOOLS_VERSION=13114758 -ARG CMD_TOOLS_ROOT=$ANDROID_SDK_ROOT/cmdline-tools/$CMD_TOOLS_VERSION -ARG SDK_MANAGER=$CMD_TOOLS_ROOT/bin/sdkmanager -RUN mkdir -p $CMD_TOOLS_ROOT && \ - export CMD_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-${CMD_TOOLS_VERSION}_latest.zip" && \ - wget $CMD_TOOLS_URL -O cmd-tools.zip && \ - unzip cmd-tools.zip && \ - rm cmd-tools.zip && \ - mv cmdline-tools/* $CMD_TOOLS_ROOT/ -ARG ANDROID_PLATFORM=android-33 -RUN yes | $SDK_MANAGER --licenses && \ - $SDK_MANAGER "platforms;$ANDROID_PLATFORM" && \ - cd $ANDROID_SDK_ROOT/platforms/$ANDROID_PLATFORM && \ - ls -1 | grep -v android.jar | xargs rm -rf -ARG NDK_VERSION=29.0.14206865 -RUN $SDK_MANAGER "ndk;$NDK_VERSION" && \ - cd $ANDROID_SDK_ROOT/ndk/$NDK_VERSION && \ - ls -1 | grep -v toolchains | xargs rm -rf - -# Use UTF-8 by default -ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8 -ENV JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF diff --git a/skiko/docker/linux-arm64/Dockerfile b/skiko/docker/linux-arm64/Dockerfile deleted file mode 100644 index eb46d0494..000000000 --- a/skiko/docker/linux-arm64/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM arm64v8/ubuntu:20.04 -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt update -y && \ - apt install binutils build-essential software-properties-common -y && \ - apt install zip unzip git python curl wget xvfb -y && \ - apt install fontconfig libfontconfig1-dev libglu1-mesa-dev libxrandr-dev libdbus-1-dev -y && \ - apt install openjdk-21-jdk -y && \ - rm -rf /var/lib/apt/lists/* - -# Install chromium tools -ENV DEPOT_TOOLS=/usr/depot_tools -ENV PATH=$DEPOT_TOOLS:$PATH -RUN git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git' $DEPOT_TOOLS - -# Use UTF-8 by default -ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8 -ENV JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF diff --git a/skiko/docker/linux-compat/Dockerfile b/skiko/docker/linux-compat/Dockerfile index 677c7ef92..660d4acc2 100644 --- a/skiko/docker/linux-compat/Dockerfile +++ b/skiko/docker/linux-compat/Dockerfile @@ -1,19 +1,41 @@ -# Multi-architecture Dockerfile for building Skiko on Linux, compatiable with GLIBC 2.26 +# Multi-architecture Dockerfile for building Skiko on Linux, compatible with GLIBC 2.26 # Supports both amd64 (x64) and arm64 architectures FROM amazonlinux:2 ARG TARGETARCH # Install basic & development tools -RUN yum install -y tar xz git wget curl zip gzip && \ +RUN yum install -y tar xz git wget curl zip unzip gzip && \ yum install -y gcc10 gcc10-c++ gcc10-binutils make && \ ln -sf /usr/bin/gcc10-gcc /usr/bin/gcc && \ ln -sf /usr/bin/gcc10-g++ /usr/bin/g++ && \ ln -sf /usr/bin/gcc10-ar /usr/bin/ar && \ yum clean all -# Install libraries -RUN yum install -y fontconfig fontconfig-devel mesa-libGLU-devel libXrandr-devel dbus-devel && \ +# Install Xvfb (and GLX module) for headless UI testing +RUN yum install -y xorg-x11-server-Xvfb xorg-x11-server-Xorg && \ + yum clean all + +# Install libraries for native builds +RUN yum install -y \ + fontconfig fontconfig-devel \ + freetype freetype-devel \ + mesa-libGL mesa-libGL-devel \ + mesa-libEGL mesa-libEGL-devel \ + mesa-libGLU mesa-libGLU-devel \ + mesa-libgbm mesa-libgbm-devel \ + mesa-dri-drivers \ + libdrm libdrm-devel \ + libX11 libX11-devel \ + libXrandr libXrandr-devel \ + libXext libXext-devel \ + libXrender libXrender-devel \ + libXfixes libXfixes-devel \ + libXdamage libXdamage-devel \ + libXtst libXtst-devel \ + libglvnd libglvnd-devel \ + libglvnd-glx libglvnd-egl \ + dbus-devel dbus-libs && \ yum clean all # Install Java (architecture-specific) @@ -34,12 +56,38 @@ RUN if [ "$TARGETARCH" = "arm64" ]; then \ find $JAVA_BASE -type d -maxdepth 1 -name "$JAVA_PATTERN" -exec mv {} $JAVA_HOME \; && \ rm $JAVA_ARCHIVE +# Install Android SDK +ENV ANDROID_SDK_ROOT=/android/sdk +ENV CMD_TOOLS_VERSION=13114758 +ENV CMD_TOOLS_ROOT=$ANDROID_SDK_ROOT/cmdline-tools/$CMD_TOOLS_VERSION +ENV SDK_MANAGER=$CMD_TOOLS_ROOT/bin/sdkmanager +RUN mkdir -p $CMD_TOOLS_ROOT && \ + export CMD_TOOLS_URL="https://dl.google.com/android/repository/commandlinetools-linux-${CMD_TOOLS_VERSION}_latest.zip" && \ + wget $CMD_TOOLS_URL -O /tmp/cmd-tools.zip && \ + unzip /tmp/cmd-tools.zip -d /tmp/cmd-tools && \ + rm /tmp/cmd-tools.zip && \ + mv /tmp/cmd-tools/cmdline-tools/* $CMD_TOOLS_ROOT/ && \ + rm -rf /tmp/cmd-tools +ENV ANDROID_PLATFORM=android-33 +RUN yes | $SDK_MANAGER --licenses && \ + $SDK_MANAGER "platforms;$ANDROID_PLATFORM" && \ + cd $ANDROID_SDK_ROOT/platforms/$ANDROID_PLATFORM && \ + ls -1 | grep -v android.jar | xargs rm -rf +ENV NDK_VERSION=29.0.14206865 +RUN $SDK_MANAGER "ndk;$NDK_VERSION" && \ + cd $ANDROID_SDK_ROOT/ndk/$NDK_VERSION && \ + ls -1 | grep -v toolchains | xargs rm -rf + # Install chromium tools ENV DEPOT_TOOLS=/usr/depot_tools ENV PATH=$DEPOT_TOOLS:$PATH RUN git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git' $DEPOT_TOOLS # Install cross-compilation toolchain for ARM64 +# This cross-compilation setup is required because Kotlin/Native doesn't support +# Linux ARM64 as a HOST platform (only as a target). When cross-compiling from x64 to ARM64, +# Kotlin/Native's linker needs ARM64 versions of system libraries. +# See: https://youtrack.jetbrains.com/issue/KT-36871 ENV ARM_TOOLCHAIN_VERSION=10.3-2021.07 ENV ARM_TOOLCHAIN_NAME=gcc-arm-${ARM_TOOLCHAIN_VERSION}-x86_64-aarch64-none-linux-gnu ENV ARM_TOOLCHAIN_PATH=/opt/arm-gnu-toolchain @@ -55,13 +103,12 @@ RUN if [ "$TARGETARCH" = "amd64" ]; then \ fi # Copy development headers to ARM64 sysroot +ENV ARM_SYSROOT_HEADERS="fontconfig freetype2 GL X11 KHR" RUN if [ "$TARGETARCH" = "amd64" ]; then \ mkdir -p $ARM_TOOLCHAIN_SYSROOT/usr/include && \ - cp -r /usr/include/fontconfig $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/freetype2 $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/GL $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/X11 $ARM_TOOLCHAIN_SYSROOT/usr/include/ && \ - cp -r /usr/include/KHR $ARM_TOOLCHAIN_SYSROOT/usr/include/; \ + for dir in $ARM_SYSROOT_HEADERS; do \ + cp -r /usr/include/$dir $ARM_TOOLCHAIN_SYSROOT/usr/include/ || exit 1; \ + done; \ fi ENV LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=en_US.UTF-8