or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

environment-state.mdindex.mdinput-output.mdjob-summaries.mdlogging-annotations.mdoidc-tokens.mdoutput-grouping.mdpath-utilities.mdplatform-detection.md
tile.json

job-summaries.mddocs/

Job Summaries

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.

Overview

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.

Capabilities

Summary Instance

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();

Write Summary

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 });

Text Content

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();

Headings

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();

Code Blocks

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();

Lists

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();

Tables

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();

Interactive Elements

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();

Media and Links

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();

Formatting Elements

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();

Common Patterns

Progressive Summary Building

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();
}

Error Report Summary

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();
}

Performance Dashboard

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();
}

Summary Management

/**
 * 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();

Summary Display

Job summaries appear in the GitHub Actions UI as:

  • Persistent content: Remains visible after workflow completion
  • Rich formatting: Full HTML rendering with CSS styling
  • Interactive elements: Collapsible sections, clickable links
  • Mobile responsive: Optimized for viewing on different screen sizes
  • Integrated display: Appears in workflow run details and can be linked from PR checks

Summaries are ideal for:

  • Build and test reports
  • Deployment summaries
  • Performance metrics
  • Error diagnostics
  • Documentation generation
  • Status dashboards