DEV Community

Cover image for Observability for CI workflows on GitHub Enterprise
Ismail Egilmez
Ismail Egilmez

Posted on

Observability for CI workflows on GitHub Enterprise

Foresight Plays Well with GitHub Enterprise

Automatic application deployment and delivery are essential to the success of software development projects today. This allows applications to be quickly deployed to production environments and minimizes the risk of issues that may arise when applications are deployed manually. However, the building and testing steps that need to be executed in the software pipelines are increasingly complicated.

Maintaining the pipeline and debugging the issues that caused it to fail can be complex, sometimes taking days to resolve everything—especially if you have to scroll through pages in the GitHub Workflow logs.

Foresight’s system monitoring ensures that you won’t have to search through an ocean of GitHub logs. Foresight’s highlight dashboard allows for quick troubleshooting by indicating which workflow has a problem. Moreover, Foresight process traces allow you to easily see which step in the workflow is problematic.

In this article, we’ll look at how Foresight can help you quickly fix problematic workflows, especially when integrating Foresight into your existing GitHub Enterprise environment.

The best way to learn about Foresight’s features, easy integration with GitHub Enterprise, and the powerful capabilities you get when integrating them is to implement GitHub workflows for a real-world application.

A Demo Application

As an example, we will implement a blog management application using Node.js with Express as the backend framework and MongoDB Atlas as the cloud database. The application will have CRUD (create, read, update, delete) APIs for managing the application’s users and blogs.

First, go to Mongo Atlas to create a new database that has three collections as: posts, revoked tokens, and users.

Image description
Figure 1: Mongo Atlas database creation

Then, cloning the application to your local machine, all you need to do is change the database configuration string in the config/db.js file.

 `module.exports = {
    MONGO_CONNECT_URL:"mongodb+srv://donaldle:cuong1990@cluster0.nolan.mongodb.net/myFirstDatabase?retryWrites=true&w=majority"
};` 
Enter fullscreen mode Exit fullscreen mode

Next, bring up the application locally and play with its API by running the following command:

 `node index.js` 
Enter fullscreen mode Exit fullscreen mode

Use Postman to create a new user with the API.

Image description
Figure 2: Creating a new user using the application API

Or create a new blog post:

Image description
Figure 3: Creating a new blog using the application API

Our blog management application is now working as expected. The next step is writing the tests.

Writing the Test

In order to make sure the existing functionalities of the application still work after adding new features, I added some tests for the application.

The tests are located in the tests directory. There are tests for authentication, user management, and blog management using Jest as a test runner as well as supertest to call HTTP requests. Below is an example of a test for an authentication API.

 `authenticate_user.test.js

const request = require("supertest")
const baseURL = "http://localhost:3000/v1"

describe("Authenticate user", () => {
    const authUser={
            email:"donald.le@iamondemand.com",
            password:"tatiana"
    }
    it("should return 200", async () => {
        const response = await request(baseURL).post("/auth").send(authUser);
        expect(response.statusCode).toBe(200);
    });
  });` 
Enter fullscreen mode Exit fullscreen mode

Here, I defined the request body to send for an authentication API, which includes the user’s email and password information. After calling the API, I expect the status code that returns from the API to equal 200.

Now we will run all the tests to see whether any issues arise.

 `npx jest tests` 
Enter fullscreen mode Exit fullscreen mode

Image description
Figure 4: Tests are run with pass results

As shown in Figure 4, it passed all the tests. Now we will move to creating GitHub Workflows for the application.

Create Github Workflow

GitHub Workflows help to automatically build the application whenever a change is applied to the GitHub repository. In addition, the API tests are executed so I will be notified if something goes wrong. It is possible to create workflow to build the application and execute the test in the GitHub agent environment for:

  • User management
  • Blog management
  • Authentication

GitHub workflows are defined in the .github folder in the Git repository. For example, the blog management workflow is written as follows:

 `name: Blog Management Workflow
on: [push]
jobs:
 build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Use Node.js
        uses: actions/setup-node@v3
        with:
        node-version: '16.x'
    - run: npm ci
    - run: node index.js &
    - run: npx jest --config ./jest.config.json --collectCoverage --coverageDirectory="./coverage" tests/blog_management/` 
Enter fullscreen mode Exit fullscreen mode

Here we have steps to check the code to the GitHub agent, install application dependencies, build the application, and finally execute the tests for the application. Whenever a code to the GitHub repository is updated, the workflow is automatically triggered.


Image description
Figure 6: Workflow results in GitHub Actions

After successfully applying the GitHub workflows for the application, the next step is adding a new feature to the application.

Applying API Cache for the Application

In order to boost the performance of the blog management APIs, especially for the APIs that are used for retrieving blog content, we can add the API caching mechanism using the express-api-cache library.

Navigate to the app/route/v1/post.js, update the code for getting the blog by its ID, and get all blogs, as shown below:

 `const router = require('express').Router();
const PostController = require(APP_CONTROLLER_PATH + 'post');
let postController = new PostController();
var cacheService = require("express-api-cache");
var cache = cacheService.cache;

router.get('/', cache("10 minutes"),postController.getAll);
router.get('/:id', cache("10 minutes"),postController.get);
router.post('/', postController.create);
router.delete('/:id', postController.remove);
router.put('/:id', postController.update);

module.exports = router;` 
Enter fullscreen mode Exit fullscreen mode

The API for getting all blogs and for getting one blog by its ID is now cached for 10 minutes. After that time, the existing cache will be invalidated and the new cache will be applied.

Pushing the new code to the GitHub repository will cause the blog management workflow to fail.

Image description
Figure 7: Recent workflow runs are failed for blog management

Why did the blog management workflow fail after applying the caching mechanism for the APIs? The next step is troubleshooting this problem.

Integrate GitHub Enterprise with Foresight

Foresight is ideal for monitoring workflows and troubleshooting tests in the application. You can easily integrate Foresight with GitHub Enterprise with just a few steps.

First, search for the Foresight application in the GitHub MarketPlace apps and the Foresight app for your GitHub Enterprise.

Image description
Figure 8: Foresight application in GitHub Apps Marketplace

Then add permissions in order to give Foresight the capabilities to access your repositories in GitHub.

Image description
Figure 9: Add permissions for Foresight to access GitHub repositories

The final steps are adding foresight-workflow-kit-action and foresight-test-kit-action to the existing workflows along with the Foresight API key.

 `name: Blog Management Workflow
on: [push]
jobs:
 build:
    runs-on: ubuntu-latest
    steps:
    - name: Collect Workflow Telemetry
        uses: runforesight/foresight-workflow-kit-action@v1
        if: success() || failure()
        with:
        api_key: b983b8fb-e108-483a-bcd7-fdabdb16944a
    - uses: actions/checkout@v3
    - name: Use Node.js
        uses: actions/setup-node@v3
        with:
        node-version: '16.x'
    - run: npm ci
    - run: node index.js &
    - run: npx jest --config ./jest.config.json --collectCoverage --coverageDirectory="./coverage" tests/blog_management/
    - run: ls
    - name: Foresight test kit
        if: success() || failure()
        uses: runforesight/foresight-test-kit-action@v1
        with:
        api_key: b983b8fb-e108-483a-bcd7-fdabdb16944a
        test_format: JUNIT
        test_framework: JEST
        test_path: junit.xml
        coverage_format: COBERTURA/XML
        coverage_path: ./coverage/cobertura-coverage.xml` 
Enter fullscreen mode Exit fullscreen mode

We should now be able to capture the workflow and test results from Foresight.

Image description
Figure 10: Successfully integrate Foresight with GitHub Enterprise

In the next section, we’ll look at how to debug the failed blog management workflow.

Debugging the Failed Test with Foresight

Looking at the Foresight highlights, you can see that all the workflow failures are from the blog management workflow.

Image description
Figure 11: Foresight workflow highlights

Scrolling down a little bit, you can see the overall status of the workflows.

Image description
Figure 12: Summary of workflows in Foresight

Let’s check the details of this problematic workflow for more information.

Image description
Figure 13: Find out the step that cause workflow failed

The step that caused the workflow to fail is the test execution.

Clicking on the “Tests” column, we see that 10 lines of new codes were added although I did not add any tests to it.

Image description
Figure 14: Check out the change impact analysis of the git repository

When we click on “See changes” we see that there are 4 files that were recently updated, including the app/route/v/post.js file.

Image description
Figure 15: Files that have been modified

By clicking on the app/route/v1/post.js file, you can see the line of code that was updated. In the post.js file, I updated 4 lines to apply a caching mechanism for the APIs to get blogs.

Image description
Figure 16: Details of the app/route/v1/posts.js file that has been modified

Next, let’s check the processes column in the workflow detail. You can see the process details for the test execution. The process file name in this step is:

 `home/runner/work/foresight-lc-m/foresight-lc-m/node_modules/.bin/jest` 
Enter fullscreen mode Exit fullscreen mode

And its process argument is:

 `"/usr/bin/env","node","/home/runner/work/foresight-lc-m/foresight-lc-m/node_modules/.bin/jest","--config","./jest.config.json","--collectCoverage","--coverageDirectory=./coverage","tests/blog_management/"‍` 
Enter fullscreen mode Exit fullscreen mode

Image description
Figure 17: Processes details captured by Foresight

Navigating to the “metrics” tab, you can see the metrics for CPU, memory, network, and disk.

Image description
Figure 18: CPU metrics for the current workflow capture

Image description
Figure 19: Memory metrics for the current workflow capture

Image description
Figure 20: Memory metrics for the current workflow capture

Image description
Figure 21: Disk metrics for the current workflow capture

Comparing the current network metrics to the previous network metrics of this workflow, you can see the difference in network IORx Mb value. The current network metric is only 7 MB, whereas in the previous network metrics the value was 9 MB.

Image description
Figure 22: Network metrics for the previous workflow capture

This makes sense because I already applied the caching mechanism for the APIs, so the API for getting blog content does not directly call the MongoDB to get the blog value; it only calls the caching content.

Let’s go to the test runs to see the details for every step in the test.

For the blog management workflow, we have two test suites. The failed one is “Update existing blog.”

Image description
Figure 23: Determining the problematic test suite

Click on “Update existing blog” to see the details.

Image description
Figure 24: Determining why the test failed

You can see that the test failed, because the expected value is “A new testing blog afterUpdateBlog,” whereas the received value is “A new testing blog beforeUpdateBlog.”

Next, check the test file for more details.

 ``beforeAll(async () => {
    // Get user token
    const response = await request(baseURL).post("/auth").send(authUser);
    expect(response.statusCode).toBe(200);
    userToken=response.body.data.token
    const responsePut = await request(baseURL).put(`/posts/${blogId}`).set("Authorization","JWT " + userToken).send(beforeUpdateBlog);
    expect(responsePut.statusCode).toBe(200);
    })
    it("should return 200", async () => {
    const responseGetBefore = await request(baseURL).get(`/posts/${blogId}`).set("Authorization","JWT "+userToken);
    expect(responseGetBefore.statusCode).toBe(200);
    expect(responseGetBefore.body.data.title).toBe(beforeUpdateBlog.title);

    const response = await request(baseURL).put(`/posts/${blogId}`).set("Authorization","JWT "+userToken).send(afterUpdateBlog);
    expect(response.statusCode).toBe(200);
    expect(response.body.data.title).toBe(afterUpdateBlog.title);

    const responseGet = await request(baseURL).get(`/posts/${blogId}`).set("Authorization","JWT "+userToken);
    expect(responseGet.statusCode).toBe(200);
    expect(responseGet.body.data.title).toBe(afterUpdateBlog.title);
    });`` 
Enter fullscreen mode Exit fullscreen mode

In the test for updating an existing blog, I have to change the step for the blog content to the initial value as “A new testing blog beforeUpdateBlog,” and then call to get the API blog to confirm the blog has been updated. Finally, I update the blog content to “A new testing blog afterUpdateBlog,” then call the get API blog to confirm.

The first time I call the get-API blog to confirm, the request is made to the MongoDB database. Its value will be cached because I implemented the caching mechanism. The second time when I call the get-API blog to confirm, the request is not made to the MongoDB database, but calls the cached value from the previous step. The cached value from the previous step is “A new testing blog beforeUpdateBlog,” even though I expected it to be “A new testing blog afterUpdateBlog.”

This is why the test for “Update existing blog” and the blog management workflow both failed.

Conclusion

Implementing workflows for automatically building and testing your web application is difficult; maintaining them over time is even harder. Without the right support tools, you could end up spending most of your time debugging failed workflows rather than implementing new features for your application that actually fulfill the business requirements.

Foresight does all the heavy lifting for debugging, tasks are already done. By simply looking at the Foresight highlights and process traces, you’ll know exactly which part of the workflow has problems so you can address them. This is even easier if you’re already using GitHub Enterprise: Foresight, now a partner with GitHub, provides the support you need when integrating with GitHub Enterprise.

Save yourself the frustration of scrolling repeatedly through GitHub logs for debugging tasks! Check out Foresight.

Top comments (0)