Skip to content

Commit 2e2aa1c

Browse files
[CDRIVER-5537] A Signed Release Archive + Augmented SBOM Publication (#1637)
* New Earthly targets for release artifacts - +release-archive generates an archive of the repository that contains the augmented SBOM downloaded from Silk - +sign-file can sign arbitrary files - +signed-release generates release artifacts using +release-archive and +sign-file to create a release archive and detached signature. * Add documentation on using Earthly and the release targets * Document new release steps for a signed release
1 parent c2c0c82 commit 2e2aa1c

File tree

5 files changed

+480
-1
lines changed

5 files changed

+480
-1
lines changed

Earthfile

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,85 @@ multibuild:
171171
--c_compiler=gcc --c_compiler=clang \
172172
--test_mongocxx_ref=master
173173

174+
# release-archive :
175+
# Create a release archive of the source tree. (Refer to dev docs)
176+
release-archive:
177+
FROM alpine:3.20
178+
RUN apk add git
179+
ARG --required sbom_branch
180+
ARG --required prefix
181+
ARG --required ref
182+
WORKDIR /s
183+
COPY --dir .git .
184+
COPY (+sbom-download/augmented-sbom.json --branch=$sbom_branch) cyclonedx.sbom.json
185+
RUN git archive -o release.tar.gz \
186+
--verbose \
187+
--prefix="$prefix/" \ # Set the archive path prefix
188+
"$ref" \ # Add the source tree
189+
--add-file cyclonedx.sbom.json # Add the SBOM
190+
SAVE ARTIFACT release.tar.gz
191+
192+
# Obtain the signing public key. Exported as an artifact /c-driver.pub
193+
signing-pubkey:
194+
FROM alpine:3.20
195+
RUN apk add curl
196+
RUN curl --location --silent --fail "https://pgp.mongodb.com/c-driver.pub" -o /c-driver.pub
197+
SAVE ARTIFACT /c-driver.pub
198+
199+
# sign-file :
200+
# Sign an arbitrary file. This uses internal MongoDB tools and requires authentication
201+
# to be used to access them. (Refer to dev docs)
202+
sign-file:
203+
# Pull from Garasign:
204+
FROM artifactory.corp.mongodb.com/release-tools-container-registry-local/garasign-gpg
205+
# Copy the file to be signed
206+
ARG --required file
207+
COPY $file /s/file
208+
# Run the GPG signing command. Requires secrets!
209+
RUN --secret GRS_CONFIG_USER1_USERNAME --secret GRS_CONFIG_USER1_PASSWORD \
210+
gpgloader && \
211+
gpg --yes --verbose --armor --detach-sign --output=/s/signature.asc /s/file
212+
# Export the detatched signature
213+
SAVE ARTIFACT /s/signature.asc /
214+
# Verify the file signature against the public key
215+
COPY +signing-pubkey/c-driver.pub /s/
216+
RUN touch /keyring && \
217+
gpg --no-default-keyring --keyring /keyring --import /s/c-driver.pub && \
218+
gpgv --keyring=/keyring /s/signature.asc /s/file
219+
220+
# signed-release :
221+
# Generate a signed release artifact. Refer to the "Earthly" page of our dev docs for more information.
222+
# (Refer to dev docs)
223+
signed-release:
224+
FROM alpine:3.20
225+
RUN apk add git
226+
# We need to know which branch to get the SBOM from
227+
ARG --required sbom_branch
228+
# The version of the release. This affects the filepaths of the output and is the default for --ref
229+
ARG --required version
230+
# The Git revision of the repository to be archived. By default, archives the tag of the given version
231+
ARG ref=refs/tags/$version
232+
# File stem and archive prefix:
233+
LET stem="mongo-c-driver-$version"
234+
WORKDIR /s
235+
# Run the commands "locally" so that the files can be transferred between the
236+
# targets via the host filesystem.
237+
LOCALLY
238+
# Clean out a scratch space for us to work with
239+
LET rel_dir = ".scratch/release"
240+
RUN rm -rf -- "$rel_dir"
241+
# Primary artifact files
242+
LET rel_tgz = "$rel_dir/$stem.tar.gz"
243+
LET rel_asc = "$rel_dir/$stem.tar.gz.asc"
244+
# Make the release archive:
245+
COPY (+release-archive/release.tar.gz --branch=$sbom_branch --prefix=$stem --ref=$ref) $rel_tgz
246+
# Sign the release archive:
247+
COPY (+sign-file/signature.asc --file $rel_tgz) $rel_asc
248+
# Save them as an artifact.
249+
SAVE ARTIFACT $rel_dir /dist
250+
# Remove our scratch space from the host. Getting at the artifacts requires `earthly --artifact`
251+
RUN rm -rf -- "$rel_dir"
252+
174253
# This target is simply an environment in which the SilkBomb executable is available.
175254
silkbomb:
176255
FROM artifactory.corp.mongodb.com/release-tools-container-registry-public-local/silkbomb:1.0

docs/dev/conf.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
88

99
from pathlib import Path
10+
import re
11+
from typing import Callable
12+
13+
from sphinx import addnodes
14+
from sphinx.application import Sphinx
15+
from sphinx.environment import BuildEnvironment
1016

1117
THIS_FILE = Path(__file__).resolve()
1218
THIS_DIR = THIS_FILE.parent
@@ -23,6 +29,7 @@
2329
extensions = []
2430
templates_path = []
2531
exclude_patterns = []
32+
default_role = "any"
2633

2734
# -- Options for HTML output -------------------------------------------------
2835
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
@@ -35,3 +42,59 @@
3542
.. role:: bash(code)
3643
:language: bash
3744
"""
45+
46+
47+
def annotator(
48+
annot: str,
49+
) -> Callable[[BuildEnvironment, str, addnodes.desc_signature], str]:
50+
"""
51+
Create a parse_node function that adds a parenthesized annotation to an object signature.
52+
"""
53+
54+
def parse_node(
55+
env: BuildEnvironment, sig: str, signode: addnodes.desc_signature
56+
) -> str:
57+
signode += addnodes.desc_name(sig, sig)
58+
signode += addnodes.desc_sig_space()
59+
signode += addnodes.desc_annotation("", f"({annot})")
60+
return sig
61+
62+
return parse_node
63+
64+
65+
def parse_earthly_artifact(
66+
env: BuildEnvironment, sig: str, signode: addnodes.desc_signature
67+
) -> str:
68+
"""
69+
Parse and render the signature of an '.. earthly-artifact::' signature"""
70+
mat = re.match(r"(?P<target>\+.+?)(?P<path>/.*)$", sig)
71+
if not mat:
72+
raise RuntimeError(
73+
f"Invalid earthly-artifact signature: {sig!r} (expected “+<target>/<path> string)"
74+
)
75+
signode += addnodes.desc_addname(mat["target"], mat["target"])
76+
signode += addnodes.desc_name(mat["path"], mat["path"])
77+
signode += addnodes.desc_sig_space()
78+
signode += addnodes.desc_annotation("", "(Earthly Artifact)")
79+
return sig
80+
81+
82+
def setup(app: Sphinx):
83+
app.add_object_type( # type: ignore
84+
"earthly-target",
85+
"earthly-target",
86+
indextemplate="pair: earthly target; %s",
87+
parse_node=annotator("Earthly target"),
88+
)
89+
app.add_object_type( # type: ignore
90+
"script",
91+
"script",
92+
indextemplate="pair: shell script; %s",
93+
parse_node=annotator("shell script"),
94+
)
95+
app.add_object_type( # type: ignore
96+
"earthly-artifact",
97+
"earthly-artifact",
98+
indextemplate="pair: earthly artifact; %s",
99+
parse_node=parse_earthly_artifact,
100+
)

0 commit comments

Comments
 (0)