Skip to content

chore: Fix Windows Docker build #2825

chore: Fix Windows Docker build

chore: Fix Windows Docker build #2825

Workflow file for this run

name: Build and release
on:
workflow_dispatch:
pull_request:
push:
branches:
- '**'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
native_image_opts: --verbose -H:Log=registerResource:verbose -H:+PrintClassInitialization -march=compatibility
graal_distribution: graalvm-community
graal_java_version: 21
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check-out source code
uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: PROD - Prepare GitHub release
id: create_prod_release
uses: googleapis/release-please-action@v4
if: contains(github.ref, 'refs/heads/rel/')
with:
skip-github-pull-request: true
target-branch: ${{ github.ref_name }}
release-type: simple
- name: PROD - Define release info
if: steps.create_prod_release.outputs.release_created
run: |
tag=${{steps.create_prod_release.outputs.tag_name}}
version=${{steps.create_prod_release.outputs.version}}
major=${{steps.create_prod_release.outputs.major}}
minor=${{steps.create_prod_release.outputs.minor}}
patch=${{steps.create_prod_release.outputs.patch}}
echo DO_BUILD=true >> $GITHUB_ENV
echo DO_RELEASE=true >> $GITHUB_ENV
echo DO_PROD_RELEASE=true >> $GITHUB_ENV
echo RELEASE_TAG=${tag} >> $GITHUB_ENV
echo RELEASE_VERSION=${version} >> $GITHUB_ENV
echo VERSION_MAJOR=${major} >> $GITHUB_ENV
echo VERSION_MINOR=${minor} >> $GITHUB_ENV
echo VERSION_PATCH=${patch} >> $GITHUB_ENV
- name: DEV - Define release info
if: (startsWith(github.ref, 'refs/heads/') || github.event_name == 'pull_request') && !env.DO_PROD_RELEASE
run: |
# For pull_request events, use head_ref (source branch); for push/workflow_dispatch, extract from ref
if [ "${{ github.event_name }}" == "pull_request" ]; then
branch="${{ github.head_ref }}"
else
branch="${GITHUB_REF#refs/heads/}"
fi
tag="dev_${branch//[^a-zA-Z0-9_.-]/.}" # Replace all special characters by a dot
tag="${tag//dev_dev./dev_}" # Remove duplicate dev-prefix
tag="${tag//dev_rel./dev_}" # Remove rel-prefix
major="0"
minor="$(date +'%Y%m%d')"
patch="$(date +'%H%M%S')-$tag"
version="${major}.${minor}.${patch}"
echo DO_BUILD=true >> $GITHUB_ENV # We always want to do a build if we're building a branch
echo BRANCH=${branch} >> $GITHUB_ENV
echo RELEASE_TAG=${tag} >> $GITHUB_ENV
echo RELEASE_VERSION=${version} >> $GITHUB_ENV
echo VERSION_MAJOR=${major} >> $GITHUB_ENV
echo VERSION_MINOR=${minor} >> $GITHUB_ENV
echo VERSION_PATCH=${patch} >> $GITHUB_ENV
if git ls-remote --exit-code origin refs/tags/${tag} >/dev/null 2>&1; then
echo "Found tag ${tag}, development release will be published"
echo DO_RELEASE=true >> $GITHUB_ENV
echo DO_DEV_RELEASE=true >> $GITHUB_ENV
else
echo "Tag ${tag} does not exist, no development release will be published"
fi
- name: Build release ${{env.RELEASE_VERSION}}
if: env.DO_BUILD
run: ./gradlew clean build dist distThirdPartyReleaseAsset distFtest -Pversion=${{env.RELEASE_VERSION}} -PautoFormat=false
- name: Check fcli version
if: env.DO_BUILD
run: java -jar build/libs/fcli.jar --version | tee /dev/stderr | grep -E '[0-9]+\.[0-9]+\.[0-9]+' >/dev/null || (echo "fcli --version doesn't output proper version number"; exit 1)
- name: Publish build artifacts
uses: actions/upload-artifact@v4
with:
name: build-output
path: build/dist/**/*
outputs:
do_release: ${{ env.DO_RELEASE }}
do_prod_release: ${{ env.DO_PROD_RELEASE }}
do_dev_release: ${{ env.DO_DEV_RELEASE }}
release_tag: ${{ env.RELEASE_TAG }}
release_version: ${{ env.RELEASE_VERSION }}
version_major: ${{ env.VERSION_MAJOR }}
version_minor: ${{ env.VERSION_MINOR }}
version_patch: ${{ env.VERSION_PATCH }}
branch: ${{ env.BRANCH }}
native_linux:
name: native-image-linux
needs: build
runs-on: ubuntu-22.04
# env:
# TOOLCHAIN_BASE: /opt/musl_cc
# TOOLCHAIN_DIR: /opt/musl_cc/x86_64-linux-musl-native
# CC: /opt/musl_cc/x86_64-linux-musl-native/bin/gcc
steps:
- name: Check-out source code
uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
distribution: ${{ env.graal_distribution }}
java-version: ${{ env.graal_java_version }}
native-image-musl: true
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
path: ./artifacts
name: build-output
# For Linux, we build a statically linked native image, to allow for building a 'FROM scratch'
# Docker image, and to avoid libc version issues. Since Jansi is not supported on statically
# linked images (see https://github.com/fusesource/jansi/issues/246), we set a system property
# to indicate that FortifyCLI shouldn't try to invoke AnsiConsole::systemInstall/Uninstall. In
# order for FortifyCLI to be able to see this system property, we need to initialize this class
# at build time (see https://www.graalvm.org/22.1/reference-manual/native-image/Properties/).
# We also exclude the native Jansi library resources, as these are now no longer needed.
- name: Create native fcli
run: native-image ${{ env.native_image_opts }} --static --libc=musl -Djansi.disable=true --initialize-at-build-time=com.fortify.cli.common.util.JAnsiConfig -H:ExcludeResources="org/fusesource/jansi/internal/native/.*" -jar ./artifacts/release-assets/fcli.jar fcli
- name: Compress native fcli
uses: svenstaro/upx-action@v2
with:
files: fcli
- name: Basic test of native fcli
run: ./fcli --help && ./fcli get --help && ./fcli action help ci
- name: Check fcli version
run: ./fcli --version | tee /dev/stderr | grep -E '[0-9]+\.[0-9]+\.[0-9]+' >/dev/null || (echo "fcli --version doesn't output proper version number"; exit 1)
- name: Package native fcli
run: tar -zcvf artifacts/release-assets/fcli-linux.tgz fcli -C ./artifacts fcli_completion
- uses: actions/upload-artifact@v4
with:
path: ./artifacts/**/fcli-linux.tgz
name: fcli-linux
native_mac:
name: native-image-mac
needs: build
runs-on: macos-latest
steps:
- name: Check-out source code
uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
distribution: ${{ env.graal_distribution }}
java-version: ${{ env.graal_java_version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
path: ./artifacts
name: build-output
# For MacOS, we build a dynamically linked image. Jansi by default provides a resource-config.json
# file to include native libraries for all platforms; we override this to include only the MacOS
# libraries
- name: Create native fcli
run: native-image ${{ env.native_image_opts }} -march=compatibility -H:ExcludeResources="org/fusesource/jansi/internal/native/Windows/.*" -H:ExcludeResources="org/fusesource/jansi/internal/native/Linux/.*" -H:ExcludeResources="org/fusesource/jansi/internal/native/FreeBSD/.*" -jar ./artifacts/release-assets/fcli.jar fcli
# Disabled for now, as compressed binaries crash on macOS Ventura or above
#- name: Compress native fcli
# uses: svenstaro/upx-action@v2
# with:
# files: fcli
- name: Basic test of native fcli
run: ./fcli --help && ./fcli get --help && ./fcli action help ci
- name: Package native fcli
run: tar -zcvf ./artifacts/release-assets/fcli-mac.tgz fcli -C ./artifacts fcli_completion
- uses: actions/upload-artifact@v4
with:
path: ./artifacts/**/fcli-mac.tgz
name: fcli-mac
native_win:
name: native-image-win
needs: build
runs-on: windows-2022
steps:
- uses: graalvm/setup-graalvm@v1
with:
distribution: ${{ env.graal_distribution }}
java-version: ${{ env.graal_java_version }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
path: ./artifacts
name: build-output
# For Windows, we build a dynamically linked image. Jansi by default provides a resource-config.json
# file to include native libraries for all platforms; we override this to include only the 64-bit
# Windows library
- name: Create native fcli
run: >-
"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" &&
${{ env.JAVA_HOME }}\bin\native-image.cmd ${{ env.native_image_opts }} -H:ExcludeResources="org/fusesource/jansi/internal/native/Mac/.*" -H:ExcludeResources="org/fusesource/jansi/internal/native/Linux/.*" -H:ExcludeResources="org/fusesource/jansi/internal/native/FreeBSD/.*" -jar .\artifacts\release-assets\fcli.jar fcli
shell: cmd
# We don't compress the Windows binary for now as this is incompatible with current Graal version.
# See https://github.com/fortify/fcli/issues/148
# - name: Compress native fcli
# uses: svenstaro/upx-action@v2
# with:
# files: fcli.exe
- name: Basic test of native fcli
run: |
.\fcli.exe --help
.\fcli.exe get --help
.\fcli.exe action help ci
- name: Package native fcli
run: 7z a artifacts\release-assets\fcli-windows.zip fcli*.exe
- uses: actions/upload-artifact@v4
with:
path: ./artifacts/**/fcli-windows.zip
name: fcli-windows
combine-artifacts:
needs: [build, native_linux, native_mac, native_win]
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
merge-multiple: true
- run: |
cd ./artifacts/release-assets
for f in *; do
sha256sum ${f} > ${f}.sha256
done
if [ -z "$SIGN_KEY" ] || [ -z "$SIGN_PASSPHRASE" ]; then
echo "Signatures for fcli artifacts haven't been calculated; SIGN_KEY and/or SIGN_PASSPHRASE secrets not present"
else
for f in *; do
openssl dgst -sha256 -passin env:SIGN_PASSPHRASE -sign <(echo "${SIGN_KEY}") -out ${f}.rsa_sha256 ${f}
done
fi
env:
SIGN_PASSPHRASE: ${{ secrets.SIGN_PASSPHRASE }}
SIGN_KEY: ${{ secrets.SIGN_KEY }}
- uses: actions/upload-artifact@v4
with:
path: ./artifacts
name: combined-artifacts
ftest:
name: Trigger functional tests
needs: [build, native_linux, native_mac, native_win, combine-artifacts]
runs-on: ubuntu-latest
steps:
# Previously we had a workflow_run trigger on functional-tests.yaml, but:
# - This approach allows for starting functional tests a bit earlier (no need to wait for Docker builds)
# - The workflow_run trigger executes the workflow file from the default branch, whereas this approach
# allows for running the workflow file from the same branch as the ci workflow.
- name: Check-out source code
uses: actions/checkout@v4
- run: |
# Use branch output from build job if available, otherwise fall back to GITHUB_REF
if [ -n "${{ needs.build.outputs.branch }}" ]; then
ref="${{ needs.build.outputs.branch }}"
else
ref="${GITHUB_REF}"
fi
gh workflow run functional-tests.yml --ref "${ref}" -f runId=${GITHUB_RUN_ID}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release:
name: release
if: needs.build.outputs.do_release
needs: [build, native_linux, native_mac, native_win, combine-artifacts]
runs-on: ubuntu-latest
steps:
- name: Check-out source code
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
name: combined-artifacts
- name: PROD - Prepare release PR
if: contains(github.ref, 'refs/heads/rel/')
uses: googleapis/release-please-action@v4
with:
skip-github-release: true
target-branch: ${{ github.ref_name }}
release-type: simple
- name: DEV - Prepare GitHub release
if: needs.build.outputs.do_dev_release
run: |
gh release delete ${{ needs.build.outputs.release_tag }} -y || true
gh release create ${{ needs.build.outputs.release_tag }} -p -t "Development Release - ${{ needs.build.outputs.branch }} branch" -n 'See `Assets` section below for latest build artifacts'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: DEV - Update ${{ needs.build.outputs.release_tag }} tag
uses: richardsimko/update-tag@v1
if: needs.build.outputs.do_dev_release
with:
tag_name: ${{ needs.build.outputs.release_tag }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload assets to release
if: needs.build.outputs.do_release
run: |
files=$(find "./artifacts/release-assets" -type f -printf "%p ")
gh release upload "${{ needs.build.outputs.release_tag }}" $files --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
semantic_versions:
name: Publish semantic versions
if: needs.build.outputs.do_prod_release
needs: [build, native_linux, native_mac, native_win, combine-artifacts, release]
strategy:
matrix:
semantic_version:
- v${{needs.build.outputs.version_major}}.${{needs.build.outputs.version_minor}}
- v${{needs.build.outputs.version_major}}
# TODO If we publish a feature/patch for an older major version,
# for example from the rel/v2.x branch,
# this may cause the older major version to be tagged as 'latest'
- latest
runs-on: ubuntu-latest
steps:
- name: Check-out source code
uses: actions/checkout@v4
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
name: combined-artifacts
- name: PROD - Publish ${{ matrix.semantic_version }} tag
uses: richardsimko/update-tag@v1
with:
tag_name: ${{ matrix.semantic_version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: PROD - Publish semantic release ${{ matrix.semantic_version }}
run: |
gh release delete ${{ matrix.semantic_version }} -y || true
gh release create ${{ matrix.semantic_version }} -t "${{ matrix.semantic_version }}" -n "Semantic version release for [${{ needs.build.outputs.release_tag }}](https://github.com/fortify/fcli/releases/tag/${{ needs.build.outputs.release_tag }})" --verify-tag --latest=false
files=$(find "./artifacts/release-assets" -type f -printf "%p ")
gh release upload "${{ matrix.semantic_version }}" $files --clobber
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
docker:
name: Trigger Docker builds
needs: [build, release, semantic_versions]
# Always run if build & release were successful, ignoring status of semantic_versions
if: always() && needs.build.result == 'success' && needs.release.result == 'success'
runs-on: ubuntu-latest
steps:
- name: Check-out source code
uses: actions/checkout@v4
- run: |
# Use branch output from build job if available, otherwise fall back to GITHUB_REF
if [ -n "${{ needs.build.outputs.branch }}" ]; then
ref="${{ needs.build.outputs.branch }}"
else
ref="${GITHUB_REF}"
fi
gh workflow run docker.yml --ref "${ref}" -f "releaseTag=${{ needs.build.outputs.release_tag }}" -f doPublish=${{ needs.build.outputs.do_release }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publishPages:
name: publishPages
if: needs.build.outputs.do_release
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Check-out existing docs from gh-pages branch
uses: actions/checkout@v4
with:
ref: gh-pages
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./tmp
name: build-output
- name: Update documentation from artifact
run: |
# Extract top-level documentation resources
# TODO Should we do this only when building a release, or also for dev_v3.x,
# or for all (dev & release) versions like we do now?
unzip -o tmp/docs-gh-pages-static.zip -d "."
# Define the output directory, based on tag/branch name
versionDir=${{ needs.build.outputs.release_tag }}
# Delete, recreate and fill the directory for the current tag/branch name,
# while leaving documentation for other tags/branches intact (as checked out above)
rm -rf "${versionDir}"
mkdir -p "${versionDir}"
unzip tmp/docs-gh-pages-versioned.zip -d "${versionDir}"
# Update symlinks based on available versions, processing versions in ascending order
# to replace previous links with a newer version if appropriate. For example, 'latest'
# will first point to the oldest version, then replaced with second-oldest version, ...,
# until it points to the latest version.
ls -d v*.*.* | sort -V | while read line; do
ln -sfT $line latest
ln -sfT $line $(echo "$line" | cut -d. -f 1) # v<major>
ln -sfT $line $(echo "$line" | cut -d. -f 1,2) # v<major>.<minor>
done
# Same for dev_*, but only generating latest_dev symlink
ls -d dev_* | sort -V | while read line; do
ln -sfT $line latest_dev
done
# Recreate version data files, which may be empty if no versions available
mkdir -p _data/versions
touch _data/versions/release.yml
touch _data/versions/dev.yml
ls -d v*.*.* | sort -rV | while read line; do echo "- '$line'"; done > _data/versions/release.yml
ls -d dev_* | sort | while read line; do echo "- '$line'"; done > _data/versions/dev.yml
git config user.name github-actions
git config user.email github-actions@fortify.com
git add .
git commit -m "Update documentation for ${{ needs.build.outputs.release_tag }}"
git push