Home / Function/ run() — dead-code-hunter Function Reference

run() — dead-code-hunter Function Reference

Architecture documentation for the run() function in index.ts from the dead-code-hunter codebase.

Entity Profile

Dependency Diagram

graph TD
  2707a308_9fa9_efa8_2fbd_febf984897e5["run()"]
  e9e74cdc_be9b_7bf4_8d35_36946c34cb62["createZipArchive()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| e9e74cdc_be9b_7bf4_8d35_36946c34cb62
  d58f9df4_f889_6148_ffb9_17e807228538["generateIdempotencyKey()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| d58f9df4_f889_6148_ffb9_17e807228538
  110ea7f3_8de2_f010_244b_fee04cd36588["pollForResult()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| 110ea7f3_8de2_f010_244b_fee04cd36588
  06636363_5986_b74f_155b_e1b4310ee84a["filterByIgnorePatterns()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| 06636363_5986_b74f_155b_e1b4310ee84a
  54526593_a8f4_ba9f_228e_754b8aa089c8["getChangedFiles()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| 54526593_a8f4_ba9f_228e_754b8aa089c8
  73125a5b_e60f_fe3f_510e_f54d8809eb51["filterByChangedFiles()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| 73125a5b_e60f_fe3f_510e_f54d8809eb51
  36781569_21cd_f406_4e3c_4e1ebae94da9["formatPrComment()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| 36781569_21cd_f406_4e3c_4e1ebae94da9
  d8391dca_c2e7_cb94_03ef_2b8ad084cde7["safeSerialize()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| d8391dca_c2e7_cb94_03ef_2b8ad084cde7
  e9f67350_005a_e85e_cb5a_a57d13ad57ef["redactSensitive()"]
  2707a308_9fa9_efa8_2fbd_febf984897e5 -->|calls| e9f67350_005a_e85e_cb5a_a57d13ad57ef
  style 2707a308_9fa9_efa8_2fbd_febf984897e5 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

src/index.ts lines 191–367

async function run(): Promise<void> {
  try {
    const apiKey = core.getInput('supermodel-api-key', { required: true }).trim();

    if (!apiKey.startsWith('smsk_')) {
      core.warning('API key format looks incorrect. Get your key at https://dashboard.supermodeltools.com');
    }

    const commentOnPr = core.getBooleanInput('comment-on-pr');
    const failOnDeadCode = core.getBooleanInput('fail-on-dead-code');
    const ignorePatterns: string[] = JSON.parse(core.getInput('ignore-patterns') || '[]');

    const workspacePath = process.env.GITHUB_WORKSPACE || process.cwd();

    core.info('Dead Code Hunter starting...');

    // Step 1: Create zip archive
    const zipPath = await createZipArchive(workspacePath);

    // Step 2: Generate idempotency key
    const idempotencyKey = await generateIdempotencyKey(workspacePath);

    // Step 3: Call Supermodel dead code analysis API
    core.info('Analyzing codebase with Supermodel...');

    const config = new Configuration({
      basePath: process.env.SUPERMODEL_BASE_URL || 'https://api.supermodeltools.com',
      apiKey: apiKey,
    });

    const api = new DefaultApi(config);

    const zipBuffer = await fs.readFile(zipPath);
    const zipBlob = new Blob([zipBuffer], { type: 'application/zip' });

    const result = await pollForResult(api, idempotencyKey, zipBlob);

    // Step 4: Apply client-side ignore patterns
    let candidates = filterByIgnorePatterns(result.deadCodeCandidates, ignorePatterns);

    // Step 5: Scope to PR diff when running on a pull request
    const token = core.getInput('github-token') || process.env.GITHUB_TOKEN;
    let changedFiles: Set<string> | null = null;

    if (github.context.payload.pull_request && token) {
      changedFiles = await getChangedFiles(token);
      if (changedFiles) {
        const totalBeforeScoping = candidates.length;
        candidates = filterByChangedFiles(candidates, changedFiles);
        core.info(`Scoped to PR: ${candidates.length} findings in changed files (${totalBeforeScoping} total across repo, ${changedFiles.size} files in PR)`);
      }
    }

    core.info(`Found ${candidates.length} potentially unused code elements (${result.metadata.totalDeclarations} declarations analyzed)`);
    core.info(`Analysis method: ${result.metadata.analysisMethod}`);
    core.info(`Alive: ${result.metadata.aliveCode}, Entry points: ${result.entryPoints.length}, Root files: ${result.metadata.rootFilesCount ?? 'n/a'}`);
    for (const dc of candidates) {
      core.info(`  [${dc.confidence}] ${dc.type} ${dc.name} @ ${dc.file}:${dc.line} — ${dc.reason}`);
    }

    // Step 6: Set outputs
    core.setOutput('dead-code-count', candidates.length);
    core.setOutput('dead-code-json', JSON.stringify(candidates));

    // Step 7: Post PR comment if enabled
    if (commentOnPr && github.context.payload.pull_request) {
      if (token) {
        const octokit = github.getOctokit(token);
        const comment = formatPrComment(candidates, result.metadata);

        await octokit.rest.issues.createComment({
          owner: github.context.repo.owner,
          repo: github.context.repo.repo,
          issue_number: github.context.payload.pull_request.number,
          body: comment,
        });

        core.info('Posted findings to PR');
      } else {
        core.warning('GITHUB_TOKEN not available, skipping PR comment');
      }
    }

    // Step 8: Clean up
    await fs.unlink(zipPath);

    // Step 9: Fail if configured and dead code found
    if (candidates.length > 0 && failOnDeadCode) {
      core.setFailed(`Found ${candidates.length} potentially unused code elements`);
    }

  } catch (error: any) {
    core.info('--- Error Debug Info ---');
    core.info(`Error type: ${error?.constructor?.name ?? 'unknown'}`);
    core.info(`Error message: ${error?.message ?? 'no message'}`);
    core.info(`Error name: ${error?.name ?? 'no name'}`);

    try {
      if (error?.response) {
        core.info(`Response status: ${error.response.status ?? 'unknown'}`);
        core.info(`Response statusText: ${error.response.statusText ?? 'unknown'}`);
        core.info(`Response data: ${safeSerialize(error.response.data)}`);
        core.debug(`Response headers: ${safeSerialize(redactSensitive(error.response.headers))}`);
      }
      if (error?.body) {
        core.info(`Error body: ${safeSerialize(error.body)}`);
      }
      if (error?.status) {
        core.info(`Error status: ${error.status}`);
      }
      if (error?.statusCode) {
        core.info(`Error statusCode: ${error.statusCode}`);
      }
      if (error?.cause) {
        core.debug(`Error cause: ${safeSerialize(error.cause)}`);
      }
    } catch {
      core.debug('Failed to serialize some error properties');
    }
    core.info('--- End Debug Info ---');

    let errorMessage = 'An unknown error occurred';
    let helpText = '';

    const status = error?.response?.status || error?.status || error?.statusCode;
    let apiMessage = '';

    try {
      apiMessage =
        error?.response?.data?.message ||
        error?.response?.data?.error ||
        error?.response?.data ||
        error?.body?.message ||
        error?.body?.error ||
        error?.message ||
        '';
      if (typeof apiMessage !== 'string') {
        apiMessage = safeSerialize(apiMessage, 500);
      }
    } catch {
      apiMessage = '';
    }

    if (status === 401) {
      errorMessage = 'Invalid API key';
      helpText = 'Get your key at https://dashboard.supermodeltools.com';
    } else if (status === 500) {
      errorMessage = apiMessage || 'Internal server error';

      if (apiMessage.includes('Nested archives')) {
        helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' +
          'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' +
          'Example: tests/fixtures/*.zip export-ignore';
      } else if (apiMessage.includes('exceeds maximum')) {
        helpText = 'Your repository or a file within it exceeds size limits. ' +
          'Consider excluding large files using .gitattributes with "export-ignore".';
      }
    } else if (status === 413) {
      errorMessage = 'Repository archive too large';
      helpText = 'Reduce archive size by excluding large files in .gitattributes';
    } else if (status === 429) {
      errorMessage = 'Rate limit exceeded';
      helpText = 'Please wait before retrying';
    } else if (status) {
      errorMessage = apiMessage || `API error (${status})`;
    } else {
      errorMessage = apiMessage || error?.message || 'An unknown error occurred';
    }

    core.error(`Error: ${errorMessage}`);
    if (helpText) {
      core.error(`Help: ${helpText}`);
    }

    core.setFailed(errorMessage);
  }
}

Domain

Subdomains

Frequently Asked Questions

What does run() do?
run() is a function in the dead-code-hunter codebase.
What does run() call?
run() calls 9 function(s): createZipArchive, filterByChangedFiles, filterByIgnorePatterns, formatPrComment, generateIdempotencyKey, getChangedFiles, pollForResult, redactSensitive, and 1 more.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free