From 631cba5850895756624fbbe4fff6b9eaf2bf4f16 Mon Sep 17 00:00:00 2001 From: soonhong99 Date: Sun, 6 Oct 2024 00:33:50 +0900 Subject: [PATCH 1/3] Add Ubuntu support and multi-architecture builds - Create fosslight_wrapper_ubuntu.py for easy Docker image usage on Ubuntu - Add GitHub Actions workflow for building Ubuntu executables (aarch64, x86_64) - Update release process to include Ubuntu-specific artifacts - Improve cross-platform compatibility and ease of use This commit enhances the FossLight wrapper to support Ubuntu environments and multiple CPU architectures, making it more accessible to a wider range of users and systems. Signed-off-by: soonhong99 --- .github/workflows/docker-build-push.yml | 82 +++++++- fosslight_wrapper_ubuntu.py | 249 ++++++++++++++++++++++++ 2 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 fosslight_wrapper_ubuntu.py diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml index 963e54e..5d25902 100644 --- a/.github/workflows/docker-build-push.yml +++ b/.github/workflows/docker-build-push.yml @@ -10,19 +10,15 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v2 - - name: Set up QEMU uses: docker/setup-qemu-action@v1 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Login to DockerHub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push uses: docker/build-push-action@v2 with: @@ -31,4 +27,80 @@ jobs: platforms: linux/amd64,linux/arm64 tags: | fosslight/fosslight_scanner:latest - fosslight/fosslight_scanner:${{ github.event.release.tag_name }} \ No newline at end of file + fosslight/fosslight_scanner:${{ github.event.release.tag_name }} + + create-windows-executable: + runs-on: windows-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Install dependencies + run: | + pip install pyinstaller + - name: Create executable + run: pyinstaller --onefile fosslight_wrapper.py + - name: Upload executable to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./dist/fosslight_wrapper.exe + asset_name: fosslight_wrapper.exe + asset_content_type: application/vnd.microsoft.portable-executable + + create-macos-command-file: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Create .command file + run: | + chmod +x fosslight_wrapper_mac.command + - name: Upload .command file to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./fosslight_wrapper_mac.command + asset_name: fosslight_wrapper_mac.command + asset_content_type: application/x-sh + + create-ubuntu-executables: + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x86_64, aarch64] + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Install dependencies + run: | + pip install pyinstaller + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Build with PyInstaller + run: | + if [ "${{ matrix.arch }}" = "aarch64" ]; then + docker run --rm -v $PWD:/work -w /work multiarch/ubuntu-core:arm64-focal /bin/bash -c "apt-get update && apt-get install -y python3-pip && pip3 install pyinstaller && pyinstaller --onefile fosslight_wrapper_ubuntu.py" + else + pyinstaller --onefile fosslight_wrapper_ubuntu.py + fi + - name: Upload executable to release + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./dist/fosslight_wrapper_ubuntu + asset_name: fosslight_wrapper_ubuntu-${{ matrix.arch }} + asset_content_type: application/octet-stream \ No newline at end of file diff --git a/fosslight_wrapper_ubuntu.py b/fosslight_wrapper_ubuntu.py new file mode 100644 index 0000000..5101474 --- /dev/null +++ b/fosslight_wrapper_ubuntu.py @@ -0,0 +1,249 @@ +import sys +import io +import subprocess +import logging +from datetime import datetime +import os + + +def setup_logging(): + current_time = datetime.now().strftime("%Y%m%d_%H%M") + log_filename = f'fosslight_log_{current_time}.txt' + logging.basicConfig(filename=log_filename, level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s') + + +def check_and_pull_image(image_name): + try: + result = subprocess.run(["docker", "image", "inspect", image_name], + capture_output=True, text=True) + if result.returncode == 0: + logging.info(f"Image {image_name} already exists locally.") + return True + + logging.info(f"Pulling the image {image_name} from Docker Hub") + subprocess.run(["docker", "pull", image_name], check=True) + logging.info(f"Successfully pulled the image {image_name}") + return True + except subprocess.CalledProcessError as e: + logging.error(f"Error with Docker image {image_name}: {e}") + return False + + +def get_user_input(auto_image=None): + if auto_image: + return auto_image, 'local', os.getcwd() + + print("FossLight Wrapper") + image = input("Enter Docker image name (e.g., fosslight/fosslight_scanner:latest): ") + analysis_type = input("Choose analysis type (1 for local path, 2 for Git repository): ") + + if analysis_type == '1': + input_path = input("Enter local path to analyze: ") + return image, 'local', input_path + elif analysis_type == '2': + git_url = input("Enter Git repository URL to analyze: ") + return image, 'git', git_url + else: + print("Invalid choice. Exiting.") + sys.exit(1) + + +def display_current_options(options): + if not options: + print("Only the default option has been applied.") + else: + print("Current additional options:") + for i, option in enumerate(options, 1): + print(f"{i}. {option}") + + +def get_additional_options(): + options = [] + while True: + print("\nManage additional options:") + print("1. Add new option") + print("2. Remove option") + print("3. View current options") + print("4. Finish and proceed") + + choice = input("\nEnter your choice (1-4): ") + + if choice == '1': + options.extend(add_option()) + elif choice == '2': + options = remove_option(options) + elif choice == '3': + display_current_options(options) + elif choice == '4': + break + else: + print("Invalid choice. Please try again.") + + return options + + +def add_option(): + print("\nAvailable additional options:") + print("1. -f : FOSSLight Report file format (excel, yaml)") + print("2. -c : Number of processes to analyze source") + print("3. -r: Keep raw data") + print("4. -t: Hide the progress bar") + print("5. -s : Path to apply setting from file") + print("6. --no_correction: Don't correct OSS information") + print("7. --correct_fpath : Path to the sbom-info.yaml file") + print("8. -u : DB Connection (for 'all' or 'bin' mode)") + print("9. -d : Additional arguments for dependency analysis") + + choice = input("\nEnter the number of the option you want to add: ") + + if choice == '1': + format_type = input("Enter format (excel/yaml): ") + return ['-f', format_type] + elif choice == '2': + processes = input("Enter number of processes: ") + return ['-c', processes] + elif choice == '3': + return ['-r'] + elif choice == '4': + return ['-t'] + elif choice == '5': + settings_path = input("Enter path to settings file: ") + return ['-s', settings_path] + elif choice == '6': + return ['--no_correction'] + elif choice == '7': + sbom_path = input("Enter path to sbom-info.yaml: ") + return ['--correct_fpath', sbom_path] + elif choice == '8': + db_url = input("Enter DB URL: ") + return ['-u', db_url] + elif choice == '9': + dep_arg = input("Enter dependency argument: ") + return ['-d', dep_arg] + else: + print("Invalid option. No option added.") + return [] + + +def remove_option(options): + if not options: + print("No options to remove.") + return options + + display_current_options(options) + choice = input("Enter the number of the option you want to remove (or 0 to cancel): ") + + try: + index = int(choice) - 1 + if 0 <= index < len(options): + removed_option = options.pop(index) + print(f"Removed option: {removed_option}") + elif index == -1: + print("Removal cancelled.") + else: + print("Invalid number. No option removed.") + except ValueError: + print("Invalid input. No option removed.") + + return options + + +def remove_wfp_file(output_path): + wfp_file = os.path.join(output_path, "scanner_output.wfp") + if os.path.exists(wfp_file): + try: + os.remove(wfp_file) + logging.info(f"Successfully removed WFP file: {wfp_file}") + except Exception as e: + logging.error(f"Failed to remove WFP file: {wfp_file}. Error: {e}") + + +def run_fosslight(image, analysis_type, input_source, output_path, additional_options): + docker_cmd = [ + "docker", "run", "--rm", + "-v", f"{output_path}:/output" + ] + + if analysis_type == 'local': + docker_cmd.extend(["-v", f"{input_source}:/src"]) + + docker_cmd.extend([ + image, + "fosslight", + "-o", "/output", + ]) + + if analysis_type == 'local': + docker_cmd.extend(["-p", "/src"]) + else: # Git repository + docker_cmd.extend(["-w", input_source]) + + docker_cmd.extend(additional_options) + + logging.info(f"Running Docker command: {' '.join(docker_cmd)}") + + try: + process = subprocess.Popen(docker_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + bufsize=1, universal_newlines=True) + for line in process.stdout: + line = line.strip() + if line: + print(line) + sys.stdout.flush() + logging.info(line) + process.wait() + if process.returncode != 0: + logging.error(f"FossLight exited with error code {process.returncode}") + else: + logging.info("FossLight completed successfully") + except subprocess.CalledProcessError as e: + logging.error(f"Error running FossLight: {e}") + except Exception as e: + logging.error(f"Unexpected error: {e}") + + remove_wfp_file(output_path) + + +def get_execution_mode(): + return "manual" if len(sys.argv) > 1 and sys.argv[1] == "--manual" else "auto" + + +def main(): + setup_logging() + + execution_mode = get_execution_mode() + + if execution_mode == "auto": + logging.info("Executing in automatic mode") + image_name = "nanayah99/fosslight_scanner:latest" + if not check_and_pull_image(image_name): + print(f"Failed to ensure the presence of the Docker image: {image_name}") + sys.exit(1) + + current_dir = os.getcwd() + image, analysis_type, input_source = image_name, 'local', current_dir + output_path = current_dir + additional_options = ["-f", "excel"] + else: + logging.info("Executing in manual mode") + image, analysis_type, input_source = get_user_input() + output_path = input("Enter path for output: ") + additional_options = get_additional_options() + + additional_options = list(dict.fromkeys(additional_options)) + + logging.info("Starting FossLight wrapper") + logging.info(f"Docker image: {image}") + logging.info(f"Analysis type: {analysis_type}") + logging.info(f"Input source: {input_source}") + logging.info(f"Output path: {output_path}") + logging.info(f"Additional options: {' '.join(additional_options)}") + + run_fosslight(image, analysis_type, input_source, output_path, additional_options) + + print("\nFossLight wrapper completed.") + + +if __name__ == "__main__": + main() From 67b8e3ab49e97846e995420e32a6e68b9e4752f1 Mon Sep 17 00:00:00 2001 From: soonhong99 Date: Sun, 6 Oct 2024 00:47:28 +0900 Subject: [PATCH 2/3] fix: add copyright and fix flake8 error Signed-off-by: soonhong99 --- fosslight_wrapper_ubuntu.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fosslight_wrapper_ubuntu.py b/fosslight_wrapper_ubuntu.py index 5101474..a60d159 100644 --- a/fosslight_wrapper_ubuntu.py +++ b/fosslight_wrapper_ubuntu.py @@ -1,5 +1,7 @@ +# Copyright (c) 2021 LG Electronics +# SPDX-License-Identifier: Apache-2.0 + import sys -import io import subprocess import logging from datetime import datetime From 274892501011749011d201e446ffb4f0a80ef44d Mon Sep 17 00:00:00 2001 From: soonhong99 Date: Sun, 6 Oct 2024 00:50:13 +0900 Subject: [PATCH 3/3] chore: docker image name nanayah99 -> fosslight Signed-off-by: soonhong99 --- fosslight_wrapper_ubuntu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fosslight_wrapper_ubuntu.py b/fosslight_wrapper_ubuntu.py index a60d159..fcddf69 100644 --- a/fosslight_wrapper_ubuntu.py +++ b/fosslight_wrapper_ubuntu.py @@ -218,7 +218,7 @@ def main(): if execution_mode == "auto": logging.info("Executing in automatic mode") - image_name = "nanayah99/fosslight_scanner:latest" + image_name = "fosslight/fosslight_scanner:latest" if not check_and_pull_image(image_name): print(f"Failed to ensure the presence of the Docker image: {image_name}") sys.exit(1)