name: Install NSS description: Install NSS inputs: minimum-version: description: "Minimum required version of NSS" required: true target: description: "Target for cross-compilation" default: "" runs: using: composite steps: - name: Install system NSS (Linux) shell: bash if: ${{ runner.os == 'Linux' && inputs.target == '' && (runner.environment != 'self-hosted' || contains(runner.name, 'CodSpeed')) }} env: DEBIAN_FRONTEND: noninteractive run: | [ "$APT_UPDATED" ] || sudo apt-get update && echo "APT_UPDATED=1" >> "$GITHUB_ENV" sudo apt-get install -y --no-install-recommends libnss3-dev - name: Install system NSS (MacOS) shell: bash if: ${{ runner.os == 'MacOS' && inputs.target == '' }} run: | [ "$BREW_UPDATED" ] || brew update && echo "BREW_UPDATED=1" >> "$GITHUB_ENV" brew install nss - name: Check system NSS version id: system_nss env: MIN_VERSION: ${{ inputs.minimum-version }} shell: bash if: inputs.target == '' run: | if ! pkg-config --atleast-version "$MIN_VERSION" nss; then echo -n "System NSS needs ${MIN_VERSION}, got " pkg-config --modversion nss 2>/dev/null || echo "pkg-config error" exit 0 fi echo "System NSS is suitable: $NSS_VERSION" echo "suitable=1" >> "$GITHUB_OUTPUT" - name: Use sccache # Apparently the action can't be installed twice in the same workflow, so check if # it's already installed by checking if the SCCACHE_ENABLED environment variable is set # (which every "use" of this action needs to therefore set) # # Also, only enable sscache on our self-hosted runner, because the GitHub cache limit # is too small for this to be effective there. if: ${{ env.SCCACHE_ENABLED != '1' && !steps.system_nss.outputs.suitable && runner.environment == 'self-hosted' && !contains(runner.name, 'CodSpeed') }} uses: mozilla-actions/sccache-action@7d986dd989559c6ecdb630a3fd2557667be217ad # v0.0.9 - name: Enable sscache if: ${{ !steps.system_nss.outputs.suitable && runner.environment == 'self-hosted' && !contains(runner.name, 'CodSpeed') }} env: RUNNER_ENVIRONMENT: ${{ runner.environment }} RUNNER_OS: ${{ runner.os }} shell: bash run: | echo "SCCACHE_ENABLED=1" >> "$GITHUB_ENV" if [ "$RUNNER_OS" != "Windows" ]; then # TODO: Figure out how to make this work on Windows echo "SCCACHE_CC=sccache cc" >> "$GITHUB_ENV" echo "SCCACHE_CXX=sccache c++" >> "$GITHUB_ENV" fi echo "CMAKE_C_COMPILER_LAUNCHER=sccache" >> "$GITHUB_ENV" echo "CMAKE_CXX_COMPILER_LAUNCHER=sccache" >> "$GITHUB_ENV" echo "SCCACHE_GHA_ENABLED=true" >> "$GITHUB_ENV" - name: Retrieve NSS id: nss if: ${{ !steps.system_nss.outputs.suitable }} shell: bash env: NSS_VERSION: "3.120" # TODO: Bump this periodically; also in qns/Dockerfile. Trailing zeroes are stripped w/o quotes! run: | NSS_TAG="${NSS_VERSION//./_}" NSS_URL="https://ftp.mozilla.org/pub/security/nss/releases/NSS_${NSS_TAG}_RTM/src/nss-${NSS_VERSION}.tar.gz" echo "Retrieving NSS $NSS_VERSION from $NSS_URL" curl -L "$NSS_URL" | tar xz --strip-components=1 echo "version=$NSS_VERSION" >> "$GITHUB_OUTPUT" - name: Retrieve NSPR id: nspr if: ${{ !steps.system_nss.outputs.suitable }} shell: bash env: NSPR_VERSION: "4.38.2" # TODO: Bump this periodically; also in qns/Dockerfile. Trailing zeroes are stripped w/o quotes! run: | NSPR_URL="https://ftp.mozilla.org/pub/nspr/releases/v$NSPR_VERSION/src/nspr-$NSPR_VERSION.tar.gz" echo "Retrieving NSPR $NSPR_VERSION from $NSPR_URL" curl -L "$NSPR_URL" | tar xz --strip-components=1 echo "version=$NSPR_VERSION" >> "$GITHUB_OUTPUT" - name: Store Ubuntu release code name (Linux) id: ubuntu_release shell: bash if: ${{ runner.os == 'Linux' && !steps.system_nss.outputs.suitable }} run: | # Store Ubuntu release codename for use in cache key. . /etc/os-release echo "codename=-$UBUNTU_CODENAME" >> "$GITHUB_OUTPUT" # Use restore-only here so PRs don't create redundant caches. PRs restore # from main; only main saves new caches. This reduces churn and evictions. # Downside: PRs that change NSS version/build will rebuild on every CI run. - name: Restore NSS cache id: cache if: ${{ !steps.system_nss.outputs.suitable }} uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: dist key: nss-${{ inputs.target || runner.os }}${{ steps.ubuntu_release.outputs.codename }}-${{ runner.arch }}-${{ steps.nss.outputs.version }}-${{ steps.nspr.outputs.version }} - name: Check if build is needed id: check_build if: ${{ !steps.system_nss.outputs.suitable }} env: CACHE_HIT: ${{ steps.cache.outputs.cache-hit }} shell: bash run: | if [ "$CACHE_HIT" != "true" ]; then echo "Building NSS from source" echo "build_nss=1" >> "$GITHUB_OUTPUT" else echo "Using cached prebuilt NSS" fi - name: Install build dependencies (Linux) shell: bash if: ${{ runner.os == 'Linux' && steps.check_build.outputs.build_nss && (runner.environment != 'self-hosted' || contains(runner.name, 'CodSpeed')) }} env: DEBIAN_FRONTEND: noninteractive run: sudo apt-get install -y --no-install-recommends gyp ninja-build - name: Install build dependencies (MacOS) shell: bash if: ${{ runner.os == 'MacOS' && steps.check_build.outputs.build_nss }} run: | brew install ninja echo "gyp-next>=0.18.1" > req.txt python3 -m pip install --break-system-packages -r req.txt - name: Install build dependencies (Windows) shell: bash if: ${{ runner.os == 'Windows' && steps.check_build.outputs.build_nss }} run: | # shellcheck disable=SC2028 { echo C:/msys64/usr/bin echo C:/msys64/mingw64/bin } >> "$GITHUB_PATH" /c/msys64/usr/bin/pacman -S --noconfirm python3-pip nsinstall echo "gyp-next>=0.18.1" > req.txt python3 -m pip install -r req.txt - name: Set up MSVC (Windows) if: ${{ runner.os == 'Windows' && steps.check_build.outputs.build_nss }} uses: ilammy/msvc-dev-cmd@v1 # zizmor: ignore[unpinned-uses] # TODO: Would like to pin this, but the Mozilla org allowlist requires "ilammy/msvc-dev-cmd@v1*" # uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 - name: Set up build environment (Windows) shell: bash if: ${{ runner.os == 'Windows' && steps.check_build.outputs.build_nss }} run: | { echo "GYP_MSVS_OVERRIDE_PATH=$VSINSTALLDIR" echo "GYP_MSVS_VERSION=2022" echo "BASH=$SHELL" } >> "$GITHUB_ENV" # See https://github.com/ilammy/msvc-dev-cmd#name-conflicts-with-shell-bash rm /usr/bin/link.exe || true - name: Set up environment shell: bash if: ${{ !steps.system_nss.outputs.suitable }} env: NSS_DIR: ${{ github.workspace }}/nss RUNNER_OS: ${{ runner.os }} WORKSPACE: ${{ github.workspace }} run: | # zizmor: ignore[github-env] We need to write to GITHUB_PATH on Windows. NSS_OUT="$WORKSPACE/dist/Release" { echo "LD_LIBRARY_PATH=$NSS_OUT/lib" echo "DYLD_FALLBACK_LIBRARY_PATH=$NSS_OUT/lib" echo "NSS_DIR=$NSS_DIR" echo "NSS_PREBUILT=1" } >> "$GITHUB_ENV" if [ "$RUNNER_OS" == "Windows" ]; then echo "$NSS_OUT/lib" >> "$GITHUB_PATH" fi - name: Build shell: bash if: ${{ steps.check_build.outputs.build_nss }} env: TARGET_PLATFORM: ${{ inputs.target }} RUNNER_OS: ${{ runner.os }} run: | # We want to do an optimized build for accurate CPU profiling, but # we also want debug symbols and frame pointers for that, which the normal optimized NSS # build process doesn't provide. [ "$RUNNER_OS" != "Windows" ] && export CFLAGS="-ggdb3 -fno-omit-frame-pointer" if [[ $TARGET_PLATFORM == *-android* ]]; then for file in build-nss-android.sh build-android-common.sh; do curl -o "$file" -sSf "https://raw.githubusercontent.com/mozilla/application-services/refs/tags/v137.0/libs/$file" chmod +x "$file" done # See https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md#android ANDROID_NDK_VERSION=$(basename "$ANDROID_NDK" | cut -d. -f1) # See https://github.com/mozilla/application-services/blob/46cacda811da094653dc8e93158956f4cd57e87a/libs/build-all.sh#L89-L102 # It figures that NSPR would require monkey-patching to build on Android. sed -i'' 's/if test -z "$android_ndk" ; then/$as_echo "#define ANDROID 1" >>confdefs.h\n ;;\nunreachable)\n if test -z "$android_ndk" ; then/g' nspr/configure ./build-nss-android.sh "$(pwd)" "/tmp/dist" "$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64" "$TARGET_PLATFORM" "$ANDROID_NDK_VERSION" # Manually move the temporary build directory to the final location, which is what neqo-crypto expects. find /tmp/tmp.* > tmp CERTUTIL="$(grep certutil tmp)" TARGET_DIR="$(dirname $(dirname $CERTUTIL))" mkdir -p "dist/Release" cp -vaL "$TARGET_DIR"/* "dist/Release/" NSPR_H="$(grep nspr.h tmp)" INCLUDE_DIR="$(dirname $NSPR_H)" mkdir -p "dist/Release/include/nspr" cp -vaL "$INCLUDE_DIR"/* "dist/Release/include/nspr" CHACHA="$(grep chacha20poly1305.h tmp)" PRIVATE_DIR="$(dirname $(dirname $CHACHA))" mkdir -p "dist/private" cp -vaL "$PRIVATE_DIR"/* "dist/private/" UTILRENAME="$(grep utilrename.h tmp)" PUBLIC_DIR="$(dirname $(dirname $(dirname $UTILRENAME)))" mkdir -p "dist/public" cp -vaL "$PUBLIC_DIR"/* "dist/" LIBNSPR4="$(grep lib/libnspr4.a tmp)" LIB_DIR="$(dirname $LIBNSPR4)" mkdir -p "dist/Release/lib" cp -vaL "$LIB_DIR"/* "dist/Release/lib" else [ "$SCCACHE_CC" ] && [ "$SCCACHE_CXX" ] && export CC="$SCCACHE_CC" CXX="$SCCACHE_CXX" $NSS_DIR/build.sh -g -Ddisable_tests=1 -Ddisable_dbm=1 -Ddisable_libpkix=1 -Ddisable_ckbi=1 -Ddisable_fips=1 --opt --static fi - name: Save NSS cache if: ${{ steps.check_build.outputs.build_nss && github.event_name != 'pull_request' }} uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: dist key: nss-${{ inputs.target || runner.os }}${{ steps.ubuntu_release.outputs.codename }}-${{ runner.arch }}-${{ steps.nss.outputs.version }}-${{ steps.nspr.outputs.version }}