add a changelog #3355
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Build and test on each commit. Create a release on tagged commits. | |
# | |
# Binaries on each platform are stripped. This removes debug symbols. | |
name: Build | |
on: [push] | |
jobs: | |
build-all: | |
name: ${{ matrix.os-name }}-build | |
runs-on: ${{ matrix.os }} | |
container: ${{ matrix.container }} | |
env: | |
GHC_VERSION: '9.4.8' | |
defaults: | |
run: | |
shell: bash | |
strategy: | |
matrix: | |
os: ['windows-latest', 'ubuntu-latest', 'macos-12', 'macos-latest'] | |
include: | |
- os: ubuntu-latest | |
os-name: Linux | |
container: fossa/haskell-static-alpine:ghc-9.4.8 | |
project-file: cabal.project.ci.linux | |
ghc: '9.4.8' | |
# macos-latest pointed at macos-12 this before it was changed to ARM. | |
- os: macos-12 | |
os-name: macOS-intel | |
project-file: cabal.project.ci.macos | |
ghc: '9.4.8' | |
- os: windows-latest | |
os-name: Windows | |
project-file: cabal.project.ci.windows | |
ghc: '9.4.8' | |
- os: macos-latest | |
os-name: macOS-arm64 | |
project-file: cabal.project.ci.macos | |
ghc: '9.4.8' | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
lfs: true | |
fetch-depth: 2 | |
- name: Install MacOS binary dependencies | |
if: ${{ contains(matrix.os, 'macos') }} | |
run: | | |
brew install jq | |
# Set up Haskell. | |
- uses: haskell-actions/setup@v2 | |
id: setup-haskell | |
name: Setup ghc/cabal (non-alpine) | |
if: ${{ !contains(matrix.os, 'ubuntu') }} | |
with: | |
ghc-version: ${{ matrix.ghc }} | |
cabal-version: '3.10.2.1' | |
# Set up Rust. | |
# This action installs the 'minimal' profile. | |
- uses: dtolnay/rust-toolchain@stable | |
- name: Install additional Rust tooling | |
uses: taiki-e/install-action@v2 | |
with: | |
tool: nextest | |
- uses: Swatinem/rust-cache@v2 | |
- name: Debugging information | |
run: | | |
ghc --version || echo "no ghc" | |
cabal --version || echo "no cabal" | |
ghcup --version || echo "no ghcup" | |
rustc -V || echo "no rustc" | |
cargo -V || echo "no cargo" | |
# Linux builds are run in Alpine, which builds them statically; | |
# using jemalloc reduces the performance impact of this | |
# over the default libc allocator (which performs very poorly in static builds). | |
- name: Build Rust dependencies (Linux) | |
if: ${{ matrix.os-name == 'linux' }} | |
run: cargo build --features jemalloc --release | |
# Non-Linux environments generally don't need `jemalloc` | |
# and in particular Windows doesn't support it. | |
- name: Build Rust dependencies (non-Linux) | |
if: ${{ matrix.os-name != 'linux' }} | |
run: cargo build --release | |
# Run tests in release mode to reduce the need for rebuilds. | |
- name: Test Rust dependencies | |
run: cargo nextest run --release | |
- name: Ensure git ownership check does not lead to compiler error | |
run: git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
- name: Check git status and collect information | |
# https://github.com/actions/checkout/issues/760#issuecomment-1099519159 | |
run: | | |
git status --porcelain | |
echo "parent_commit=$(git rev-parse HEAD^)" >> $GITHUB_ENV | |
# Compute cache key and save to a temporary file. | |
# | |
# We compute the cache key based on the solved install plan instead of just | |
# hashing the `.cabal` file, since there are many kinds of changes that will | |
# cause `.cabal` to change (e.g. adding new source modules). | |
- name: Compute cache key | |
id: compute-cache-key | |
env: | |
RUNNER_OS: ${{ runner.os }} | |
run: | | |
cabal --project-file=${{ matrix.project-file }} update | |
cabal --project-file=${{ matrix.project-file }} build --dry-run | |
cat dist-newstyle/cache/plan.json | jq '."install-plan"[]."id"' | sort > /tmp/cabal-cache-key | |
echo "Install plan:" | |
cat /tmp/cabal-cache-key | |
if [ "$RUNNER_OS" = "macOS" ]; then | |
PLAN_SUM=$(shasum -a256 /tmp/cabal-cache-key) | |
else | |
PLAN_SUM=$(sha256sum /tmp/cabal-cache-key) | |
fi | |
export CABAL_CACHE_KEY=$(echo $PLAN_SUM | awk '{print $1}') | |
echo "Cabal cache key: $CABAL_CACHE_KEY" | |
echo "cabal-cache-key=$CABAL_CACHE_KEY" >> $GITHUB_OUTPUT | |
# Build FOSSA CLI. | |
- uses: actions/cache@v4 | |
name: Cache cabal store | |
with: | |
path: ${{ steps.setup-haskell.outputs.cabal-store || '~/.local/state/cabal' }} | |
key: ${{ matrix.os-name }}-${{ matrix.ghc }}-cabal-cache-${{ steps.compute-cache-key.outputs.cabal-cache-key }} | |
restore-keys: | | |
${{ matrix.os-name }}-${{ matrix.ghc }}-cabal-cache- | |
${{ matrix.os-name }}-${{ matrix.ghc }}- | |
${{ matrix.os-name }}- | |
- uses: actions/cache@v4 | |
name: Cache dist-newstyle | |
with: | |
path: ${{ github.workspace }}/dist-newstyle | |
key: ${{ matrix.os-name }}-${{ env.GHC_VERSION }}-dist-newstyle-${{ github.sha }} | |
restore-keys: | | |
${{ matrix.os-name }}-${{ env.GHC_VERSION }}-dist-newstyle-${{ env.parent_commit }} | |
${{ matrix.os-name }}-${{ env.GHC_VERSION }}-dist-newstyle- | |
${{ matrix.os-name }}-${{ env.GHC_VERSION }}- | |
${{ matrix.os-name }}- | |
- name: Update vendored binaries | |
run: | | |
mkdir vendor-bins | |
./vendor_download.sh | |
env: | |
GITHUB_TOKEN: ${{ secrets.BASIS_ACCESS_TOKEN }} | |
- name: Build test data | |
run: | | |
make build-test-data | |
- name: Build | |
# Occasionally, we run out of memory on the build process. | |
# Since cabal uses incremental compilation, we can retry from where we left off | |
# by simply re-running cabal if we fail. | |
env: | |
RUN_CMD: cabal build --project-file=${{ matrix.project-file }} all | |
run: | | |
: # With dist-newstyle caches: | |
: # Cabal mainly knows to recompile based on changes to files. | |
: # Tagging in git doesn't reliably change a file in a fixed location that cabal/GHC can track to indicate that there's a new tag. | |
: # For our release process, we merge to master, which builds (and may store a dist-newstyle cache), then push a release tag. | |
: # During the tag build, cabal/GHC may not realize that they have to rebuild the Version.hs file because the tag is invisible to it. | |
: # This line adds a unique comment to our version source file to prompt cabal/GHC to rebuild Version.hs unconditionally. | |
echo "{- $RANDOM$RANDOM$RANDOM -}" >> src/App/Version.hs | |
cabal update | |
$RUN_CMD || $RUN_CMD | |
- name: Run unit tests | |
run: | | |
cabal test --project-file=${{ matrix.project-file }} unit-tests | |
- name: Validate diagnose commands run on the platform | |
run: | | |
cargo run --bin diagnose -- walk --trace-spans none --trace-level info | |
# Test cabal-install | |
- name: Test Cabal Install (Linux) | |
if: ${{ contains(matrix.os, 'linux') }} | |
run: cabal install --overwrite-policy=always --project=${{ matrix.project-file }} --ghc-options="-Wwarn" | |
# Save artifacts. | |
- name: Find and move binaries (Windows) | |
if: ${{ contains(matrix.os, 'windows') }} | |
run: | | |
mkdir release | |
find . -type f -path '*/fossa/fossa.exe' -exec cp {} release \; | |
./release/fossa.exe --version | |
cp target/release/diagnose.exe release | |
cp target/release/millhone.exe release | |
- name: Find and move binaries (non-Windows) | |
if: ${{ !contains(matrix.os, 'windows') }} | |
run: | | |
mkdir release | |
find . -type f -path '*/fossa/fossa' -exec cp {} release \; | |
./release/fossa --version | |
cp target/release/diagnose release | |
cp target/release/millhone release | |
- name: Strip binaries | |
run: | | |
strip release/* | |
- name: Sign and Notarize Binaries (Mac OS) | |
if: ${{ contains(matrix.os, 'macos') && startsWith(github.ref, 'refs/tags/v') }} | |
env: | |
MACOS_BUILD_CERT_BASE64: ${{ secrets.MACOS_BUILD_CERT_BASE64 }} | |
MACOS_BUILD_CERT_P12_PASSWORD: ${{ secrets.MACOS_BUILD_CERT_P12_PASSWORD }} | |
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }} | |
APPLE_NOTARIZATION_DEV_PASS: ${{ secrets.APPLE_NOTARIZATION_DEV_PASS }} | |
APPLE_NOTARIZATION_DEV_ID: ${{ secrets.APPLE_NOTARIZATION_DEV_ID }} | |
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
OS_NAME: ${{ matrix.os-name }} | |
run: | | |
# create variables | |
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 | |
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db | |
# import certificate and provisioning profile from secrets | |
echo -n "$MACOS_BUILD_CERT_BASE64" | base64 --decode -o $CERTIFICATE_PATH | |
# create temporary keychain | |
security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH | |
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" $KEYCHAIN_PATH | |
# import certificate to keychain | |
security import $CERTIFICATE_PATH -P "$MACOS_BUILD_CERT_P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH | |
security list-keychain -d user -s $KEYCHAIN_PATH | |
chmod +x release/* | |
# '--options runtime' enables the hardened runtime: https://developer.apple.com/documentation/security/hardened_runtime | |
# On Apple Silicon there doesn't seem to be a default liblzma, and the one installed via homebrew is rejected. | |
# The entitlement line will relax that check. | |
# The hardened runtime is required for notarization. | |
if [ "$OS_NAME" = "macOS-arm64" ] ; then | |
codesign --entitlements .github/entitlements.plist --options runtime -s 'FOSSA, Inc.' release/fossa | |
else | |
# Intel does not need the entitlement to run liblzma, which is included in the base system. | |
codesign --options runtime -s 'FOSSA, Inc.' release/fossa | |
fi | |
codesign --options runtime -s 'FOSSA, Inc.' release/diagnose | |
codesign --options runtime -s 'FOSSA, Inc.' release/millhone | |
# Perform notarization | |
zip -rj notarization-archive.zip release | |
NOTARY_RESULTS=$(xcrun notarytool submit notarization-archive.zip --apple-id "$APPLE_NOTARIZATION_DEV_ID" --password "$APPLE_NOTARIZATION_DEV_PASS" --team-id "$APPLE_TEAM_ID" --wait) | |
echo "$NOTARY_RESULTS" | |
rm notarization-archive.zip | |
# The notarization tool doesn't set $?, so examine the output. | |
if ! echo "$NOTARY_RESULTS" | grep -q "status: Accepted"; then | |
exit 1 | |
fi | |
- uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ matrix.os-name }}-binaries | |
path: release | |
create-release: | |
name: create-release | |
runs-on: ubuntu-latest | |
needs: ['build-all'] | |
permissions: | |
id-token: write | |
contents: write | |
steps: | |
- uses: actions/download-artifact@v4 | |
# Sets VERSION from git's tag or sha. | |
# refer to: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#example-of-setting-an-output-parameter | |
- name: Get version | |
id: get-version | |
run: | | |
case $GITHUB_REF in | |
refs/tags/v*) | |
# This strips the 'v' prefix from the tag. | |
echo "VERSION=${GITHUB_REF/refs\/tags\/v/}" >> "$GITHUB_OUTPUT" | |
;; | |
*) | |
echo "VERSION=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" | |
;; | |
esac | |
- name: Check that version info was embedded correctly | |
if: ${{ startsWith(github.ref, 'refs/tags/v') }} | |
run: | | |
chmod +x Linux-binaries/fossa | |
echo $GITHUB_REF_NAME | |
echo $GITHUB_REF_TYPE | |
echo $GITHUB_SHA | |
echo ${GITHUB_SHA:0:12} | |
VERSION=$(echo $(Linux-binaries/fossa --version)) | |
EXPECTED="fossa-cli version ${{ steps.get-version.outputs.VERSION }} (revision ${GITHUB_SHA:0:12} compiled with ghc-9.4)" | |
echo " VERSION: $VERSION" | |
echo "EXPECTED: $EXPECTED" | |
[ "$GITHUB_REF_TYPE" = "tag" ] && echo "Ref type OK" | |
[ "$VERSION" = "$EXPECTED" ] && echo "CLI version OK" | |
- name: Install Cosign | |
if: ${{ github.ref_type == 'tag' }} | |
uses: sigstore/[email protected] | |
- name: Sign Release (Linux) | |
if: ${{ github.ref_type == 'tag' }} | |
run: | | |
cosign version | |
cosign sign-blob --yes --bundle "Linux-binaries/fossa.bundle" "Linux-binaries/fossa" | |
cosign sign-blob --yes --bundle "Linux-binaries/diagnose.bundle" "Linux-binaries/diagnose" | |
cosign sign-blob --yes --bundle "Linux-binaries/millhone.bundle" "Linux-binaries/millhone" | |
- name: Verify Signatures | |
if: ${{ github.ref_type == 'tag' }} | |
run: | | |
cosign verify-blob --bundle "Linux-binaries/fossa.bundle" --certificate-oidc-issuer "https://token.actions.githubusercontent.com" --certificate-identity "https://github.com/$GITHUB_WORKFLOW_REF" "Linux-binaries/fossa" | |
cosign verify-blob --bundle "Linux-binaries/diagnose.bundle" --certificate-oidc-issuer "https://token.actions.githubusercontent.com" --certificate-identity "https://github.com/$GITHUB_WORKFLOW_REF" "Linux-binaries/diagnose" | |
cosign verify-blob --bundle "Linux-binaries/millhone.bundle" --certificate-oidc-issuer "https://token.actions.githubusercontent.com" --certificate-identity "https://github.com/$GITHUB_WORKFLOW_REF" "Linux-binaries/millhone" | |
# This uses names compatible with our install script. | |
# | |
# Originally, CLI >=2.x Linux releases were only packaged as zip files, but | |
# we added tar.gz to improve compatibility. Our install script depends on | |
# the unzip command, which is not installed in most Linux distributions by | |
# default. To avoid breaking compatibility with older install scripts, we | |
# release both formats but default to using tar.gz when installing. | |
- name: Bundle binaries | |
env: | |
LINUX_FOSSA_TAR_PATH: "release/fossa_${{ steps.get-version.outputs.VERSION }}_linux_amd64.tar" | |
LINUX_FOSSA_ZIP_PATH: "release/fossa_${{ steps.get-version.outputs.VERSION }}_linux_amd64.zip" | |
LINUX_DIAGNOSE_TAR_PATH: "release/diagnose_${{ steps.get-version.outputs.VERSION }}_linux_amd64.tar" | |
LINUX_DIAGNOSE_ZIP_PATH: "release/diagnose_${{ steps.get-version.outputs.VERSION }}_linux_amd64.zip" | |
LINUX_MILLHONE_TAR_PATH: "release/millhone_${{ steps.get-version.outputs.VERSION }}_linux_amd64.tar" | |
LINUX_MILLHONE_ZIP_PATH: "release/millhone_${{ steps.get-version.outputs.VERSION }}_linux_amd64.zip" | |
run: | | |
mkdir release | |
ls -R | |
chmod +x Linux-binaries/* | |
zip -j "$LINUX_FOSSA_ZIP_PATH" Linux-binaries/fossa | |
zip -j "$LINUX_DIAGNOSE_ZIP_PATH" Linux-binaries/diagnose | |
zip -j "$LINUX_MILLHONE_ZIP_PATH" Linux-binaries/millhone | |
tar --create --verbose --file "$LINUX_FOSSA_TAR_PATH" --directory Linux-binaries fossa | |
tar --create --verbose --file "$LINUX_DIAGNOSE_TAR_PATH" --directory Linux-binaries diagnose | |
tar --create --verbose --file "$LINUX_MILLHONE_TAR_PATH" --directory Linux-binaries millhone | |
if [ "$GITHUB_REF_TYPE" = "tag" ]; then | |
tar --append --file "$LINUX_FOSSA_TAR_PATH" --directory Linux-binaries fossa.bundle | |
tar --append --file "$LINUX_DIAGNOSE_TAR_PATH" --directory Linux-binaries diagnose.bundle | |
tar --append --file "$LINUX_MILLHONE_TAR_PATH" --directory Linux-binaries millhone.bundle | |
zip -j "$LINUX_FOSSA_ZIP_PATH" Linux-binaries/fossa.bundle | |
zip -j "$LINUX_DIAGNOSE_ZIP_PATH" Linux-binaries/diagnose.bundle | |
zip -j "$LINUX_MILLHONE_ZIP_PATH" Linux-binaries/millhone.bundle | |
fi | |
gzip "$LINUX_FOSSA_TAR_PATH" | |
gzip "$LINUX_DIAGNOSE_TAR_PATH" | |
gzip "$LINUX_MILLHONE_TAR_PATH" | |
chmod +x macOS-intel-binaries/* | |
zip -j release/fossa_${{ steps.get-version.outputs.VERSION }}_darwin_amd64.zip macOS-intel-binaries/fossa | |
zip -j release/diagnose_${{ steps.get-version.outputs.VERSION }}_darwin_amd64.zip macOS-intel-binaries/diagnose | |
zip -j release/millhone_${{ steps.get-version.outputs.VERSION }}_darwin_amd64.zip macOS-intel-binaries/millhone | |
chmod +x macOS-arm64-binaries/* | |
zip -j release/fossa_${{ steps.get-version.outputs.VERSION }}_darwin_arm64.zip macOS-arm64-binaries/fossa | |
zip -j release/diagnose_${{ steps.get-version.outputs.VERSION }}_darwin_arm64.zip macOS-arm64-binaries/diagnose | |
zip -j release/millhone_${{ steps.get-version.outputs.VERSION }}_darwin_arm64.zip macOS-arm64-binaries/millhone | |
chmod +x Windows-binaries/* | |
zip -j release/fossa_${{ steps.get-version.outputs.VERSION }}_windows_amd64.zip Windows-binaries/fossa.exe | |
zip -j release/diagnose_${{ steps.get-version.outputs.VERSION }}_windows_amd64.zip Windows-binaries/diagnose.exe | |
zip -j release/millhone_${{ steps.get-version.outputs.VERSION }}_windows_amd64.zip Windows-binaries/millhone.exe | |
- name: Create checksums | |
# We have to run from within the release dir so that "release" isn't prepended to the relative path of the zip file. | |
run: | | |
cd release | |
sha256sum --binary "fossa_${{ steps.get-version.outputs.VERSION }}_linux_amd64.zip" > "fossa_${{ steps.get-version.outputs.VERSION }}_linux_amd64.zip.sha256" | |
sha256sum --binary "fossa_${{ steps.get-version.outputs.VERSION }}_linux_amd64.tar.gz" > "fossa_${{ steps.get-version.outputs.VERSION }}_linux_amd64.tar.gz.sha256" | |
sha256sum --binary "fossa_${{ steps.get-version.outputs.VERSION }}_darwin_amd64.zip" > "fossa_${{ steps.get-version.outputs.VERSION }}_darwin_amd64.zip.sha256" | |
sha256sum --binary "fossa_${{ steps.get-version.outputs.VERSION }}_darwin_arm64.zip" > "fossa_${{ steps.get-version.outputs.VERSION }}_darwin_arm64.zip.sha256" | |
sha256sum --binary "fossa_${{ steps.get-version.outputs.VERSION }}_windows_amd64.zip" > "fossa_${{ steps.get-version.outputs.VERSION }}_windows_amd64.zip.sha256" | |
echo "Sanity-checking the checksums." | |
cat *.sha256 | sha256sum --check --status | |
# Uploads the generated archives (tar.gz/zip) as build artifacts to allow | |
# verifying them without needing to do an actual release. This step does not | |
# need to run for tagged release versions. | |
- name: Upload release archives | |
if: ${{ !startsWith(github.ref, 'refs/tags/v') }} | |
uses: actions/upload-artifact@v4 | |
with: | |
name: release-archives | |
path: release | |
- name: Release | |
if: ${{ startsWith(github.ref, 'refs/tags/v') }} | |
uses: softprops/action-gh-release@v1 | |
with: | |
files: release/* | |
draft: true | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |