Skip to content

Commit 9b72f0c

Browse files
authored
MONGOCRYPT-841 add signed binaries for Github release (#1145)
* add `upload-release` tasks * update release steps * note RHEL 6.2 is deprecated RHEL 6.2 builds will be dropped, but consumers can migrate to nocrypto builds with equivalent glibc requirements
1 parent 055811f commit 9b72f0c

5 files changed

Lines changed: 221 additions & 5 deletions

File tree

.evergreen/build_all.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ set -eu
1515
: "${LIBMONGOCRYPT_COMPILE_FLAGS:=}"
1616
# Additional CMake flags that apply only to the libmongocrypt build. (Used by the C driver)
1717
: "${LIBMONGOCRYPT_EXTRA_CMAKE_FLAGS:=}"
18+
# release_os_arch is set for release builds.
19+
: "${release_os_arch:=}"
1820

1921
# Control the build configuration that is generated.
2022
export CMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-RelWithDebInfo}"
@@ -90,6 +92,27 @@ if [ "$CONFIGURE_ONLY" ]; then
9092
fi
9193
echo "Installing libmongocrypt"
9294
_cmake_with_env --build "$BINARY_DIR" --target install
95+
96+
# If release_os_arch names a minimum glibc requirement (e.g. "linux-x86_64-glibc_2_17-nocrypto"),
97+
# verify it matches the maximum glibc symbol used.
98+
if [[ "$release_os_arch" == *glibc* ]]; then
99+
expected_glibc=$(echo "$release_os_arch" | sed -r 's/.*glibc_([0-9]+)_([0-9]+).*/\1.\2/')
100+
if [ -f "$CMAKE_INSTALL_PREFIX/lib64/libmongocrypt.so" ]; then
101+
check_lib="$CMAKE_INSTALL_PREFIX/lib64/libmongocrypt.so"
102+
elif [ -f "$CMAKE_INSTALL_PREFIX/lib/libmongocrypt.so" ]; then
103+
check_lib="$CMAKE_INSTALL_PREFIX/lib/libmongocrypt.so"
104+
else
105+
echo "glibc version check failed: libmongocrypt.so not found under $CMAKE_INSTALL_PREFIX"
106+
exit 1
107+
fi
108+
actual_glibc=$(objdump -T "$check_lib" | grep 'GLIBC_' | sed -r -e 's/.*GLIBC_([0-9.]+).*/\1/' | sort -u | tail -1)
109+
if [ "$actual_glibc" != "$expected_glibc" ]; then
110+
echo "glibc version check failed: release_os_arch requires glibc $expected_glibc but library uses glibc $actual_glibc"
111+
exit 1
112+
fi
113+
echo "glibc version check passed: $actual_glibc"
114+
fi
115+
93116
run_chdir "$BINARY_DIR" run_ctest
94117

95118
# MONGOCRYPT-372, ensure macOS universal builds contain both x86_64 and arm64 architectures.

.evergreen/config.yml

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ functions:
117117
export VS_VERSION=${vs_version|}
118118
export VS_TARGET_ARCH=${vs_target_arch|amd64}
119119
export CMAKE_GENERATOR=${CMAKE_GENERATOR|Ninja}
120+
export release_os_arch=${release_os_arch}
120121
env ${compile_env|} \
121122
bash "$EVG_DIR/env-run.sh" \
122123
bash "$EVG_DIR/build_all.sh"
@@ -628,7 +629,7 @@ tasks:
628629
commands:
629630
- func: "download and merge python releases"
630631

631-
- name: upload-java
632+
- name: upload-java # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks.
632633
depends_on:
633634
- variant: rhel-62-64-bit
634635
name: build-and-test-and-upload
@@ -708,7 +709,7 @@ tasks:
708709
filenames:
709710
- libmongocrypt-java-${tag_upload_location!|*revision}.tar.gz
710711

711-
- name: upload-all
712+
- name: upload-all # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks.
712713
depends_on:
713714
- variant: ubuntu1604
714715
name: build-and-test-and-upload
@@ -947,7 +948,7 @@ tasks:
947948
filenames:
948949
- libmongocrypt-all-${tag_upload_location!|*revision}.tar.gz
949950

950-
- name: sign-all
951+
- name: sign-all # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks.
951952
patchable: false # Garasign credentials are marked as "Admin only" in Evergreen project. "Admin only" variables are not included in patch builds. To test a patch: temporarily unselect "Admin only".
952953
depends_on: upload-all
953954
commands:
@@ -1044,7 +1045,7 @@ tasks:
10441045
- func: "fetch source"
10451046
- func: "setup packaging credentials"
10461047
- func: "publish packages"
1047-
- name: windows-upload-release
1048+
- name: windows-upload-release # TODO(MONGOCRYPT-894) remove in favor of `upload-release` tasks.
10481049
patchable: false # Garasign credentials are marked as "Admin only" in Evergreen and are not included in patch builds.
10491050
depends_on:
10501051
- variant: windows-test
@@ -1162,6 +1163,98 @@ tasks:
11621163
filenames:
11631164
- libmongocrypt-windows-x86_64-${tag_upload_location!|*revision}.tar.gz
11641165

1166+
- name: upload-release
1167+
run_on: ubuntu2404-latest-small
1168+
patchable: false # Garasign credentials are marked as "Admin only" in Evergreen and are not included in patch builds. To test a patch: temporarily unselect "Admin only".
1169+
commands:
1170+
- func: "fetch source"
1171+
- command: s3.get # Download build.
1172+
params:
1173+
role_arn: ${upload_arn}
1174+
remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${libmongocrypt_s3_suffix}/libmongocrypt.tar.gz'
1175+
bucket: ${upload_bucket}
1176+
extract_to: libmongocrypt_download
1177+
- command: shell.exec
1178+
params:
1179+
shell: bash
1180+
script: |-
1181+
set -o xtrace
1182+
set -o errexit
1183+
1184+
# Move just the mongocrypt files needed into the final upload
1185+
mkdir libmongocrypt_upload
1186+
1187+
if [[ "${release_os_arch}" == *"nocrypto"* ]]; then
1188+
# Publish libmongocrypt library without crypto dependency.
1189+
srcdir="libmongocrypt_download/nocrypto"
1190+
else
1191+
srcdir="libmongocrypt_download"
1192+
fi
1193+
1194+
# Move headers
1195+
mkdir libmongocrypt_upload/include
1196+
mv $srcdir/include/mongocrypt libmongocrypt_upload/include
1197+
1198+
# Move library
1199+
if [ -f "$srcdir/lib64/libmongocrypt.so" ]; then
1200+
mkdir libmongocrypt_upload/lib64
1201+
mv $srcdir/lib64/libmongocrypt.so libmongocrypt_upload/lib64
1202+
elif [ -f "$srcdir/lib/libmongocrypt.so" ]; then
1203+
mkdir libmongocrypt_upload/lib
1204+
mv $srcdir/lib/libmongocrypt.so libmongocrypt_upload/lib
1205+
elif [ -f "$srcdir/lib/libmongocrypt.dylib" ]; then
1206+
mkdir libmongocrypt_upload/lib
1207+
mv $srcdir/lib/libmongocrypt.dylib libmongocrypt_upload/lib
1208+
elif [ -f "$srcdir/bin/libmongocrypt.dll" ]; then
1209+
mkdir libmongocrypt_upload/bin
1210+
mv $srcdir/bin/libmongocrypt.dll libmongocrypt_upload/bin
1211+
fi
1212+
- command: archive.targz_pack
1213+
params:
1214+
target: libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz
1215+
source_dir: libmongocrypt_upload
1216+
include: [./**]
1217+
- command: s3.put # Upload tarball for GitHub Release.
1218+
params:
1219+
role_arn: ${upload_arn}
1220+
skip_existing: true
1221+
remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${revision}/${version_id}/libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz'
1222+
display_name: libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz
1223+
bucket: ${upload_bucket}
1224+
permissions: ${upload_permissions}
1225+
visibility: ${upload_visibility}
1226+
local_file: 'libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz'
1227+
content_type: 'application/x-gzip'
1228+
- command: shell.exec
1229+
params:
1230+
shell: bash
1231+
script: |-
1232+
set -o errexit
1233+
# Copy file to sign into `libmongocrypt` directory to be used by Earthly.
1234+
cp libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz libmongocrypt
1235+
- func: "earthly" # Sign tarball.
1236+
vars:
1237+
args: --secret garasign_username=${garasign_username} --secret garasign_password=${garasign_password} +sign --file_to_sign=libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz --output_file=libmongocrypt_upload.asc
1238+
- command: s3.put # Upload signature for GitHub Release.
1239+
params:
1240+
role_arn: ${upload_arn}
1241+
skip_existing: true
1242+
remote_file: 'libmongocrypt/${build_variant}/${branch_name}/${revision}/${version_id}/libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.asc'
1243+
display_name: libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.asc
1244+
bucket: ${upload_bucket}
1245+
permissions: ${upload_permissions}
1246+
visibility: ${upload_visibility}
1247+
local_file: 'libmongocrypt/libmongocrypt_upload.asc'
1248+
content_type: 'application/pgp-signature'
1249+
- command: papertrail.trace
1250+
params:
1251+
key_id: ${papertrail_key_id}
1252+
secret_key: ${papertrail_secret_key}
1253+
product: ${papertrail_project} # libmongocrypt-dev or libmongocrypt-release
1254+
version: ${tag_upload_location!|*revision} # Use ${tag_upload_location} if non-empty, otherwise ${revision}.
1255+
filenames:
1256+
- libmongocrypt-${release_os_arch}-${tag_upload_location!|*revision}.tar.gz
1257+
11651258
- name: debian-package-build
11661259
run_on: &deb-package-build-run_on
11671260
- ubuntu2004
@@ -1419,13 +1512,16 @@ buildvariants:
14191512
expansions:
14201513
packager_distro: rhel72
14211514
packager_arch: s390x
1515+
release_os_arch: linux-s390x-glibc_2_7-nocrypto
14221516
tasks:
14231517
- build-and-test-and-upload
14241518
- build-and-test-shared-bson
14251519
- build-packages
14261520
- name: publish-packages
14271521
distros:
14281522
- rhel70-small
1523+
- name: upload-release
1524+
depends_on: build-and-test-and-upload
14291525
- name: rhel83-zseries
14301526
# rhel83-zseries has a new enough g++ to build the C++ tests.
14311527
# rhel72-zseries-test does not build the C++ tests.
@@ -1439,9 +1535,12 @@ buildvariants:
14391535
expansions:
14401536
vs_version: "15"
14411537
vs_target_arch: amd64
1538+
release_os_arch: windows-x86_64
14421539
tasks:
14431540
- build-and-test-and-upload
14441541
- build-and-test-shared-bson
1542+
- name: upload-release
1543+
depends_on: build-and-test-and-upload
14451544
- name: windows-test-python
14461545
display_name: "Windows Python"
14471546
run_on: windows-64-vsMulti-small
@@ -1600,26 +1699,32 @@ buildvariants:
16001699
expansions:
16011700
packager_distro: rhel70
16021701
packager_arch: x86_64
1702+
release_os_arch: linux-x86_64-glibc_2_7-nocrypto
16031703
tasks:
16041704
- build-and-test-and-upload
16051705
- build-and-test-shared-bson
16061706
- build-packages
16071707
- name: publish-packages
16081708
distros:
16091709
- rhel70-small
1710+
- name: upload-release
1711+
depends_on: build-and-test-and-upload
16101712
- name: rhel-71-ppc64el
16111713
display_name: "RHEL 7.1 ppc64el"
16121714
run_on: rhel71-power8-test
16131715
expansions:
16141716
packager_distro: rhel71
16151717
packager_arch: ppc64le
1718+
release_os_arch: linux-ppc64le-glibc_2_17-nocrypto
16161719
tasks:
16171720
- build-and-test-and-upload
16181721
- build-and-test-shared-bson
16191722
- build-packages
16201723
- name: publish-packages
16211724
distros:
16221725
- rhel70-small
1726+
- name: upload-release
1727+
depends_on: build-and-test-and-upload
16231728
- name: rhel-80-64-bit
16241729
display_name: "RHEL 8.0 64-bit"
16251730
run_on: rhel80-test
@@ -1641,6 +1746,7 @@ buildvariants:
16411746
expansions:
16421747
packager_distro: rhel82
16431748
packager_arch: aarch64
1749+
release_os_arch: linux-arm64-glibc_2_17-nocrypto
16441750
tasks:
16451751
- build-and-test-and-upload
16461752
- test-python
@@ -1649,6 +1755,8 @@ buildvariants:
16491755
- name: publish-packages
16501756
distros:
16511757
- rhel70-small
1758+
- name: upload-release
1759+
depends_on: build-and-test-and-upload
16521760
- name: rhel-81-ppc64el
16531761
display_name: "RHEL 8.1 ppc64el"
16541762
run_on: rhel81-power8-small
@@ -1886,10 +1994,13 @@ buildvariants:
18861994
CMAKE=/opt/homebrew/bin/cmake
18871995
# Disable Ninja to work around error "Bad CPU type in executable"
18881996
CMAKE_GENERATOR: ''
1997+
release_os_arch: macos-universal
18891998
tasks:
18901999
- build-and-test-and-upload
18912000
- name: test-python
18922001
distros: macos-14-arm64
2002+
- name: upload-release
2003+
depends_on: build-and-test-and-upload
18932004
- name: windows-vs2017-32bit
18942005
# Test Windows 32 bit builds for PHPC. PHPC builds libmongocrypt from source. See MONGOCRYPT-391.
18952006
display_name: "Windows VS 2017 32-bit compile"
@@ -1912,9 +2023,12 @@ buildvariants:
19122023
display_name: "Alpine Linux 3.18 amd64 (via Earthly)"
19132024
expansions:
19142025
earthly_env: alpine
2026+
release_os_arch: linux-x86_64-musl_1_2-nocrypto
19152027
tasks:
19162028
- name: build-with-earthly
19172029
run_on: ubuntu2204-small
2030+
- name: upload-release
2031+
depends_on: build-with-earthly
19182032

19192033
- name: alpine-arm64-earthly
19202034
display_name: "Alpine Linux 3.18 arm64 (via Earthly)"

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22

33
## Unreleased
44

5+
### Added
6+
- Signed binaries for macOS and Linux are now available on the GitHub release.
7+
- Linux binaries including `nocrypto` in the name have no dependency on OpenSSL. Drivers using the `nocrypto` variant are expected to set crypto callbacks (e.g. call `mongocrypt_setopt_crypto_hooks`) to do operations requiring crypto to avoid an error.
8+
- Drivers that package libmongocrypt binaries are encouraged to migrate release scripts to use these binaries.
9+
- No reduction in platform support is expected. glibc dependencies were checked against existing builds on RHEL 6.2 and Ubuntu 16.04.
510

611
### Changed
712

813
- Final release packages in the PPA are now available by specifying `release` in the repository configuration in place of the major/minor version (e.g., `1.17`). Details in `README.md`.
914

15+
### Deprecated
16+
- RHEL 6.2 builds are deprecated and may be removed in the future. The `linux-x86_64-glibc_2_7-nocrypto` release build may be used instead and has equivalent glibc requirements.
17+
1018
### Removed
1119

1220
- The configure-time CMake parameter `ENABLE_WINDOWS_STATIC_RUNTIME` has been

doc/releasing.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ Do the following when releasing:
8080
- All `publish-packages` tasks.
8181
- If the `publish-packages` tasks fail with an error like `[curator] 2024/01/02 13:56:17 [p=emergency]: problem submitting repobuilder job: 404 (Not Found)`, this suggests the published path does not yet exist. Barque (the Linux package publishing service) has protection to avoid unintentional publishes. File a DEVPROD ticket ([example](https://jira.mongodb.org/browse/DEVPROD-15320)) and assign to the team called Release Infrastructure to request the path be created. Then re-run the failing `publish-packages` task. Ask in the slack channel `#ask-devprod-release-tools` for further help with `Barque` or `curator`.
8282
- Create the release from the GitHub releases page from the new tag.
83-
- Attach the tarball and signature file from the Files tab of the `windows-upload-release` task. [Example](https://github.com/mongodb/libmongocrypt/releases/tag/1.13.0).
83+
- Attach the tarball and signature files from the `upload-release` tasks. Use `etc/download-artifacts.py` to download all such files. Obtain the version ID from the Evergreen URL:
84+
```bash
85+
# Example: the Evergreen URL: https://spruce.corp.mongodb.com/version/69cfada41e1f8400073c971e has VERSION_ID=69cfada41e1f8400073c971e
86+
# Downloads to _build/artifacts
87+
uv run etc/download-artifacts.py ${VERSION_ID:?}
88+
```
8489
- Attach the Augmented SBOM file to the release as `cyclonedx.augmented.sbom.json`.
8590
Download the Augmented SBOM from a recent execution of the `sbom` task in an Evergreen patch or commit build.
8691
- Attach `etc/third_party_vulnerabilities.md` to the release.

etc/download-artifacts.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Download libmongocrypt artifacts to upload to a GitHub release.
2+
#
3+
# /// script
4+
# dependencies = [
5+
# "pyyaml",
6+
# "requests",
7+
# ]
8+
# ///
9+
10+
import argparse
11+
import requests
12+
import yaml
13+
from pathlib import Path
14+
from urllib.parse import urlsplit
15+
16+
17+
def download_file(url: str):
18+
url_parts = urlsplit(url)
19+
path = Path(url_parts.path)
20+
filename = path.name
21+
destination_dir = Path("_build/artifacts")
22+
destination_dir.mkdir(exist_ok=True, parents=True)
23+
destination = destination_dir / filename
24+
print(f"Downloading {url} to {destination}")
25+
with requests.get(url, stream=True) as resp:
26+
with destination.open("wb") as file:
27+
for chunk in resp.iter_content(chunk_size=8192):
28+
file.write(chunk)
29+
30+
31+
def main():
32+
parser = argparse.ArgumentParser(description="Download release artifacts from an Evergreen version")
33+
parser.add_argument("version_id", help="Evergreen version ID. (e.g. https://evergreen.mongodb.com/version/<version_id>)")
34+
args = parser.parse_args()
35+
36+
version_id = args.version_id
37+
38+
# Get Evergreen API credentials:
39+
path: Path = Path().home() / ".evergreen.yml"
40+
with path.open() as file:
41+
evg_settings = yaml.load(file, Loader=yaml.FullLoader)
42+
43+
api_server_host = "https://evergreen.mongodb.com/rest/v2"
44+
api_key = evg_settings["api_key"]
45+
api_user = evg_settings["user"]
46+
headers = {'Api-User': api_user, 'Api-Key': api_key}
47+
48+
resp: requests.Response = requests.get(
49+
f"{api_server_host}/versions/{version_id}/builds",
50+
params={"include_task_info": "true"},
51+
headers=headers)
52+
builds = resp.json()
53+
54+
for build in builds:
55+
for task_info in build["task_cache"]:
56+
if task_info["display_name"] == "upload-release":
57+
task_id = task_info["id"]
58+
resp = requests.get(
59+
f"{api_server_host}/tasks/{task_id}", headers=headers)
60+
task = resp.json()
61+
for artifact in task["artifacts"]:
62+
download_file(artifact["url"])
63+
64+
65+
if __name__ == "__main__":
66+
main()

0 commit comments

Comments
 (0)