Skip to main content

4 posts tagged with "deploys"

View All Tags

Github: Show deployed service URLs

Open staging and production URLs for this service instantly
đź”’
https://github.com/mycompany/webshop
preload

How does this help you?​

  • Quickly access the staging and production URLs for repositories

Preview​

How it looks in the extension

ToolJump•
Deployed URLs
• ... Other items ...

High level approach​

There are two common ways to map a repository to its deployed URLs:

  1. Data file (simple): Keep a small mapping file alongside your integrations that lists, for each repo, the staging and production URLs. This is easy to start with and version‑controlled.
  2. Service catalog (scalable): Query your source of truth (e.g., Backstage, Datadog Service Catalog) to resolve environment endpoints dynamically. This is preferable when you already maintain a catalog.

Below, we implement approach number 1 (the simple data file approach) and render a dropdown with links to staging and production.

Example data file (deployed-urls.data.json)​

{
"mappings": {
"company/webshop": {
"production": "https://webshop.company.com",
"staging": "https://staging.webshop.company.com"
},
"company/notifier": {
"production": "https://notifier.company.com",
"staging": "https://staging.notifier.company.com"
}
}
}

Prerequisites​

No external auth required. Add the data file (e.g., deployed-urls.data.json) to your data directory in your Github repository. Learn more about data files in Data.

Code​

github.deployed-urls.integration.example.js
module.exports = {
metadata: {
name: 'github-show-deployed-service-urls',
description: 'Show staging and production URLs for this repository as a dropdown, using a data file mapping',
match: {
contextType: 'github',
context: {
'page.repository': { startsWith: 'my-org/' }
}
},
requiredSecrets: [],
cache: 300
},
run: async function (context, secrets = {}, dataFiles = []) {
const repo = context.page.repository; // e.g., "company/webshop"

// Expect a data file like deployed-urls.data.json with shape:
// { "mappings": { "org/repo": { "production": "https://...", "staging": "https://..." } } }
const df = dataFiles.find(f => f.id === 'deployed-urls');
if (!df || !df.data) {
return [];
}

const mappings = df.data.mappings || {};
const envs = mappings[repo];
if (!envs || (typeof envs !== 'object')) {
return [];
}

const items = [];
if (envs.production) {
items.push({ content: 'Production', href: envs.production, status: 'important', icon: 'link' });
}
if (envs.staging) {
items.push({ content: 'Staging', href: envs.staging, icon: 'link' });
}

if (items.length === 0) {
return [];
}

return [
{
type: 'dropdown',
content: 'Deployed URLs',
items
}
];
}
};

GitHub: Next deploy time

Next deploy countdown — know when changes go live
đź”’
https://github.com/mycompany/webshop
preload

How does this help you?​

  • Quick, readable countdown for the next production deploy

Preview​

How it looks in the extension

ToolJump•Next deploy in 2h• ... Other items ...

High level approach​

Hardcode your organization’s deployment schedule (or read it via an API call from your CI/CD system) and compute the next matching slot relative to the current time. This example uses:

  • Days: Tuesday, Wednesday, Thursday
  • Times: 09:00, 15:00, 20:00 (local time)

It returns a simple text item like Next deploy: 1d 3h

Prerequisites​

No external auth or data files required.

Code​

github.next-deploy.integration.example.js
module.exports = {
metadata: {
name: 'github-next-deploy',
description: 'Show when the repository will be deployed next based on a hardcoded schedule',
match: {
contextType: 'github',
context: {
'page.repository': { startsWith: 'my-org/' }
}
},
requiredSecrets: [],
cache: 60
},
run: async function (context, secrets = {}, dataFiles = []) {
// Hardcoded deployment schedule:
// Days: Tuesday, Wednesday, Thursday
// Times (local time): 09:00, 15:00, 20:00
const allowedDays = new Set([2, 3, 4]); // 0=Sun ... 6=Sat
const allowedHours = [9, 15, 20];

const now = new Date();

function nextScheduled(nowDate) {
const candidates = [];
for (let d = 0; d <= 7; d++) { // look up to one week ahead
const probe = new Date(nowDate);
probe.setDate(nowDate.getDate() + d);
const day = probe.getDay();
if (!allowedDays.has(day)) continue;
for (const h of allowedHours) {
const slot = new Date(probe);
slot.setHours(h, 0, 0, 0);
if (slot > nowDate) {
candidates.push(slot.getTime());
}
}
}
if (candidates.length === 0) return null;
const ts = Math.min(...candidates);
return new Date(ts);
}

function formatDiff(from, to) {
const ms = Math.max(0, to.getTime() - from.getTime());
const HOUR = 60 * 60 * 1000;
const DAY = 24 * HOUR;
const days = Math.floor(ms / DAY);
const hours = Math.floor((ms - days * DAY) / HOUR);
if (days > 0) return `Next deploy: ${days}d ${hours}h`;
return `Next deploy: ${hours}h`;
}

const next = nextScheduled(now);
if (!next) return [];

return [
{ type: 'text', status: 'relevant', content: formatDiff(now, next) }
];
}
};

tip

You can also change the status to values like important or relevant (or do not set status) depending on how soon the next deploy is. For example, if the next deploy is in 1h, it should be important and show with red. If it is in 3 hours, it can be shown wih yellow as relevant.

GitHub → CircleCI: Browse deployment

How does this help you?​

  • One-click to browse the exact Git commit deployed to each environment
  • Reduces guesswork when debugging differences between environments

Preview​

How it looks in the extension

ToolJump•• ... Other items ...

Approaches​

  • Insights by workflow name (implemented): Use CircleCI Insights for conventional workflow names (e.g., deploy-staging, deploy-prod) on branch main, find the latest successful run, resolve the pipeline to get the commit SHA, and link to the GitHub tree. Simple and widely applicable.
  • Pipelines + workflows traversal: List recent pipelines for the repo/branch, then fetch workflows per pipeline to find the most recent successful deploy job per environment. More API calls, similar outcome.
  • Artifact or metadata lookup: Emit an artifact or store metadata (e.g., commit SHA, release tag, URL) during deploy jobs and fetch it via the Artifacts API. More setup, flexible if your org already publishes metadata.

Assumptions​

  • Branch: main. Adjust in the example if your default deploy branch differs.
  • Workflows: deploy-staging and deploy-prod. Rename to match your pipeline.
  • Output: a dropdown labeled Browse deployment with Staging and Prod items linking to https://github.com/<org>/<repo>/tree/<sha>.

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.browse.deployment.integration.example.js
module.exports = {
metadata: {
name: 'github-circleci-browse-deployment',
description: 'Dropdown to browse the deployed revisions (staging, QA) on GitHub using CircleCI workflows',
match: {
contextType: 'github',
context: {
'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 branch = 'main';

// Conventional workflow names used by many teams; customize if needed
const WORKFLOWS = {
staging: 'deploy-staging',
prod: 'deploy-prod'
};

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

async function getDeployedShaForWorkflow(workflowName) {
// Use Insights to find the most recent successful run for the workflow on the target branch
const insightsUrl = `${CIRCLE_HOST}/insights/${encodeURIComponent(projectSlug)}/workflows/${encodeURIComponent(workflowName)}?branch=${encodeURIComponent(branch)}`;
const r1 = await fetch(insightsUrl, { headers });
if (!r1.ok) throw new Error(`CircleCI insights error (${workflowName}): ${r1.status} ${r1.statusText}`);
const j1 = await r1.json();
const items = Array.isArray(j1.items) ? j1.items : [];
const success = items.find(i => i.status === 'success');
if (!success) return null;

// Resolve pipeline id from pipeline number so we can get VCS revision (commit SHA)
const byNumberUrl = `${CIRCLE_HOST}/project/${encodeURIComponent(projectSlug)}/pipeline/${success.pipeline_number}`;
const r2 = await fetch(byNumberUrl, { headers });
if (!r2.ok) throw new Error(`CircleCI pipeline lookup error: ${r2.status} ${r2.statusText}`);
const j2 = await r2.json();
const pipelineId = j2.id;
if (!pipelineId) return null;

const pipelineUrl = `${CIRCLE_HOST}/pipeline/${pipelineId}`;
const r3 = await fetch(pipelineUrl, { headers });
if (!r3.ok) throw new Error(`CircleCI pipeline fetch error: ${r3.status} ${r3.statusText}`);
const j3 = await r3.json();
const sha = j3.vcs && j3.vcs.revision;
return sha || null;
}

const [stagingSha, prodSha] = await Promise.all([
getDeployedShaForWorkflow(WORKFLOWS.staging),
getDeployedShaForWorkflow(WORKFLOWS.prod)
]);

const items = [];
if (stagingSha) {
items.push({ content: 'Staging', href: `https://github.com/${repo}/tree/${stagingSha}` });
}
if (prodSha) {
items.push({ content: 'Prod', href: `https://github.com/${repo}/tree/${prodSha}` });
}

if (items.length === 0) return [];

return [{
type: 'dropdown',
content: 'Browse deployment',
icon: 'github',
items
}];
}
};

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' }
];
}
};