Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.deployhub.cloud/llms.txt

Use this file to discover all available pages before exploring further.

DeployHub provides comprehensive build tracking and monitoring capabilities to help you understand your deployment pipeline and troubleshoot issues.

Build Model

Each build is tracked as a document in the Build collection:
// From build.model.js:3-25
const buildSchema = new mongoose.Schema({
  project: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "Project",
    required: true,
    index: true
  },

  commitSha: String,

  status: {
    type: String,
    enum: ['pending', 'success', 'failed'],
    default: 'pending'
  },

  startedAt: Date,
  finishedAt: Date,
  dockerImage: String,
  logUrl: String
}, { timestamps: true });

buildSchema.index({ project: 1, createdAt: -1 });
Build Statuses:
  • pending - Build queued or in progress
  • success - Build completed successfully
  • failed - Build encountered an error
Builds are indexed by project and creation time for fast historical queries.

Build Lifecycle

1. Build Creation

A new build is created when deployment starts:
// From createDeployment.controller.js:153-156
const newBuild = new Model.Build({
  project: newProject._id,
  commitSha: commitSha,
});

await newBuild.save({ validateBeforeSave: false });

2. Build Queuing

Build is added to BullMQ queue:
// From createDeployment.controller.js:201
buildqueue.add("buildqueue", { 
  buildId: newBuild._id.toString(), 
  projectId: newProject._id.toString() 
});

3. Build Processing

Build worker picks up the job:
// From buildworker.js:34-41
await Model.Build.findByIdAndUpdate(buildData._id, {
  status: "pending",
  startedAt: new Date()
});

await Model.Project.findByIdAndUpdate(projectData._id, {
  status: "building"
});

4. Docker Build Logs

Build progress is streamed to stdout:
// From buildworker.js:109-121
await new Promise((resolve, reject) => {
  docker.modem.followProgress(
    tarStream,
    (err, res) => (err ? reject(err) : resolve(res)),
    (event) => {
      if (event.stream) process.stdout.write(event.stream);
      if (event.error) {
        console.error("Docker build error:", event.error);
        reject(new Error(event.error));
      }
    },
  );
});
Example Build Output:
Step 1/8 : FROM node:20.19.5 AS build
 ---> abc123def456
Step 2/8 : WORKDIR /app
 ---> Running in xyz789
 ---> def456ghi789
Step 3/8 : COPY package*.json ./
 ---> 123abc456def
Step 4/8 : RUN npm ci
 ---> Running in 789xyz012
npm WARN deprecated package@1.0.0
added 1234 packages in 45s

5. Image Push Logs

Image push progress is also streamed:
// From buildworker.js:161-173
const pushStream = await image.push({
  authconfig: {
    username: dockerusername,
    password: dockerpassword,
  },
});

await new Promise((resolve, reject) => {
  docker.modem.followProgress(
    pushStream,
    (err, res) => (err ? reject(err) : resolve(res)),
    (event) => {
      if (event.stream) process.stdout.write(event.stream);
      if (event.error) {
        console.error("image push error:", event.error);
        reject(new Error(event.error));
      }
    },
  );
});

6. Build Completion

Successful builds update the database:
// From buildworker.js:236-240
await Model.Build.findByIdAndUpdate(buildData._id, {
  status: "success",
  finishedAt: new Date(),
  dockerImage: imageName
});

7. Build Failure

Failed builds are marked accordingly:
// From buildworker.js:267-279
buildWorker.on("failed", async (job, err) => {
  await Model.Build.findByIdAndUpdate(
    job.data.buildId,
    { status: "failed", finishedAt: new Date() },
    { validateBeforeSave: false },
  );

  await Model.Project.findByIdAndUpdate(
    job.data.projectId,
    { status: "failed-deploy" },
    { validateBeforeSave: false },
  );
  
  console.log(`worker failed build on id ${job.id} with error ${err}`);
});

Worker Event Logging

Build Worker Events

// From buildworker.js:259-284
buildWorker.on("active", async (job) => {
  console.log(`worker start build working on id ${job.id}`);
});

buildWorker.on("completed", async (job) => {
  await deploymentQueue.add("deploymentQueue", job.data);
});

buildWorker.on("failed", async (job, err) => {
  console.log(`worker failed build on id ${job.id} with error ${err}`);
});

Deployment Worker Events

// From deployworker.js:98-154
worker.on("completed", async (job) => {
  const { projectId } = job.data;
  const projectData = await Model.Project.findById(projectId);
  projectData.status = 'live';
  await projectData.save({ validateBeforeSave: false });
  
  console.log(`Deployment job ${job.id} completed successfully.`);
});

worker.on("failed", async (job, err) => {
  const { projectId } = job.data;
  const projectData = await Model.Project.findById(projectId);
  projectData.status = 'failed-deploy';
  await projectData.save({ validateBeforeSave: false });
  
  console.log(`Deployment job ${job.id} failed with error: ${err.message}`);
});

Redeployment Worker Events

// From reDeploy.worker.js:444-479
reDeployMentWorker.on("active", async (job) => {
  console.log(`ReDeployment Worker started for project ID ${job.data}`);
});

reDeployMentWorker.on("completed", async (job) => {
  const project = await Model.Project.findById(job.data);
  project.status = "live";
  await project.save({ validateBeforeSave: false });
  
  console.log(`ReDeployment Worker completed for project ID ${job.data}`);
});

reDeployMentWorker.on("failed", async (job, err) => {
  const project = await Model.Project.findById(job.data);
  project.status = "failed-deploy";
  await project.save({ validateBeforeSave: false });
  
  console.error(`ReDeployment Worker failed for project ID ${job.data}:`, err);
});

Retrieving Build History

Get All Builds for Project

// From Buildscontroller.js:4-29
export const getProjectBuilds = async (req, res) => {
  const project = await Model.Project.findOne({
    _id: req.params.id,
    owner: req.user._id,
    status: { $ne: 'deleted' },
  }).select('_id totalBuilds').lean();

  if (!project) {
    return res.status(404).json({ 
      success: false, 
      message: 'Project not found' 
    });
  }

  const builds = await Model.Build.find({ project: req.params.id })
    .select('commitSha status startedAt finishedAt logUrl createdAt')
    .sort({ createdAt: -1 })  // Most recent first
    .limit(50)
    .lean();

  res.status(200).json({
    success: true,
    total: project.totalBuilds || builds.length,
    builds,
  });
};
API Endpoint:
GET /api/projects/:id/builds
Response:
{
  "success": true,
  "total": 15,
  "builds": [
    {
      "_id": "507f1f77bcf86cd799439022",
      "commitSha": "a1b2c3d4e5f6",
      "status": "success",
      "startedAt": "2025-03-04T10:15:30.000Z",
      "finishedAt": "2025-03-04T10:17:45.000Z",
      "logUrl": null,
      "createdAt": "2025-03-04T10:15:00.000Z"
    },
    {
      "_id": "507f1f77bcf86cd799439021",
      "commitSha": "f6e5d4c3b2a1",
      "status": "failed",
      "startedAt": "2025-03-03T14:20:10.000Z",
      "finishedAt": "2025-03-03T14:21:05.000Z",
      "createdAt": "2025-03-03T14:20:00.000Z"
    }
  ]
}
Build history is limited to 50 most recent builds per project for performance.

Get Single Build Details

// From Buildscontroller.js:32-54
export const getBuildById = async (req, res) => {
  const project = await Model.Project.findOne({
    _id: req.params.id,
    owner: req.user._id,
    status: { $ne: 'deleted' },
  }).select('_id').lean();

  if (!project) {
    return res.status(404).json({ 
      success: false, 
      message: 'Project not found' 
    });
  }

  const build = await Model.Build.findOne({
    _id: req.params.buildId,
    project: req.params.id,
  }).select('commitSha status startedAt finishedAt logUrl dockerImage createdAt').lean();

  if (!build) {
    return res.status(404).json({ 
      success: false, 
      message: 'Build not found' 
    });
  }

  res.status(200).json({ success: true, build });
};
API Endpoint:
GET /api/projects/:id/builds/:buildId
Response:
{
  "success": true,
  "build": {
    "_id": "507f1f77bcf86cd799439022",
    "commitSha": "a1b2c3d4e5f6",
    "status": "success",
    "startedAt": "2025-03-04T10:15:30.000Z",
    "finishedAt": "2025-03-04T10:17:45.000Z",
    "dockerImage": "deployhub/507f1f77bcf86cd799439011:507f1f77bcf86cd799439022",
    "logUrl": null,
    "createdAt": "2025-03-04T10:15:00.000Z"
  }
}

Build Duration Calculation

Calculate build time from timestamps:
const buildDurationMs = new Date(build.finishedAt) - new Date(build.startedAt);
const buildDurationSeconds = Math.floor(buildDurationMs / 1000);
const buildDurationMinutes = Math.floor(buildDurationSeconds / 60);

console.log(`Build took ${buildDurationMinutes}m ${buildDurationSeconds % 60}s`);
// Output: "Build took 2m 15s"

Container Logs

For Node.js applications, container logs can be retrieved:
// From deployworker.js:82
await container.logs({ stdout: true, stderr: true });
Retrieving logs manually:
const container = docker.getContainer(containerId);
const logs = await container.logs({
  stdout: true,
  stderr: true,
  tail: 100,    // Last 100 lines
  timestamps: true
});

Build Statistics

Project model tracks total build count:
// From project.model.js:67-70
totalBuilds: {
  type: Number,
  default: 0
}
Incremented on each build:
// From createDeployment.controller.js:135
newProject.totalBuilds += 1;
await newProject.save({ validateBeforeSave: false });

Commit SHA Tracking

Each build stores the Git commit SHA:
// From createDeployment.controller.js:116-126
const response = await fetch(
  `https://api.github.com/repos/${owner}/${repo}/git/ref/heads/${branchname}`,
  { headers }
);

const data = await response.json();
commitSha = data?.object?.sha || null;

const newBuild = new Model.Build({
  project: newProject._id,
  commitSha: commitSha,
});
Commit SHA enables intelligent redeployments - DeployHub checks if code has changed before rebuilding.

Redeployment Intelligence

Redeployment worker checks commit history:
// From reDeploy.worker.js:70-74
const isBuildFailed = buildData.status === "failed";
const isNewCommit = commitSha && buildData.commitSha !== commitSha;

if (isBuildFailed || isNewCommit) {
  // Build new image
} else {
  // Reuse existing image
}
Rebuild triggers:
  • Previous build failed
  • New commit detected (SHA mismatch)
Reuse existing image when:
  • Same commit SHA
  • Previous build succeeded

Error Handling

Build errors are captured and logged:
// From buildworker.js:242-244
catch (error) {
  console.log("error in build worker", error);
  throw error;
}
Errors trigger the worker’s failed event, which:
  1. Updates build status to failed
  2. Updates project status to failed-deploy
  3. Logs error details

Build Cleanup

Build artifacts are cleaned up automatically:
// From buildworker.js:245-251
finally {
  const buildFilePath = path.join("builds", job.data.buildId.toString());
  if (fs.existsSync(buildFilePath)) {
    fs.rmSync(buildFilePath, { recursive: true, force: true });
    console.log("Cleaned up build files");
  }
}
Build directories are removed after processing to conserve disk space.

Monitoring Recommendations

Real-Time Monitoring

// Poll build status every 5 seconds
const pollBuild = async (projectId, buildId) => {
  const response = await fetch(`/api/projects/${projectId}/builds/${buildId}`);
  const { build } = await response.json();
  
  if (build.status === 'pending') {
    setTimeout(() => pollBuild(projectId, buildId), 5000);
  } else {
    console.log('Build completed:', build.status);
  }
};

Build Status Indicators

const getStatusColor = (status) => {
  switch (status) {
    case 'pending': return 'yellow';
    case 'success': return 'green';
    case 'failed': return 'red';
    default: return 'gray';
  }
};

const getStatusIcon = (status) => {
  switch (status) {
    case 'pending': return '⏳';
    case 'success': return '✅';
    case 'failed': return '❌';
    default: return '❓';
  }
};

Common Build Issues

Build Timeout

Builds may timeout if they take too long:
  • Optimize dependencies (use npm ci instead of npm install)
  • Reduce build output size
  • Check for infinite loops in build scripts

Out of Memory

Large builds may exceed memory limits:
  • Reduce concurrent build processes
  • Optimize webpack/vite configuration
  • Use smaller dependencies

Docker Build Errors

Common Docker issues:
  • Missing Dockerfile
  • Invalid build arguments
  • File permission issues
  • Network connectivity problems

Image Push Failures

Push errors usually indicate:
  • Docker Hub authentication issues
  • Network connectivity problems
  • Insufficient disk space
  • Registry rate limits

API Reference Summary

Get Project Builds

GET /api/projects/:id/builds
Returns up to 50 most recent builds.

Get Build Details

GET /api/projects/:id/builds/:buildId
Returns detailed information for a specific build.

Future Enhancements

Planned Features

  • Real-time WebSocket log streaming
  • Build log storage and retrieval
  • Build metrics and analytics
  • Build performance insights
  • Email notifications on build failure
  • Slack/Discord integration
Currently, logs are written to stdout/stderr. Log persistence to object storage is planned for future releases.