diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 3f5b1d2b..b5acc8c3 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -34,6 +34,10 @@ on: branches: - '**' # '*' matches zero or more characters but does not match the `/` character '**' matches zero or more of any character + pull_request: # we need to build on pull requests so that we can generate and upload the sbom before merging onto main/develop branches + branches: + - '**' + jobs: @@ -68,35 +72,37 @@ jobs: - name: '🏗 📦 Build, Pack & Announce New Release (if appropriate)' shell: 'bash' run: | - cd "${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts" \ - && \ - echo "${{env.SCL_DEPENDENCY_TRACKER_SIGNING_PRIVATE_KEY}}" > "${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/dependency_tracker_private_signing_key.ppk" \ - && \ - dotnet \ - msbuild \ - "Laerdal.Builder.targets" \ - -m:1 \ - -p:Should_Skip_MacCatalyst="false" \ - \ - -p:PackageOutputPath="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Artifacts" \ - -p:Laerdal_Gradle_Path="/opt/homebrew/opt/gradle@7/bin/gradle" \ - -p:Laerdal_Source_Branch="${{env.LAERDAL_SOURCE_BRANCH}}" \ - -p:Laerdal_Repository_Path="${{env.LAERDAL_REPOSITORY_PATH}}" \ - -p:Laerdal_Github_Access_Token="${{env.SCL_GITHUB_ACCESS_TOKEN}}" \ - -p:Laerdal_Test_Results_Folderpath="${{env.BUILD_REPOSITORY_FOLDERPATH}}/TestResults" \ - \ - -p:Laerdal_CycloneCLI_Path="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/cyclonedx" \ - -p:Laerdal_Dependency_Tracker_Api_Key="${{env.SCL_DEPENDENCY_TRACKER_API_KEY}}" \ - -p:Laerdal_Dependency_Tracker_Server_Url="${{env.SCL_DEPENDENCY_TRACKER_SERVER_URL}}" \ - -p:Laerdal_Dependency_Tracker_Private_Signing_Key_Path="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/dependency_tracker_private_signing_key.ppk" \ - \ - -p:Laerdal_Bindings_iOS___Sdk_Version="${{env.BINDINGS_IOS___SDK_VERSION}}" \ - -p:Laerdal_Bindings_iOS___Xcode_Ide_Dev_Path="${{env.BINDINGS_IOS___XCODE_IDE_DEV_PATH}}" \ - \ - -p:Laerdal_Bindings_MacCatalyst___Sdk_Version="${{env.BINDINGS_MACCATALYST___SDK_VERSION}}" \ - -p:Laerdal_Bindings_MacCatalyst___Xcode_Ide_Dev_Path="${{env.BINDINGS_MACCATALYST___XCODE_IDE_DEV_PATH}}" \ - && \ - rm "${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/dependency_tracker_private_signing_key.ppk" + cd "${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts" \ + && \ + echo "${{env.SCL_DEPENDENCY_TRACKER_API_KEY}}" > "./dependency_tracker_api_key.ppk" \ + && \ + echo "${{env.SCL_DEPENDENCY_TRACKER_SIGNING_PRIVATE_KEY}}" > "./dependency_tracker_private_signing_key.ppk" \ + && \ + dotnet \ + msbuild \ + "Laerdal.Builder.targets" \ + -m:1 \ + -p:Should_Skip_MacCatalyst="false" \ + \ + -p:PackageOutputPath="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Artifacts" \ + -p:Laerdal_Gradle_Path="/opt/homebrew/opt/gradle@7/bin/gradle" \ + -p:Laerdal_Source_Branch="${{env.LAERDAL_SOURCE_BRANCH}}" \ + -p:Laerdal_Repository_Path="${{env.LAERDAL_REPOSITORY_PATH}}" \ + -p:Laerdal_Github_Access_Token="${{env.SCL_GITHUB_ACCESS_TOKEN}}" \ + -p:Laerdal_Test_Results_Folderpath="${{env.BUILD_REPOSITORY_FOLDERPATH}}/TestResults" \ + \ + -p:Laerdal_CycloneCLI_Path="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/cyclonedx" \ + -p:Laerdal_Dependency_Tracker_Server_Url="${{env.SCL_DEPENDENCY_TRACKER_SERVER_URL}}" \ + -p:Laerdal_Dependency_Tracker_Api_Key_File_Path="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/dependency_tracker_api_key.ppk" \ + -p:Laerdal_Dependency_Tracker_Private_Signing_Key_File_Path="${{env.BUILD_REPOSITORY_FOLDERPATH}}/Laerdal.Scripts/dependency_tracker_private_signing_key.ppk" \ + \ + -p:Laerdal_Bindings_iOS___Sdk_Version="${{env.BINDINGS_IOS___SDK_VERSION}}" \ + -p:Laerdal_Bindings_iOS___Xcode_Ide_Dev_Path="${{env.BINDINGS_IOS___XCODE_IDE_DEV_PATH}}" \ + \ + -p:Laerdal_Bindings_MacCatalyst___Sdk_Version="${{env.BINDINGS_MACCATALYST___SDK_VERSION}}" \ + -p:Laerdal_Bindings_MacCatalyst___Xcode_Ide_Dev_Path="${{env.BINDINGS_MACCATALYST___XCODE_IDE_DEV_PATH}}" \ + && \ + rm "./dependency_tracker_private_signing_key.ppk" "./dependency_tracker_api_key.ppk" - name: '📡 Publish Test Results' # https://github.com/marketplace/actions/publish-test-results uses: 'EnricoMi/publish-unit-test-result-action/macos@v2' diff --git a/Laerdal.McuMgr.sln b/Laerdal.McuMgr.sln index 8252dce1..1b1732cf 100644 --- a/Laerdal.McuMgr.sln +++ b/Laerdal.McuMgr.sln @@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Misc", "_Misc", "{2459FC0F global.json = global.json Laerdal.SetupBuildEnvironment.sh = Laerdal.Scripts\Laerdal.SetupBuildEnvironment.sh .github\workflows\github-actions.yml = .github\workflows\github-actions.yml + Laerdal.Scripts\Laerdal.GenerateSignAndUploadSbom.sh = Laerdal.Scripts\Laerdal.GenerateSignAndUploadSbom.sh EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Laerdal.McuMgr", "Laerdal.McuMgr\Laerdal.McuMgr.csproj", "{4E2952A5-394E-4184-8E12-F2D5342A43B2}" diff --git a/Laerdal.Scripts/Laerdal.Builder.targets b/Laerdal.Scripts/Laerdal.Builder.targets index d1effe08..778ac892 100644 --- a/Laerdal.Scripts/Laerdal.Builder.targets +++ b/Laerdal.Scripts/Laerdal.Builder.targets @@ -50,7 +50,9 @@ - true + true + true + true false @@ -63,11 +65,13 @@ - + gradle $(BUILD_SOURCEBRANCH) $(BUILD_REPOSITORY_NAME) - True + + True + True $([System.IO.Path]::Combine($(Laerdal_RootDirectory_Folderpath), `Laerdal.McuMgr`, `Laerdal.McuMgr.csproj`)) $([System.IO.Path]::Combine($(Laerdal_RootDirectory_Folderpath), `Laerdal.McuMgr.Bindings.iOS`, `Laerdal.McuMgr.Bindings.iOS.csproj`)) @@ -312,17 +316,16 @@ WorkingDirectory="$(Laerdal_RootDirectory_Folderpath)"/> - + - - + Condition=" '$(Laerdal_Should_Generate_and_Upload_Sbom)' == 'True' " + AfterTargets="BuildProjects"> - - - - + + + + + @@ -340,39 +343,28 @@ <_Laerdal_Command___Generate_SBOM>$(_Laerdal_Command___Generate_SBOM) --output "$(PackageOutputPath)" <_Laerdal_Command___Generate_SBOM>$(_Laerdal_Command___Generate_SBOM) --set-version "$(Laerdal_Version_Full)" + - - - - - - - - - - - - - - - - - - - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) curl "$(Laerdal_Dependency_Tracker_Server_Url)" - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) --location - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) --request "POST" - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) --header "Content-Type: multipart/form-data" - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) --header "X-API-Key: $(Laerdal_Dependency_Tracker_Api_Key)" - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) --form "autoCreate=true" - <_Laerdal_Command___Upload_SBOM>$(_Laerdal_Command___Upload_SBOM) --form "projectVersion=$(Laerdal_Version_Full)" + <_Laerdal_Sbom_Script_Parameters>$(_Laerdal_Sbom_Script_Parameters) --project-version "$(Laerdal_Version_Assembly)" + <_Laerdal_Sbom_Script_Parameters>$(_Laerdal_Sbom_Script_Parameters) --output-directory-path "$(PackageOutputPath)" + <_Laerdal_Sbom_Script_Parameters>$(_Laerdal_Sbom_Script_Parameters) --sbom-signing-key-file-path "$(Laerdal_Dependency_Tracker_Private_Signing_Key_File_Path)" + + <_Laerdal_Sbom_Script_Parameters>$(_Laerdal_Sbom_Script_Parameters) --dependency-tracker-url "$(Laerdal_Dependency_Tracker_Server_Url)" + <_Laerdal_Sbom_Script_Parameters>$(_Laerdal_Sbom_Script_Parameters) --dependency-tracker-api-key-file-path "$(Laerdal_Dependency_Tracker_Api_Key_File_Path)" + + <_Laerdal_Sbom_Script_Parameters>$(_Laerdal_Sbom_Script_Parameters) --csproj-classifier "Library" - - - - - + + + + + + + + + + diff --git a/Laerdal.Scripts/Laerdal.GenerateSignAndUploadSbom.sh b/Laerdal.Scripts/Laerdal.GenerateSignAndUploadSbom.sh new file mode 100644 index 00000000..94b41001 --- /dev/null +++ b/Laerdal.Scripts/Laerdal.GenerateSignAndUploadSbom.sh @@ -0,0 +1,248 @@ +#!/bin/bash + +# set -x + +declare project_name="" +declare project_version="" + +declare parent_project_name="" +declare parent_project_version="" + +declare csproj_file_path="" +declare csproj_classifier="" +declare output_directory_path="" +declare output_sbom_file_name="" +declare sbom_signing_key_file_path="" + +declare dependency_tracker_url="" +declare dependency_tracker_api_key_file_path="" + + +function parse_arguments() { + + while [[ $# -gt 0 ]]; do + case $1 in + + --project-name) + project_name="$2" + shift + ;; + + --project-version) + project_version="$2" + shift + ;; + + --parent-project-name) + parent_project_name="$2" + shift + ;; + + --parent-project-version) + parent_project_version="$2" + shift + ;; + + --csproj-file-path) + csproj_file_path="$2" + shift + ;; + + --csproj-classifier) + csproj_classifier="$2" + shift + ;; + + --output-directory-path) + output_directory_path="$2" + shift + ;; + + --output-sbom-file-name) + output_sbom_file_name="$2" + shift + ;; + + --sbom-signing-key-file-path) + sbom_signing_key_file_path="$2" + shift + ;; + + --dependency-tracker-url) + dependency_tracker_url="$2" + shift + ;; + + --dependency-tracker-api-key-file-path) + dependency_tracker_api_key_file_path="$2" + shift + ;; + + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac + + shift + done + + if [[ -z ${project_name} ]]; then + echo "Specifying --project-name is mandatory!" + usage + exit 1 + fi + + if [[ -z ${project_version} ]]; then + echo "Specifying --project-version is mandatory!" + usage + exit 1 + fi + + # if [[ -z ${parent_project_name} ]]; then this is optional + # ... + + # if [[ -z ${parent_project_version} ]]; then this is optional + # ... + + if [[ -n ${parent_project_name} && -z ${parent_project_version} ]]; then + echo "Specifying --parent-project-version is mandatory when --parent-project-name has been used!" + usage + exit 1 + fi + + if [[ -z ${csproj_file_path} ]]; then + echo "Specifying --csproj-file-path is mandatory!" + usage + exit 1 + fi + + if [[ -z ${csproj_classifier} ]]; then + echo "Specifying --csproj-classifier is mandatory!" + usage + exit 1 + fi + + if [[ -z ${output_directory_path} ]]; then + echo "Specifying --output-directory-path is mandatory!" + usage + exit 1 + fi + + if [[ -z ${output_sbom_file_name} ]]; then + echo "Specifying --output-sbom-file-name is mandatory!" + usage + exit 1 + fi + + if [[ -z ${sbom_signing_key_file_path} ]]; then + echo "Specifying --sbom-signing-key-file-path is mandatory!" + usage + exit 1 + fi + + if [[ -z ${dependency_tracker_url} ]]; then + echo "Specifying --dependency-tracker-url is mandatory!" + usage + exit 1 + fi + + if [[ -z ${dependency_tracker_api_key_file_path} ]]; then + echo "Specifying --dependency-tracker-api-key-file-path is mandatory!" + usage + exit 1 + fi +} + +function usage() { + local -r script_name=$(basename "$0") + + echo "Usage: ${script_name} --project-name --project-version [--parent-project-name --parent-project-version ] --csproj-file-path --csproj-file-path --output-directory-path --output-sbom-file-name --sbom-signing-key-file-path --dependency-tracker-url --dependency-tracker-api-key-file-path " +} + +function generate_sign_and_upload_sbom() { + set -x + + # GENERATE SBOM + dotnet-CycloneDX "${csproj_file_path}" \ + --exclude-dev \ + --include-project-references \ + \ + --output "${output_directory_path}" \ + --set-type "${csproj_classifier}" \ + --set-version "${project_version}" \ + \ + --filename "${output_sbom_file_name}" + declare exitCode=$? + if [ ${exitCode} != 0 ]; then + echo "##vso[task.logissue type=error]Failed to generate the SBOM!" + exit 10 + fi + + + + # SIGN SBOM todo figure out why this doesnt actually sign anything on windows even though on macos it works as intended + declare -r bom_file_path="${output_directory_path}/${output_sbom_file_name}" + ./cyclonedx sign bom \ + "${bom_file_path}" \ + --key-file "${sbom_signing_key_file_path}" + declare exitCode=$? + if [ ${exitCode} != 0 ]; then + echo "##vso[task.logissue type=error]Singing the SBOM failed!" + exit 20 + fi + echo -e "\n\n" + tail "${bom_file_path}" + echo -e "\n\n" + + + + # UPLOAD SBOM + declare optional_parent_project_name_parameter="" + if [[ -n ${parent_project_name} ]]; then + optional_parent_project_name_parameter="--form parentName=${parent_project_name}" + fi + + declare optional_parent_project_version_parameter="" + if [[ -n ${parent_project_version} ]]; then + optional_parent_project_version_parameter="--form parentVersion=${parent_project_version}" + fi + + + declare -r http_response_code=$( \ + curl "${dependency_tracker_url}" \ + --location \ + --request "POST" \ + \ + --header "Content-Type: multipart/form-data" \ + --header "X-API-Key: $(cat "${dependency_tracker_api_key_file_path}")" \ + \ + --form "bom=@${bom_file_path}" \ + --form "autoCreate=true" \ + \ + --form "projectName=${project_name}" \ + --form "projectVersion=${project_version}" \ + \ + ${optional_parent_project_name_parameter} \ + ${optional_parent_project_version_parameter} \ + \ + -w "%{http_code}" \ + ) + declare exitCode=$? + set +x + + echo "** Curl sbom-uploading HTTP Response Code: ${http_response_code}" + + if [ ${exitCode} != 0 ]; then + echo "##vso[task.logissue type=error]SBOM Uploading failed!" + exit 30 + fi +} + +function main() { + parse_arguments "$@" + generate_sign_and_upload_sbom +} + +main "$@"