Skip to main content

One post tagged with "cicd"

View All Tags

GitHub β†’ CircleCI: Last production deploy

See time of last production deploy without leaving GitHub
πŸ”’
https://github.com/mycompany/webshop
preload

How does this help you?​

  • See when this repo was last deployed to production
  • Jump directly to the corresponding successful workflow run in CircleCI

Preview​

How it looks in the extension

ToolJumpβ€’circleciDeployed 5m agoβ€’ ... Other items ...

High level approach​

There are multiple ways to determine the last production deploy for a repository:

  1. Insights API by workflow name (simple): Query GET /insights/{project-slug}/workflows/{workflow_name} for the deploy-prod workflow on main and pick the most recent successful run.
  2. Pipelines β†’ Workflows traversal: List pipelines for main via GET /project/{project-slug}/pipeline?branch=main, then for each pipeline call GET /pipeline/{id}/workflow and find the latest successful deploy-prod workflow.
  3. Job-level heuristics: If workflow names vary, filter workflow runs whose names contain both β€œdeploy” and β€œprod”, or use a job name convention inside workflows.

The implementation below uses the Insights API (option 1) for simplicity.

Prerequisites​

For the code below to work, please follow the guide on Connecting to CircleCI. Provide CIRCLECI_API_TOKEN as a secret.

Code​

github.circleci.last-deploy.integration.example.js
module.exports = {
metadata: {
name: 'github-circleci-last-deploy',
description: 'Show when main was last deployed to production (deploy-prod workflow) and link to the workflow run',
match: {
contextType: 'github',
context: {
'url': { exists: true },
'page.repository': { startsWith: 'my-org/' }
}
},
requiredSecrets: ['CIRCLECI_API_TOKEN'],
cache: 300
},
run: async function (context, secrets = {}) {
const CIRCLE_HOST = 'https://circleci.com/api/v2';
const repo = context.page.repository; // e.g., "org/repo"
const projectSlug = `gh/${repo}`;
const workflowName = 'deploy-prod';
const branch = 'main';

// Helper to format "Deployed 3d ago" / "Deployed 3h ago"
function formatRelativeDuration(thenIso) {
const then = new Date(thenIso).getTime();
const now = Date.now();
const diffMs = Math.max(0, now - then);
const diffMin = Math.floor(diffMs / 60000);
const diffHr = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHr / 24);
if (diffDay >= 1) return `Deployed ${diffDay}d ago`;
if (diffHr >= 1) return `Deployed ${diffHr}h ago`;
return `Deployed ${Math.max(1, diffMin)}m ago`;
}

const headers = {
'Circle-Token': secrets.CIRCLECI_API_TOKEN,
'Accept': 'application/json'
};

// Insights API: latest runs for a workflow on a branch
const url = `${CIRCLE_HOST}/insights/${encodeURIComponent(projectSlug)}/workflows/${encodeURIComponent(workflowName)}?branch=${encodeURIComponent(branch)}`;
const resp = await fetch(url, { headers });
if (!resp.ok) {
throw new Error(`CircleCI API error: ${resp.status} ${resp.statusText}`);
}

const json = await resp.json();
const items = Array.isArray(json.items) ? json.items : [];
const success = items.find(i => i.status === 'success');
if (!success) {
return [];
}

const workflowId = success.id; // workflow UUID
const pipelineNumber = success.pipeline_number; // for deep link
const stoppedAt = success.stopped_at || success.created_at;

const relative = formatRelativeDuration(stoppedAt);
const link = `https://app.circleci.com/pipelines/gh/${repo}/${pipelineNumber}/workflows/${workflowId}`;

return [
{ type: 'link', content: relative, href: link, icon: 'circleci' }
];
}
};