-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add SPDX SBOM generation option #5
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
name: "CI" | ||
on: [push, pull_request] | ||
|
||
jobs: | ||
bats: | ||
name: bats | ||
runs-on: ubuntu-latest | ||
steps: | ||
|
||
- name: Setup bats | ||
uses: mig4/setup-bats@v1 | ||
with: | ||
bats-version: 1.9.0 | ||
|
||
- name: Check out code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Configure git (user name) | ||
run: git config --global user.name "Decomposer (CI)" | ||
|
||
- name: Configure git (user email) | ||
run: git config --global user.email "[email protected]" | ||
|
||
- name: Configure git (default branch) | ||
run: git config --global init.defaultBranch master | ||
|
||
- name: Run bats | ||
run: bats --print-output-on-failure -r tests | ||
|
||
jsonschema: | ||
name: jsonschema | ||
runs-on: ubuntu-latest | ||
env: | ||
DECOMPOSER_TARGET_DIR: /tmp/decomposer | ||
steps: | ||
|
||
- name: Check out code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Install JSON schema | ||
run: pip install check-jsonschema | ||
|
||
- name: Fetch decomposer.json from Lunr | ||
run: curl -o decomposer.json https://raw.githubusercontent.com/lunr-php/lunr/HEAD/decomposer.json | ||
|
||
- name: Create target dir | ||
run: mkdir -p "$DECOMPOSER_TARGET_DIR" | ||
|
||
- name: Install libraries | ||
run: bin/decomposer install | ||
|
||
- name: Generate SBOM | ||
run: bin/decomposer sbom | ||
|
||
- name: Validate | ||
run: check-jsonschema --schemafile https://raw.githubusercontent.com/spdx/spdx-spec/v2.3/schemas/spdx-schema.json decomposer.sbom.json |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#!/usr/bin/env bash | ||
|
||
DECOMPOSER_TARGET_DIR=${DECOMPOSER_TARGET_DIR:-/var/www/libs/} | ||
|
||
TMP_FILE=$( mktemp ) | ||
trap 'rm "${TMP_FILE}"' EXIT | ||
|
||
SBOM_FILE=${CWD}/decomposer.sbom.json | ||
NAMESPACE="$(git remote get-url origin)#$(git rev-parse HEAD)/$(basename $SBOM_FILE)" | ||
|
||
parse_arguments() { | ||
while [ "$#" -gt 0 ]; do | ||
case "$1" in | ||
"-f" | "--file") | ||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | ||
case "$2" in | ||
/*) SBOM_FILE="$2" ;; | ||
*) SBOM_FILE="${CWD}/$2" ;; | ||
esac | ||
shift | ||
fi | ||
;; | ||
*) | ||
printf "Unknown option %s\n" "$1" | ||
if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then | ||
shift | ||
fi | ||
;; | ||
esac | ||
shift | ||
done | ||
} | ||
|
||
validate_arguments() { | ||
if ! [ -f "${CWD}/decomposer.json" ]; then | ||
exit_error 'No decomposer.json found.' | ||
fi | ||
|
||
if [ -f "${SBOM_FILE}" ]; then | ||
printf 'Backing up old SBOM...' | ||
mv "${SBOM_FILE}" "${SBOM_FILE}.backup" | ||
|
||
case "$?" in | ||
0) printf 'done\n' ;; | ||
1) printf 'failed (%s)\n' "${status_text}" ;; | ||
2) printf 'skipped (%s)\n' "${status_text}" ;; | ||
esac | ||
fi | ||
|
||
# Clear SBOM and test write | ||
if ! printf '' 2> /dev/null > "${SBOM_FILE}"; then | ||
exit_error "File '${SBOM_FILE}' is not writable." | ||
fi | ||
} | ||
|
||
process_file() { | ||
local file="$1" | ||
|
||
printf '{' > "${TMP_FILE}" | ||
map_libraries_object "${file}" process_library | ||
printf '}' >> "${TMP_FILE}" | ||
|
||
echo '{}' | | ||
jq --argjson packages "$(sed 's/,}$/}/' "${TMP_FILE}")" \ | ||
--arg name $(basename "${CWD}") \ | ||
--arg version "${DECOMPOSER_VERSION}" \ | ||
--arg namespace "$NAMESPACE" \ | ||
--from-file "${DECOMPOSER_ROOT}/share/decomposer/sbom.jq" > "${SBOM_FILE}" | ||
} | ||
|
||
process_library() { | ||
local name="$1" | ||
local object="$2" | ||
|
||
local status_text | ||
|
||
printf 'Processing %s...' "${name}" | ||
|
||
write_locked_library "${name}" "${object}" status_text | ||
|
||
case "$?" in | ||
0) printf 'done\n' ;; | ||
1) printf 'failed (%s)\n' "${status_text}" ;; | ||
2) printf 'skipped (%s)\n' "${status_text}" ;; | ||
esac | ||
} | ||
|
||
write_locked_library() { | ||
local name="$1" | ||
local object="$2" | ||
local status_text_variable="$3" | ||
|
||
local revision version url library_target_dir | ||
|
||
version=$( jq -r '.version' <<< "${object}" ) | ||
revision=$( jq -r '.revision//.version' <<< "${object}" ) | ||
url=$( jq -r '.url' <<< "${object}" ) | ||
|
||
library_target_dir=$( jq -r '."target-dir"' <<< "${object}" ) | ||
if [ "${library_target_dir}" = "null" ]; then | ||
library_target_dir="${name}-${version}" | ||
else | ||
library_target_dir="${name}-${version}${library_target_dir}" | ||
fi | ||
|
||
cd "${DECOMPOSER_TARGET_DIR}/${library_target_dir}" || return 1 | ||
|
||
local commit | ||
commit=$(git -C "${DECOMPOSER_TARGET_DIR}/${library_target_dir}" rev-parse HEAD) | ||
|
||
printf '"%s":{"commit": "%s", "revision": "%s", "version": "%s", "url": "%s"},' "${name}" "${commit}" "${revision}" "${version}" "${url}" >> "${TMP_FILE}" | ||
} | ||
|
||
parse_arguments "$@" | ||
validate_arguments | ||
|
||
process_file "${CWD}/decomposer.json" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
. += { | ||
name: $name, | ||
SPDXID: "SPDXRef-\($name)", | ||
Comment on lines
+1
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the changelog, we'd need the project version in here too. I see examples adding it to the name, which I suppose would be good enough. We could extract it from there again. A dedicated There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With version, you are referring to commit hashes right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, anything we can use as input for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This works, for example, so the output of |
||
dataLicense: "CC0-1.0", | ||
spdxVersion: "SPDX-2.2", | ||
comment: "This document was created using Decomposer \($version) using information from the declared git repos.", | ||
documentNamespace: ($namespace // "NOASSERTION"), | ||
creationInfo: { | ||
created: (now | todateiso8601), | ||
creators: ["Tool: Decomposer-\($version)"] | ||
}, | ||
packages: ($packages | to_entries | map({ | ||
SPDXID: "SPDXRef-\(.key)-\(.value.version)", | ||
name: .key, | ||
versionInfo: .value.version, | ||
copyrightText: "NOASSERTION", | ||
downloadLocation: (.value.url // "NOASSERTION"), | ||
licenseConcluded: "NOASSERTION", | ||
licenseDeclared: (.value.license // "NOASSERTION"), | ||
filesAnalyzed: false, | ||
sourceInfo: "Git checkout of the object \(.value.revision) from the repo", | ||
annotations: [ | ||
{ | ||
annotationDate: (now | todateiso8601), | ||
annotationType: "OTHER", | ||
annotator: "Tool: Decomposer-\($version)", | ||
comment: "Generated with checksum as commit hash" | ||
} | ||
], | ||
checksums: [ | ||
{algorithm: "SHA1", checksumValue: .value.commit} | ||
] | ||
})) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might be better in a separate workflow. I think the curl is not enough and we need an actual git clone to work with, and that would open up the road to a testsuite where we clone and test with multiple different repos.