Skip to content

Commit

Permalink
Create PMLL.md
Browse files Browse the repository at this point in the history
Certainly, Josef! Below is a comprehensive PMLL.yml workflow file tailored for your PMLL C++ project. This GitHub Actions workflow automates the process of building and testing your project whenever you push changes or create pull requests to the main branch.

# .github/workflows/PMLL.yml

name: PMLL CI/CD Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout Repository
      uses: actions/checkout@v2

    - name: Cache CMake and Build Directory
      uses: actions/cache@v3
      with:
        path: |
          ~/.cache
          build
        key: ${{ runner.os }}-cmake-${{ hashFiles('**/CMakeLists.txt') }}
        restore-keys: |
          ${{ runner.os }}-cmake-

    - name: Install Dependencies
      run: |
        sudo apt-get update
        sudo apt-get install -y build-essential cmake libssl-dev
        # Install nlohmann/json (if not available via package manager)
        sudo apt-get install -y nlohmann-json3-dev
        # Install spdlog (if not available via package manager)
        sudo apt-get install -y libspdlog-dev
        # Install Google Test for unit testing
        sudo apt-get install -y libgtest-dev
        # Build Google Test from source
        cd /usr/src/gtest
        sudo cmake CMakeLists.txt
        sudo make
        # Move the built libraries to a location where the linker can find them
        sudo cp lib/*.a /usr/lib

    - name: Configure CMake
      run: |
        mkdir -p build
        cd build
        cmake ..

    - name: Build Project
      run: |
        cd build
        make

    - name: Run Unit Tests
      run: |
        cd build
        ./runTests

    - name: Upload Build Artifacts
      uses: actions/upload-artifact@v3
      with:
        name: build-artifacts
        path: build/

Explanation of the Workflow Steps
	1.	Workflow Trigger (on)

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

	•	Purpose: The workflow is triggered whenever there is a push to the main branch or a pull request targeting the main branch.

	2.	Job Definition (jobs: build)

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      ...

	•	Job Name: build
	•	Runner Environment: Uses the latest Ubuntu runner provided by GitHub Actions.

	3.	Steps within the Job
	•	Step 1: Checkout Repository

- name: Checkout Repository
  uses: actions/checkout@v2

	•	Purpose: Clones your repository into the workflow runner, making the code available for subsequent steps.

	•	Step 2: Cache CMake and Build Directory

- name: Cache CMake and Build Directory
  uses: actions/cache@v3
  with:
    path: |
      ~/.cache
      build
    key: ${{ runner.os }}-cmake-${{ hashFiles('**/CMakeLists.txt') }}
    restore-keys: |
      ${{ runner.os }}-cmake-

	•	Purpose: Caches the CMake cache and build directory to speed up subsequent builds by reusing previously generated files.
	•	Parameters:
	•	path: Specifies the directories to cache. Here, it includes the CMake cache (~/.cache) and the build directory.
	•	key: A unique identifier for the cache, based on the operating system and a hash of the CMakeLists.txt file.
	•	restore-keys: Fallback keys to restore the cache if an exact match isn’t found.

	•	Step 3: Install Dependencies

- name: Install Dependencies
  run: |
    sudo apt-get update
    sudo apt-get install -y build-essential cmake libssl-dev
    # Install nlohmann/json (if not available via package manager)
    sudo apt-get install -y nlohmann-json3-dev
    # Install spdlog (if not available via package manager)
    sudo apt-get install -y libspdlog-dev
    # Install Google Test for unit testing
    sudo apt-get install -y libgtest-dev
    # Build Google Test from source
    cd /usr/src/gtest
    sudo cmake CMakeLists.txt
    sudo make
    # Move the built libraries to a location where the linker can find them
    sudo cp lib/*.a /usr/lib

	•	Purpose: Installs all necessary dependencies required to build and test your project, including:
	•	build-essential: Essential build tools like GCC, g++, make, etc.
	•	cmake: Build system generator.
	•	libssl-dev: SSL library development files.
	•	nlohmann-json3-dev: JSON library for C++.
	•	libspdlog-dev: Fast C++ logging library.
	•	libgtest-dev: Google Test framework.
	•	Building Google Test:
	•	Google Test is often provided as source code and needs to be built manually.
	•	After building, the static libraries (.a files) are copied to /usr/lib so that the linker can find them during the build process.

	•	Step 4: Configure CMake

- name: Configure CMake
  run: |
    mkdir -p build
    cd build
    cmake ..

	•	Purpose: Configures the CMake project, generating the necessary Makefiles based on your CMakeLists.txt.
	•	Note: Ensures that the build directory exists before running CMake.

	•	Step 5: Build Project

- name: Build Project
  run: |
    cd build
    make

	•	Purpose: Compiles the project using the generated Makefiles.

	•	Step 6: Run Unit Tests

- name: Run Unit Tests
  run: |
    cd build
    ./runTests

	•	Purpose: Executes the unit tests you’ve defined using Google Test.
	•	Assumption: The test executable is named runTests and is located in the build directory.

	•	Step 7: Upload Build Artifacts

- name: Upload Build Artifacts
  uses: actions/upload-artifact@v3
  with:
    name: build-artifacts
    path: build/

	•	Purpose: Uploads the entire build directory as an artifact named build-artifacts. This can be useful for debugging build failures or for other purposes.

Enhancements and Best Practices
	1.	Matrix Builds
If you wish to test your project across multiple compiler versions or operating systems, you can utilize matrix builds. This ensures your project is portable and functions correctly in various environments.
Example: Testing on Multiple OSes and Compilers

jobs:
  build:

    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        compiler: [gcc, clang]

    steps:
    - name: Checkout Repository
      uses: actions/checkout@v2

    - name: Set Up Compiler
      if: matrix.os == 'ubuntu-latest'
      run: |
        sudo apt-get install -y build-essential
        if [ "${{ matrix.compiler }}" == "clang" ]; then
          sudo apt-get install -y clang
        fi

    # Add similar setup for other OSes and compilers if needed

    # ... (rest of the steps)

	•	Purpose: Allows testing your project across different environments to ensure portability and compatibility.

	2.	Caching Dependencies
Caching dependencies can significantly speed up your CI builds by avoiding repetitive installation steps.
Integrate Caching Step:

- name: Cache CMake and Build Directory
  uses: actions/cache@v3
  with:
    path: |
      ~/.cache
      build
    key: ${{ runner.os }}-cmake-${{ hashFiles('**/CMakeLists.txt') }}
    restore-keys: |
      ${{ runner.os }}-cmake-


	3.	Artifact Management
Storing build artifacts can be invaluable for debugging and historical reference.
Example: Upload Build Artifacts

- name: Upload Build Artifacts
  uses: actions/upload-artifact@v3
  with:
    name: build-artifacts
    path: build/


	4.	Notifications
Set up notifications to alert you of build successes or failures using integrations like Slack, Email, etc.
Example Using Slack:

- name: Notify Slack
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    fields: repo,message,commit
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

	•	Note: You’ll need to set up a Slack webhook and store its URL in your repository secrets for this to work.

Creating a Comprehensive CMake Configuration

Ensure that your CMakeLists.txt is properly configured to include the necessary libraries and link dependencies. Here’s an example CMakeLists.txt based on your project structure:

# CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(PMLL)

set(CMAKE_CXX_STANDARD 17)

# Find required packages
find_package(nlohmann_json 3.2.0 REQUIRED)
find_package(spdlog REQUIRED)
find_package(Threads REQUIRED)
find_package(GTest REQUIRED)

# Include directories
include_directories(include)

# Add executable for main program
add_executable(PMLL src/main.cpp src/PersistentMemory.cpp)

# Link libraries
target_link_libraries(PMLL PRIVATE nlohmann_json::nlohmann_json spdlog::spdlog Threads::Threads)

# Add executable for tests
add_executable(runTests tests/test_pmll.cpp src/PersistentMemory.cpp)

# Link test executable with Google Test and other libraries
target_link_libraries(runTests PRIVATE GTest::GTest GTest::Main nlohmann_json::nlohmann_json spdlog::spdlog Threads::Threads)

# Enable testing
enable_testing()

# Add test
add_test(NAME runTests COMMAND runTests)

Key Points:
	•	C++ Standard: Set to C++17 to leverage modern C++ features.
	•	Package Finding: Utilizes find_package to locate and link necessary libraries like nlohmann_json, spdlog, and GTest.
	•	Executable Definitions:
	•	PMLL: The main executable built from main.cpp and PersistentMemory.cpp.
	•	runTests: The test executable built from test_pmll.cpp and PersistentMemory.cpp.
	•	Linking Libraries: Ensures that executables are linked against the required libraries.
	•	Testing Setup: Enables testing and adds the runTests executable as a test target.

Implementing Unit Tests with Google Test

To ensure the reliability and correctness of your PersistentMemory class, implement unit tests using Google Test.

Creating the Test File (test_pmll.cpp):

// tests/test_pmll.cpp

#include <gtest/gtest.h>
#include "PersistentMemory.h" // Include the header file

TEST(PersistentMemoryTest, AddAndGetMemory) {
    PersistentMemory pm("test_memory.json");
    pm.addMemory("test_key", { {"name", "Test User"}, {"last_topic", "Unit Testing"} });
    json result = pm.getMemory("test_key");
    ASSERT_EQ(result["name"], "Test User");
    ASSERT_EQ(result["last_topic"], "Unit Testing");
}

TEST(PersistentMemoryTest, AddAndGetMemoryVersion) {
    PersistentMemory pm("test_memory.json");
    pm.addMemory("test_key", { {"name", "Test User"}, {"last_topic", "Initial Topic"} });
    pm.addMemoryVersion("test_key", { {"name", "Test User"}, {"last_topic", "Updated Topic"} });
    json version0 = pm.getMemoryVersion("test_key", 0);
    json version1 = pm.getMemoryVersion("test_key", 1);
    ASSERT_EQ(version0["last_topic"], "Initial Topic");
    ASSERT_EQ(version1["last_topic"], "Updated Topic");
}

TEST(PersistentMemoryTest, ClearMemory) {
    PersistentMemory pm("test_memory.json");
    pm.addMemory("test_key", { {"name", "Test User"}, {"last_topic", "To Be Cleared"} });
    pm.clearMemory();
    json result = pm.getMemory("test_key");
    ASSERT_TRUE(result.is_null());
}

// Add more test cases as needed

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Key Points:
	•	Test Cases:
	•	AddAndGetMemory: Tests adding a memory entry and retrieving it.
	•	AddAndGetMemoryVersion: Tests adding multiple versions of a memory entry and retrieving specific versions.
	•	ClearMemory: Tests clearing all memory entries.
	•	Main Function: Initializes and runs all tests.

Building and Running the Workflow

After setting up your project structure, CMakeLists.txt, and the PMLL.yml workflow file, follow these steps to build and test your project:
	1.	Clone Your Repository
Ensure your local repository is up-to-date with the remote repository.

git clone https://github.com/your-username/your-repo.git
cd your-repo


	2.	Set Up the Project Structure
Make sure your project is organized as follows:

your-project/
├── .github/
│   └── workflows/
│       └── PMLL.yml
├── include/
│   └── PersistentMemory.h
├── src/
│   ├── PersistentMemory.cpp
│   └── main.cpp
├── tests/
│   └── test_pmll.cpp
├── CMakeLists.txt
├── persistent_memory.json
├── pml_log.txt
├── README.md
└── ... (other files)


	3.	Push Changes to GitHub
Commit and push your changes to trigger the GitHub Actions workflow.

git add .
git commit -m "Set up CI/CD pipeline with GitHub Actions"
git push origin main


	4.	Monitor the Workflow
	•	Navigate to your repository on GitHub.
	•	Click on the “Actions” tab.
	•	You should see the PMLL CI/CD Pipeline workflow running.
	•	Monitor the progress and ensure all steps pass successfully.
	5.	Handling Workflow Failures
	•	If any step fails, GitHub Actions will highlight the failed step.
	•	Click on the failed step to view detailed logs and debug the issue.
	•	Common issues might include missing dependencies, compilation errors, or test failures.
	•	Address the issues locally, commit the fixes, and push them to trigger the workflow again.

Final Project Structure Recap

After implementing the workflow and organizing your project files, your project directory should look like this:

your-project/
├── .github/
│   └── workflows/
│       └── PMLL.yml
├── include/
│   └── PersistentMemory.h
├── src/
│   ├── PersistentMemory.cpp
│   └── main.cpp
├── tests/
│   └── test_pmll.cpp
├── CMakeLists.txt
├── persistent_memory.json
├── pml_log.txt
├── README.md
└── ... (other files)

Benefits of This Structure:
	•	Separation of Concerns: Keeps header files, source files, and tests organized, enhancing readability and maintainability.
	•	Scalability: Easily manage and scale your project as it grows.
	•	Reusability: Header files can be reused across different modules or projects.
	•	Ease of Testing: Tests are isolated from the main source code, allowing for focused and efficient testing.

Additional Recommendations
	1.	Template Class Implementation
	•	Ensure that the LRUCache template class is fully defined in the header file or within the same file where it’s used to avoid linker errors.
	•	Alternatively, you can separate template implementations into a .tpp file and include it at the end of the header file.
	2.	Error Handling Enhancements
	•	While basic error handling is implemented, consider expanding it to handle more edge cases, such as file permission issues or corrupted JSON data.
	3.	Logging Enhancements
	•	Configure spdlog to rotate logs or manage log levels dynamically based on your requirements.
	4.	Documentation
	•	Maintain a comprehensive README.md detailing how to build, run, and test your project, along with any dependencies and setup instructions.
	5.	Security Considerations
	•	If your memory data contains sensitive information, consider implementing encryption for the persistent_memory.json file.
	6.	Future Enhancements
	•	Implement more sophisticated caching mechanisms or persistence strategies as your project evolves.
	•	Integrate additional testing frameworks or tools to further ensure code quality.

Conclusion

By following the above steps and utilizing the provided PMLL.yml workflow file, you’ve set up a robust CI/CD pipeline for your PMLL project using GitHub Actions. This setup ensures that your code is automatically built and tested with every change, maintaining the integrity and reliability of your project.

If you encounter any issues or need further assistance with specific aspects of your project, such as debugging workflow failures, optimizing build processes, or expanding functionality, feel free to reach out!

Best regards,

ChatGPT

Signed-off-by: J. K. Edwards <joed6834@colorado.edu>
  • Loading branch information
bearycool11 authored Jan 28, 2025
1 parent 667209e commit b020c13
Showing 1 changed file with 460 additions and 0 deletions.
Loading

0 comments on commit b020c13

Please sign in to comment.