Rich HTML content generation for creating detailed, formatted summaries that appear in GitHub Actions workflow runs. Job summaries provide a way to create persistent, formatted reports that remain visible after workflow completion.
Job summaries are HTML documents that can be built incrementally during workflow execution and are displayed in the GitHub Actions UI. They support rich formatting including tables, code blocks, images, and interactive elements.
The main summary object provides methods for building HTML content.
/**
* Main summary instance for building job summaries
*/
const summary: Summary;
interface Summary {
/** Write the summary buffer to file */
write(options?: SummaryWriteOptions): Promise<Summary>;
/** Clear buffer and summary file */
clear(): Promise<Summary>;
/** Get current buffer as string */
stringify(): string;
/** Check if buffer is empty */
isEmptyBuffer(): boolean;
/** Reset buffer without writing to file */
emptyBuffer(): Summary;
/** Add raw text to buffer */
addRaw(text: string, addEOL?: boolean): Summary;
/** Add end-of-line marker */
addEOL(): Summary;
/** Add code block */
addCodeBlock(code: string, lang?: string): Summary;
/** Add HTML list */
addList(items: string[], ordered?: boolean): Summary;
/** Add HTML table */
addTable(rows: SummaryTableRow[]): Summary;
/** Add collapsible details element */
addDetails(label: string, content: string): Summary;
/** Add image */
addImage(src: string, alt: string, options?: SummaryImageOptions): Summary;
/** Add heading */
addHeading(text: string, level?: number | string): Summary;
/** Add horizontal separator */
addSeparator(): Summary;
/** Add line break */
addBreak(): Summary;
/** Add blockquote */
addQuote(text: string, cite?: string): Summary;
/** Add anchor link */
addLink(text: string, href: string): Summary;
}Usage Examples:
import { summary } from '@actions/core';
// Basic summary usage
await summary
.addHeading('Build Results', 2)
.addRaw('Build completed successfully!')
.write();Writes the current summary buffer to the summary file and empties the buffer.
/**
* Write summary buffer to file
* @param options - Optional write configuration
* @returns Promise resolving to Summary instance
*/
write(options?: SummaryWriteOptions): Promise<Summary>;
interface SummaryWriteOptions {
/** Replace all existing content in summary file with buffer contents. Default: false */
overwrite?: boolean;
}Usage Examples:
import { summary } from '@actions/core';
// Append to existing summary
await summary
.addHeading('Test Results')
.addRaw('All tests passed!')
.write();
// Overwrite existing summary
await summary
.addHeading('Final Report')
.addRaw('Complete workflow summary')
.write({ overwrite: true });Add raw text and basic formatting to the summary.
/**
* Add raw text to summary buffer
* @param text - Content to add
* @param addEOL - Whether to append end-of-line marker. Default: false
* @returns Summary instance for chaining
*/
addRaw(text: string, addEOL?: boolean): Summary;
/**
* Add operating system-specific end-of-line marker
* @returns Summary instance for chaining
*/
addEOL(): Summary;Usage Examples:
import { summary } from '@actions/core';
await summary
.addRaw('Build started at: ')
.addRaw(new Date().toISOString())
.addEOL()
.addRaw('Status: ')
.addRaw('✅ Success', true) // true adds EOL
.write();Add HTML heading elements with configurable levels.
/**
* Add HTML section heading element
* @param text - Heading text
* @param level - Heading level (1-6). Default: 1
* @returns Summary instance for chaining
*/
addHeading(text: string, level?: number | string): Summary;Usage Examples:
import { summary } from '@actions/core';
await summary
.addHeading('Workflow Summary') // h1
.addHeading('Build Phase', 2) // h2
.addHeading('Test Results', 3) // h3
.addRaw('All tests passed successfully')
.write();Add syntax-highlighted code blocks to the summary.
/**
* Add HTML codeblock to summary buffer
* @param code - Content to render within code block
* @param lang - Optional language for syntax highlighting
* @returns Summary instance for chaining
*/
addCodeBlock(code: string, lang?: string): Summary;Usage Examples:
import { summary } from '@actions/core';
// JavaScript code block
await summary
.addHeading('Generated Code')
.addCodeBlock(`
function greet(name) {
return \`Hello, \${name}!\`;
}
`, 'javascript')
.write();
// JSON output
const config = { env: 'production', version: '1.2.3' };
await summary
.addHeading('Configuration')
.addCodeBlock(JSON.stringify(config, null, 2), 'json')
.write();
// Shell commands
await summary
.addHeading('Commands Executed')
.addCodeBlock(`
npm install
npm run build
npm test
`, 'bash')
.write();Add ordered and unordered HTML lists to the summary.
/**
* Add HTML list to summary buffer
* @param items - List items to render
* @param ordered - Whether list should be ordered (ol) or unordered (ul). Default: false
* @returns Summary instance for chaining
*/
addList(items: string[], ordered?: boolean): Summary;Usage Examples:
import { summary } from '@actions/core';
// Unordered list
const features = [
'User authentication',
'Data validation',
'Error handling',
'Logging system'
];
await summary
.addHeading('Features Implemented')
.addList(features)
.write();
// Ordered list for steps
const deploySteps = [
'Build application',
'Run tests',
'Create deployment package',
'Deploy to production',
'Run health checks'
];
await summary
.addHeading('Deployment Steps')
.addList(deploySteps, true) // ordered list
.write();Add HTML tables with support for headers, cell spanning, and complex layouts.
/**
* Add HTML table to summary buffer
* @param rows - Table rows data
* @returns Summary instance for chaining
*/
addTable(rows: SummaryTableRow[]): Summary;
type SummaryTableRow = (SummaryTableCell | string)[];
interface SummaryTableCell {
/** Cell content */
data: string;
/** Render cell as header. Default: false */
header?: boolean;
/** Number of columns the cell extends. Default: '1' */
colspan?: string;
/** Number of rows the cell extends. Default: '1' */
rowspan?: string;
}Usage Examples:
import { summary } from '@actions/core';
// Simple table with string cells
const simpleTable = [
['File', 'Status', 'Size'],
['app.js', '✅ Success', '45 KB'],
['config.json', '✅ Success', '2 KB'],
['README.md', '⚠️ Warning', '8 KB']
];
await summary
.addHeading('Build Results')
.addTable(simpleTable)
.write();
// Complex table with headers and spanning
const complexTable = [
[
{ data: 'Test Suite', header: true },
{ data: 'Results', header: true, colspan: '2' }
],
[
'Unit Tests',
'✅ 45 passed',
'❌ 2 failed'
],
[
'Integration Tests',
{ data: '✅ All passed', colspan: '2' }
]
];
await summary
.addHeading('Test Results')
.addTable(complexTable)
.write();
// Performance metrics table
const metricsTable = [
[
{ data: 'Metric', header: true },
{ data: 'Value', header: true },
{ data: 'Change', header: true }
],
['Bundle Size', '2.3 MB', '↓ 5%'],
['Load Time', '1.2s', '↓ 15%'],
['Test Coverage', '94%', '↑ 2%']
];
await summary
.addHeading('Performance Metrics')
.addTable(metricsTable)
.write();Add collapsible sections and other interactive elements.
/**
* Add collapsible HTML details element
* @param label - Text for the closed state (summary)
* @param content - Collapsible content
* @returns Summary instance for chaining
*/
addDetails(label: string, content: string): Summary;Usage Examples:
import { summary } from '@actions/core';
// Collapsible error details
const errorLog = `
Error: Connection timeout
at DatabaseClient.connect (db.js:45)
at async main (index.js:12)
at async run (runner.js:8)
`;
await summary
.addHeading('Build Issues')
.addDetails('Error Details', `<pre>${errorLog}</pre>`)
.write();
// Collapsible configuration
const configDetails = `
<h4>Environment Variables</h4>
<ul>
<li>NODE_ENV=production</li>
<li>API_URL=https://api.example.com</li>
<li>TIMEOUT=5000</li>
</ul>
`;
await summary
.addDetails('Configuration Details', configDetails)
.write();Add images and hyperlinks to the summary.
/**
* Add HTML image tag
* @param src - Path to image
* @param alt - Alt text description
* @param options - Image display options
* @returns Summary instance for chaining
*/
addImage(src: string, alt: string, options?: SummaryImageOptions): Summary;
/**
* Add HTML anchor tag
* @param text - Link text/content
* @param href - Hyperlink URL
* @returns Summary instance for chaining
*/
addLink(text: string, href: string): Summary;
interface SummaryImageOptions {
/** Image width in pixels */
width?: string;
/** Image height in pixels */
height?: string;
}Usage Examples:
import { summary } from '@actions/core';
// Add screenshot or diagram
await summary
.addHeading('Test Coverage Report')
.addImage('coverage-chart.png', 'Code coverage visualization', {
width: '600',
height: '400'
})
.addBreak()
.addLink('View full report', 'https://coverage.example.com/report')
.write();
// Multiple links
await summary
.addHeading('Deployment Links')
.addLink('Production Environment', 'https://app.example.com')
.addBreak()
.addLink('Staging Environment', 'https://staging.example.com')
.addBreak()
.addLink('Documentation', 'https://docs.example.com')
.write();Add visual separators and line breaks.
/**
* Add HTML thematic break (hr)
* @returns Summary instance for chaining
*/
addSeparator(): Summary;
/**
* Add HTML line break (br)
* @returns Summary instance for chaining
*/
addBreak(): Summary;
/**
* Add HTML blockquote
* @param text - Quote text
* @param cite - Optional citation URL
* @returns Summary instance for chaining
*/
addQuote(text: string, cite?: string): Summary;Usage Examples:
import { summary } from '@actions/core';
await summary
.addHeading('Deployment Summary')
.addRaw('Deployment completed successfully!')
.addSeparator() // Horizontal line
.addHeading('Notes', 2)
.addQuote('This deployment includes critical security updates.',
'https://security.example.com/advisory')
.addBreak()
.addRaw('Next deployment scheduled for next week.')
.write();import { summary, info } from '@actions/core';
async function buildProgressiveSummary() {
// Initialize summary
await summary
.addHeading('Workflow Execution Summary')
.addRaw(`Started at: ${new Date().toISOString()}`)
.write();
// Add build results
const buildResult = await runBuild();
await summary
.addHeading('Build Phase', 2)
.addRaw(`Status: ${buildResult.success ? '✅ Success' : '❌ Failed'}`)
.addRaw(`Duration: ${buildResult.duration}ms`)
.write();
// Add test results
const testResult = await runTests();
const testTable = [
['Test Type', 'Passed', 'Failed', 'Duration'],
['Unit', testResult.unit.passed.toString(), testResult.unit.failed.toString(), `${testResult.unit.duration}ms`],
['Integration', testResult.integration.passed.toString(), testResult.integration.failed.toString(), `${testResult.integration.duration}ms`]
];
await summary
.addHeading('Test Results', 2)
.addTable(testTable)
.write();
}import { summary, error } from '@actions/core';
async function createErrorSummary(errors: any[]) {
const errorRows = [
['File', 'Line', 'Error', 'Severity']
];
errors.forEach(err => {
errorRows.push([
err.file || 'Unknown',
err.line?.toString() || '-',
err.message,
err.severity || 'error'
]);
});
const errorDetails = errors.map(err =>
`**${err.file}:${err.line}**\n${err.message}\n\`\`\`\n${err.stack}\n\`\`\``
).join('\n\n');
await summary
.addHeading('❌ Build Failed')
.addRaw(`Found ${errors.length} errors during build process.`)
.addSeparator()
.addHeading('Error Summary', 2)
.addTable(errorRows)
.addSeparator()
.addDetails('Detailed Error Information', errorDetails)
.write();
}import { summary } from '@actions/core';
async function createPerformanceDashboard(metrics: any) {
const metricsTable = [
['Metric', 'Current', 'Previous', 'Change'],
['Bundle Size', `${metrics.bundleSize} KB`, `${metrics.previousBundleSize} KB`,
metrics.bundleSizeChange > 0 ? `↗️ +${metrics.bundleSizeChange}%` : `↘️ ${metrics.bundleSizeChange}%`],
['Load Time', `${metrics.loadTime}ms`, `${metrics.previousLoadTime}ms`,
metrics.loadTimeChange > 0 ? `↗️ +${metrics.loadTimeChange}ms` : `↘️ ${metrics.loadTimeChange}ms`],
['Test Coverage', `${metrics.coverage}%`, `${metrics.previousCoverage}%`,
metrics.coverageChange > 0 ? `↗️ +${metrics.coverageChange}%` : `↘️ ${metrics.coverageChange}%`]
];
await summary
.addHeading('📊 Performance Dashboard')
.addTable(metricsTable)
.addSeparator()
.addHeading('Lighthouse Scores', 2)
.addImage('lighthouse-scores.png', 'Lighthouse performance scores', {
width: '800'
})
.addBreak()
.addLink('Full Lighthouse Report', metrics.lighthouseUrl)
.write();
}/**
* Clear buffer and wipe summary file
* @returns Promise resolving to Summary instance
*/
clear(): Promise<Summary>;
/**
* Get current summary buffer as string
* @returns String content of buffer
*/
stringify(): string;
/**
* Check if summary buffer is empty
* @returns True if buffer has no content
*/
isEmptyBuffer(): boolean;
/**
* Reset buffer without writing to file
* @returns Summary instance for chaining
*/
emptyBuffer(): Summary;Usage Examples:
import { summary } from '@actions/core';
// Check if summary has content before writing
if (!summary.isEmptyBuffer()) {
await summary.write();
}
// Get summary content as string for debugging
const content = summary.stringify();
console.log('Summary content:', content);
// Clear everything and start fresh
await summary.clear();
// Reset buffer without affecting file
summary.emptyBuffer();Job summaries appear in the GitHub Actions UI as:
Summaries are ideal for: