diff --git a/.github/scripts/changelog.js b/.github/scripts/changelog.js index 3372552..b4875e4 100644 --- a/.github/scripts/changelog.js +++ b/.github/scripts/changelog.js @@ -1,61 +1,126 @@ /** * GitHub Username Resolution and Release Notes Generator * - * This module resolves GitHub usernames from commit email addresses and generates - * release notes using OpenAI's AI service. + * This module generates release notes by first fetching commits locally using git, + * then enriching them with GitHub usernames and pull request information via the + * GitHub API, and finally generating human-readable changelogs using GitHub Models API. + * + * USAGE: + * ------ + * 1. Set up environment variables (see below) + * 2. Run via npm script: `npm run changelog:test -- "1.2.3"` + * 3. Or import and use: `import { generateChangelog } from './.github/scripts/changelog.js'` + * + * TESTING LOCALLY: + * ---------------- + * Create a GitHub personal access token. The token needs to have models:read permissions. + * See Managing your personal access tokens: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens + * Save your token as an environment variable: + * + * # Set environment variables + * export GITHUB_TOKEN="your_github_token_here" + * + * # Run the test script + * npm run changelog:test -- "1.2.3" + * + * # Or test with different versions + * npm run changelog:test -- "2.0.0" * * Required Environment Variables: - * ----------------------------- - * GITHUB_TOKEN: Personal Access Token (PAT) for GitHub API - * - Required for higher rate limits and access to private data + * ------------------------------ + * GITHUB_TOKEN: Personal Access Token (PAT) for GitHub API and Models + * - Required for username resolution, pull request lookup, and AI model access * - Generate at: https://github.com/settings/tokens * - Minimum required scopes: + * * `models:read` - For accessing GitHub Models API (AI generation) * * `read:user` - For user email lookup - * * `repo` - For accessing repository commits + * * `repo` - For accessing repository commits and pull requests + * - For testing: Use a classic token with `models:read`, `repo`, and `read:user` scopes * - * OPENAI_API_KEY: OpenAI API Key - * - Found in your OpenAI dashboard or account settings + * HOW IT WORKS: + * ------------- + * 1. Fetches commits locally using git between the latest release tag and HEAD + * - This allows the script to run on any branch or pull request + * - No GitHub API rate limits for basic commit retrieval + * 2. For each commit, resolves GitHub usernames from commit email addresses via GitHub API + * 3. Looks up associated pull request information for each commit via GitHub API + * - Handles both merge-squash and rebase-commit scenarios + * 4. Filters out merge commits and applies conventional commit parsing + * 5. Sends processed commit data to GitHub Models API for human-readable changelog generation + * 6. Returns formatted markdown changelog content + * + * TROUBLESHOOTING: + * ---------------- + * - If username resolution fails: Check GITHUB_TOKEN scopes and rate limits + * - If PR lookup fails: Ensure GITHUB_TOKEN has `repo` scope for the repository + * - If AI generation fails: Verify GITHUB_TOKEN has `models:read` scope + * - If no commits found: Ensure git tags exist and fetch-depth is sufficient + * - For rate limiting: The script includes automatic retry logic with delays */ import { execFileSync } from 'node:child_process'; import https from 'node:https'; +import OpenAI from 'openai'; -const OPENAI_MODEL = 'gpt-4o-mini-2024-07-18'; +const ENDPOINT = 'https://models.github.ai/inference'; +const MODEL = 'openai/gpt-4.1'; const PROMPT = ` -You're the head of developer relations at a SaaS. Write a concise, professional, and fun changelog, prioritizing important changes. - -Header is provided externally. Focus on grouping commits logically under these sections with H3 level headers: "New Features ✨", "Bug Fixes πŸ›", "Improvements πŸ› ", and "Breaking Changes 🚨". - -Ignore merge commits and minor changes. For each commit, use only the first line before any dash (\`-\`) or line break. - -Translate Conventional Commit messages into professional, human-readable language, avoiding technical jargon. - -For each commit, use this format: -- **Bold 3-5 word Summary** {optional related GitHub emoji}: Continuation with 1-3 sentence description. @author (optional #PR) - - Sub-bullets for key details (include only if necessary) - -Place PR/issue numbers matching the exact pattern #\\d+ (e.g., #123) at the end of the section in parentheses. - -Do not use commit hashes as PR numbers. - -If no PR/issue number is found matching #\\d+, omit the parenthetical reference entirely. - -If the author is specified, include their GitHub username at the end of the section, just before the PR/issue number with a "@" symbol - e.g. @author. - -If the author is not specified, omit the GitHub username. - -Only include sub-bullets if they are necessary to clarify the change. - -Do not include any sections with no content. - -Do not include sections where there are no grouped changes. - -Do not include sections where content is similar to "No breaking changes in this release". - -Avoid level 4 headings; use level 3 (###) for sections. - -Attempt to add an emoji into the {optional related GitHub emoji} section of the summary that relates to the bold-3-5 word summary and 1-3 sentence description. - -Omit sections with no content silently - do not add any notes or explanations about omitted sections. +You're the head of developer relations at a SaaS company. Write a concise, professional, and engaging changelog that prioritizes user-impacting changes and tells a story about the release. + +Content Priority (High to Low): +1. User-facing features and fixes - anything that changes user experience +2. API/interface changes - breaking changes, new endpoints, deprecations +3. Performance improvements - measurable speed/memory improvements +4. Developer experience - tooling, documentation, setup improvements +5. Internal dependencies - only include if security-related or enables new features +6. Build/CI changes - generally omit unless they affect end users + +Section Structure: +Use H3 headers only when you have substantial content. Organize logically under these sections: +- 🚨 Breaking Changes (always first if present) +- ✨ New Features +- πŸ› Bug Fixes +- ⚑ Performance (for performance-specific improvements) +- πŸ›  Improvements (enhancements to existing features) +- πŸ“š Documentation (significant doc updates only) +- πŸ”§ Developer Experience (tooling, dev workflow improvements) + +Formatting Rules: +For each entry, use this format: +- **Bold 3-5 Word Summary** {contextual emoji}: Clear description in 1-2 sentences explaining the impact/benefit. @author (#PR) + - Sub-bullets only for complex changes requiring clarification + +Content Guidelines: +- Focus on impact over implementation - explain what users can now do, not how it was built +- Use active voice - "Added dark mode" not "Dark mode was added" +- Ignore trivial changes: dependency bumps (unless security), merge commits, whitespace, minor typos +- Group related commits - combine multiple commits for the same feature +- For Conventional Commits: Transform technical prefixes into user-friendly language + - feat: β†’ focus on the new capability + - fix: β†’ describe what now works correctly + - perf: β†’ quantify the improvement when possible + - docs: β†’ only include if significantly helpful to users + +Technical Details: +- Extract first line of commit messages before any dash (-) or line break +- Include author as @username if available in the author field +- Use pullRequest.number for PR references (e.g., #123) when available +- If no pull request associated with commit, omit PR reference entirely +- Skip sections with no meaningful content +- Use contextual emojis that relate to the specific change, not just the section +- Never use H4 headings + +Tone & Style: +- Professional yet approachable +- Celebrate improvements with enthusiasm +- Focus on user benefits over technical details +- Keep it scannable - users should quickly understand what changed and why they care + +Omit Silently: +- Empty sections +- Dependency updates unless security-critical +- Internal refactoring that doesn't affect users +- Minor formatting/linting changes +- Routine maintenance tasks `; // In-memory cache for username lookups @@ -75,7 +140,7 @@ function sleep(ms) { * Validates required environment variables */ function validateEnvironment() { - const requiredEnvVars = ['GITHUB_TOKEN', 'OPENAI_API_KEY']; + const requiredEnvVars = ['GITHUB_TOKEN']; // 'OPENAI_API_KEY']; const missing = requiredEnvVars .filter((envVar) => !process.env[envVar]) @@ -182,26 +247,40 @@ async function resolveGitHubUsername(email) { // Extract username from 1234567+username@users.noreply.github.com // or username@users.noreply.github.com const matches = email.match(/^(?:(\d+)\+)?([^@]+)@users\.noreply\.github\.com$/); - return matches ? matches[2] : null; + if (matches) { + console.log(`Matched to: ${matches[2]}`); + return matches[2]; + } + return null; } // Handle organization emails like username@organization.github.com if (domain.endsWith('.github.com')) { const matches = email.match(/^([^@]+)@[^@]+\.github\.com$/); - return matches ? matches[1] : null; + if (matches) { + console.log(`Matched to: ${matches[1]}`); + return matches[1]; + } + return null; } // Handle GitHub Enterprise emails // Pattern: username@github.{enterprise}.com const enterpriseMatches = email.match(/^([^@]+)@github\.[^@]+\.com$/); if (enterpriseMatches) { + console.log(`Matched to: ${enterpriseMatches[1]}`); return enterpriseMatches[1]; } // Handle GitHub staff emails if (email.endsWith('@github.com')) { const matches = email.match(/^([^@]+)@github\.com$/); - return matches ? matches[1] : null; + if (matches) { + console.log(`Matched to: ${matches[1]}`); + return matches[1]; + } + + return null; } } @@ -270,12 +349,13 @@ async function getGitHubUsername(email) { /** * Retrieves all commits between the specified latest release tag and HEAD, - * including the commit hash, author email, GitHub username (if found), and - * commit message. Filters out merge commits following a specific pattern. + * including author email, GitHub username (if found), commit message, and + * associated pull request information. Filters out merge commits following + * a specific pattern. * * @param {string} latestVersionTag - The latest release tag to compare against HEAD. * @returns {Promise} A promise that resolves to an array of processed - * commit objects, each containing the commit hash, GitHub username, and message. + * commit objects, each containing the GitHub username, message, and PR info. * @throws {Error} If git command execution fails. */ async function getCommitsBetweenLatestReleaseAndMain(latestVersionTag) { @@ -300,20 +380,35 @@ async function getCommitsBetweenLatestReleaseAndMain(latestVersionTag) { return !message.match(/^Merge [a-f0-9]+ into [a-f0-9]+/); }); - console.log('Filtered commits:'); - console.log(commitEntries); + // Extract unique email addresses from all commits + const uniqueEmails = [...new Set(commitEntries.map((entry) => entry.split('|')[1]))]; - // Process the filtered commits - const commits = []; - for (const entry of commitEntries) { - const [commitHash, commitEmail, commitMessage] = entry.split('|'); - const username = await getGitHubUsername(commitEmail); - commits.push({ - hash: commitHash, + // Pre-populate the username cache by processing all unique emails in parallel + console.log(`Pre-loading usernames for ${uniqueEmails.length} unique email addresses...`); + await Promise.all(uniqueEmails.map((email) => getGitHubUsername(email))); + + // Process the filtered commits getting usernames (from cache) and PR information in parallel + const commitPromises = commitEntries.map(async (entry) => { + const [commitSha, commitEmail, commitMessage] = entry.split('|'); + + // Username lookup will now hit the cache, PR lookup still needs API call + const [username, pullRequest] = await Promise.all([ + getGitHubUsername(commitEmail), // This will be cached + getPullRequestForCommit(commitSha), + ]); + + return { author: username, message: commitMessage.trim(), - }); - } + pullRequest: pullRequest, + }; + }); + + // Wait for all commits to be processed + const commits = await Promise.all(commitPromises); + + console.log(`${commits.length} commits:`); + console.log(commits); return commits; } catch (error) { @@ -321,6 +416,43 @@ async function getCommitsBetweenLatestReleaseAndMain(latestVersionTag) { } } +/** + * Gets pull request information for a commit SHA. + * Uses the GitHub API to find pull requests associated with a specific commit. + * Handles both merge-squash scenarios and rebase-commit scenarios. + * + * @param {string} commitSha - The commit SHA to look up + * @returns {Promise} - Pull request object with number and title, or null if not found + */ +async function getPullRequestForCommit(commitSha) { + try { + console.log(`[${commitSha}] Looking up pull request information`); + + // Use GitHub API to get pull requests associated with this commit + const pullRequests = await githubApiRequestWithRetry( + `/repos/techpivot/terraform-module-releaser/commits/${commitSha}/pulls`, + ); + + if (pullRequests && pullRequests.length > 0) { + // Return the first (most relevant) pull request + const pr = pullRequests[0]; + console.log(`[${commitSha}] Found PR #${pr.number}: ${pr.title}`); + + return { + number: pr.number, + title: pr.title, + url: pr.html_url, + }; + } + + console.log(`[${commitSha}] No pull request found`); + return null; + } catch (error) { + console.error(`[${commitSha}] Error looking up pull request:`, error); + return null; + } +} + /** * Fetches the latest release tag from the GitHub repository. * @@ -329,6 +461,7 @@ async function getCommitsBetweenLatestReleaseAndMain(latestVersionTag) { * @returns {Promise} Returns nothing. Logs the latest tag to the console. */ async function getLatestReleaseTag() { + console.log('Fetching latest release tag from GitHub...'); try { const response = await fetch('https://api.github.com/repos/techpivot/terraform-module-releaser/releases/latest'); @@ -347,54 +480,46 @@ async function getLatestReleaseTag() { } /** - * Main function to generate changelog from commits using GitHub and OpenAI APIs. + * Main function to generate changelog from commits using GitHub and GitHub Models APIs. * * This function: * - Validates environment variables - * - Retrieves commits between HEAD and origin/main + * - Retrieves commits between HEAD and latest release tag * - Resolves GitHub usernames for commit authors - * - Sends commit data to OpenAI to generate a formatted changelog + * - Sends commit data to GitHub Models API to generate a formatted changelog * + * @param {string} version - The version number for the release * @returns {Promise} - Generated changelog content * @throws {Error} - If environment variables are missing or API requests fail */ async function generateChangelog(version) { + validateEnvironment(); + // Strip the leading "v" if it's a prefix const versionNumber = version.startsWith('v') ? version.slice(1) : version; const latestVersionTag = await getLatestReleaseTag(); - validateEnvironment(); + console.log(`Generating changelog for version: ${versionNumber}`); const commits = await getCommitsBetweenLatestReleaseAndMain(latestVersionTag); - console.log('Commits:'); - console.debug(commits); + + console.log(`Found ${commits.length} commits between ${latestVersionTag} and main`); + console.log('Commits:', JSON.stringify(commits, null, 2)); try { - const response = await fetch('https://api.openai.com/v1/chat/completions', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, - }, - body: JSON.stringify({ - model: OPENAI_MODEL, - messages: [ - { - role: 'system', - content: PROMPT, - }, - { - role: 'user', - content: JSON.stringify(commits), - }, - ], - }), + const client = new OpenAI({ baseURL: ENDPOINT, apiKey: process.env.GITHUB_TOKEN }); + const response = await client.chat.completions.create({ + messages: [ + { role: 'system', content: PROMPT }, + { role: 'user', content: JSON.stringify(commits) }, + ], + temperature: 0.3, // Low temperature for consistent, focused output (0.0-2.0, lower = more deterministic) + top_p: 0.9, // High nucleus sampling to maintain quality while reducing randomness (0.0-1.0) + model: MODEL, }); - const data = await response.json(); - - console.log('Changelog'); - console.dir(data); + console.log('Generated changelog content:'); + console.log(response.choices[0].message.content); return [ '# ✨ Release Notes Preview', @@ -403,13 +528,50 @@ async function generateChangelog(version) { ``, '', `## ${versionNumber} (${getDateString()})\n`, - data.choices[0].message.content, + response.choices[0].message.content, `\n###### Full Changelog: https://github.com/techpivot/terraform-module-releaser/compare/${latestVersionTag}...v${versionNumber}`, ].join('\n'); } catch (error) { - console.error('Error querying OpenAI:', error); + console.error('Error generating changelog:', error); + throw error; } } // Export the main function for external usage export { generateChangelog }; + +/** + * CLI interface for testing the changelog generator locally. + * + * Usage: node .github/scripts/changelog.js [version] + * Example: node .github/scripts/changelog.js "1.2.3" + */ +async function main() { + // Check if this script is being run directly (not imported) + if (import.meta.url === `file://${process.argv[1]}`) { + const version = process.argv[2]; + + if (!version) { + console.error('❌ Error: Version argument is required'); + console.error('Usage: node .github/scripts/changelog.js [version]'); + console.error('Example: node .github/scripts/changelog.js "1.2.3"'); + process.exit(1); + } + + try { + const changelog = await generateChangelog(version); + + console.log('βœ… Changelog generated successfully!\n'); + console.log('πŸ“‹ Generated Changelog:'); + console.log('='.repeat(80)); + console.log(changelog); + console.log('='.repeat(80)); + } catch (error) { + console.error('❌ Error generating changelog:', error.message); + process.exit(1); + } + } +} + +// Run main function if this script is executed directly +main().catch(console.error); diff --git a/.github/workflows/release-start.yml b/.github/workflows/release-start.yml index 3a5225d..3bfc72f 100644 --- a/.github/workflows/release-start.yml +++ b/.github/workflows/release-start.yml @@ -62,8 +62,7 @@ jobs: uses: actions/github-script@v7 id: changelog env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GH_TOKEN_READ_AND_MODELS }} with: result-encoding: json script: | diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index 28ac280..7d3989b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ maintains independence while living in the same repository, with proper isolatio Additionally, the action generates a beautifully crafted wiki for each module, complete with readme information, usage examples, Terraform-docs details, and a full changelog. +Compatible with both GitHub.com and GitHub Enterprise Server (GHES) – works seamlessly in cloud and on-premises +environments. + ## πŸš€ Features - **Efficient Module Tagging** – Only includes module directory content, dramatically improving Terraform performance. @@ -70,17 +73,20 @@ example of how to use this action in a monorepo setup. See real-world usage in a ## Getting Started -### Step 1: Ensure GitHub Wiki is Enabled +### Step 1: Enable and Initialize GitHub Wiki + +Before using this action, you'll need to enable the wiki feature for your repository: -Before using this action, make sure that the wiki is enabled and initialized for your repository: +1. Go to your repository's homepage +1. Navigate to the **Settings** tab +1. Under the **Features** section, check the **Wikis** option to enable GitHub Wiki +1. Click on the **Wiki** tab in your repository +1. Click **Create the first page** button +1. Add a simple title (like "Home") and some content +1. Click **Save Page** to initialize the wiki -1. Go to your repository's homepage. -1. Navigate to the **Settings** tab. -1. Under the **Features** section, ensure the **Wikis** option is checked to enable the GitHub Wiki. -1. Navigate to the **Wiki** tab on your repository. -1. Click the **Create the first page** button and add a basic title like **Home** to initialize the wiki with an initial - commit. -1. Save the changes to ensure your wiki is not empty when the GitHub Action updates it with module information. +> This initialization step is necessary because GitHub doesn't provide an API to programmatically enable or initialize +> the wiki. ### Step 2: Configure the Action @@ -116,6 +122,22 @@ reasonably configured. If you need to customize additional parameters, please refer to [Input Parameters](#input-parameters) section below. +## GitHub Enterprise Server (GHES) Support + +This action is fully compatible with GitHub Enterprise Server deployments: + +- **Automatic Detection**: The action automatically detects when running on GHES and adjusts API endpoints accordingly +- **Wiki Generation**: Full wiki support works on GHES instances with wiki features enabled +- **Release Management**: Creates releases and tags using your GHES instance's API +- **No Additional Configuration**: Works out-of-the-box on GHES without requiring special configuration +- **SSH Source Format**: Use the use-ssh-source-format parameter for GHES environments that prefer SSH-based Git URLs + +### GHES Requirements + +- GitHub Enterprise Server version that supports GitHub Actions +- Wiki feature enabled on your GHES instance (contact your administrator if wikis are disabled) +- Appropriate permissions for the GitHub Actions runner to access repository features + ## Permissions Before executing the GitHub Actions workflow, ensure that you have the necessary permissions set for accessing pull @@ -360,7 +382,7 @@ by Piotr Krukowski. - **100% GitHub-based**: This action has no external dependencies, eliminating the need for additional authentication and complexity. Unlike earlier variations that stored built module assets in external services like Amazon S3, this action keeps everything within GitHub, providing a self-contained and streamlined solution for managing Terraform - modules. + modules. Works seamlessly with both GitHub.com and GitHub Enterprise Server environments. - **Pull Request-based workflow**: This action runs on the pull_request event, using pull request comments to track permanent releases tied to commits. This method ensures persistence, unlike Action Artifacts, which expire. As a result, the module does not support non-PR workflows, such as direct pushes to the default branch. diff --git a/__mocks__/config.ts b/__mocks__/config.ts index 815a2fc..26a0a7e 100644 --- a/__mocks__/config.ts +++ b/__mocks__/config.ts @@ -16,7 +16,7 @@ const defaultConfig: Config = { minorKeywords: ['feat', 'feature'], patchKeywords: ['fix', 'chore'], defaultFirstTag: 'v1.0.0', - terraformDocsVersion: 'v0.19.0', + terraformDocsVersion: 'v0.20.0', deleteLegacyTags: false, disableWiki: false, wikiSidebarChangelogMax: 10, diff --git a/__tests__/context.test.ts b/__tests__/context.test.ts index 0d61896..f837d46 100644 --- a/__tests__/context.test.ts +++ b/__tests__/context.test.ts @@ -177,6 +177,34 @@ describe('context', () => { }); expect(getContext().prBody).toEqual(''); }); + + it('should use custom GITHUB_API_URL when provided', () => { + const customApiUrl = 'https://github.example.com/api/v3'; + vi.stubEnv('GITHUB_API_URL', customApiUrl); + + // Clear context to force reinitialization + clearContextForTesting(); + + const context = getContext(); + + // Check that the context was created (which means the custom API URL was used) + expect(context).toBeDefined(); + expect(context.octokit).toBeDefined(); + }); + + it('should use default GITHUB_API_URL when not provided', () => { + // Ensure GITHUB_API_URL is not set to test the default fallback + vi.stubEnv('GITHUB_API_URL', undefined); + + // Clear context to force reinitialization + clearContextForTesting(); + + const context = getContext(); + + // Check that the context was created with default API URL + expect(context).toBeDefined(); + expect(context.octokit).toBeDefined(); + }); }); describe('context proxy', () => { @@ -185,14 +213,14 @@ describe('context', () => { const getterRepo = getContext().repo; expect(proxyRepo).toEqual(getterRepo); expect(startGroup).toHaveBeenCalledWith('Initializing Context'); - expect(info).toHaveBeenCalledTimes(9); + expect(info).toHaveBeenCalledTimes(11); // Reset mock call counts/history via mockClear() vi.mocked(info).mockClear(); vi.mocked(startGroup).mockClear(); // Second access should not trigger initialization - const prNumber = context.prNumber; + const prNumber = context.prNumber; // Intentionally access a property with no usage expect(startGroup).not.toHaveBeenCalled(); expect(info).not.toHaveBeenCalled(); }); diff --git a/__tests__/fixtures/_Footer.md b/__tests__/fixtures/_Footer.md index 44b9c89..7612996 100644 --- a/__tests__/fixtures/_Footer.md +++ b/__tests__/fixtures/_Footer.md @@ -1 +1 @@ -

Powered by techpivot/terraform-module-releaser

\ No newline at end of file +

Powered by:   techpivot/terraform-module-releaser

\ No newline at end of file diff --git a/__tests__/pull-request.test.ts b/__tests__/pull-request.test.ts index a1f96e4..3bd9228 100644 --- a/__tests__/pull-request.test.ts +++ b/__tests__/pull-request.test.ts @@ -444,16 +444,12 @@ describe('pull-request', () => { expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( expect.objectContaining({ - body: expect.stringContaining( - '**Note**: The following Terraform modules no longer exist in source; however, corresponding tags/releases exist.', - ), + body: expect.stringContaining('**⚠️ The following module no longer exists in source but has tags/releases.'), }), ); expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( expect.objectContaining({ - body: expect.stringContaining( - 'Automation tag/release deletion is **enabled** and corresponding tags/releases will be automatically deleted.
', - ), + body: expect.stringContaining('It will be automatically deleted.'), }), ); @@ -464,16 +460,7 @@ describe('pull-request', () => { expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( expect.objectContaining({ - body: expect.stringContaining( - '**Note**: The following Terraform modules no longer exist in source; however, corresponding tags/releases exist.', - ), - }), - ); - expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.stringContaining( - 'Automation tag/release deletion is **disabled** β€” **no** subsequent action will take place.
', - ), + body: expect.stringContaining('⏸️ Existing tags and releases will be **preserved**'), }), ); }); @@ -503,37 +490,180 @@ describe('pull-request', () => { ); }); - it('should include modules to remove when specified', async () => { + it('should include modules to remove when flag enabled', async () => { + const modulesToRemove = ['legacy-module1', 'legacy-module2']; + + stubOctokitReturnData('issues.createComment', { + data: { id: 1, html_url: 'https://github.com/org/repo/pull/1#issuecomment-1' }, + }); + stubOctokitReturnData('issues.listComments', { data: [] }); + config.set({ deleteLegacyTags: true }); + + await addReleasePlanComment([], modulesToRemove, { status: WikiStatus.SUCCESS }); + + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('- `legacy-module1`'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('- `legacy-module2`'), + }), + ); + }); + + it('should not include modules to remove when flag disabled ', async () => { const modulesToRemove = ['legacy-module1', 'legacy-module2']; stubOctokitReturnData('issues.createComment', { data: { id: 1, html_url: 'https://github.com/org/repo/pull/1#issuecomment-1' }, }); stubOctokitReturnData('issues.listComments', { data: [] }); + config.set({ deleteLegacyTags: false }); await addReleasePlanComment([], modulesToRemove, { status: WikiStatus.SUCCESS }); + const createCommentCalls = vi.mocked(context.octokit.rest.issues.createComment).mock.calls; + expect(createCommentCalls.length).toBeGreaterThanOrEqual(1); + + // Get the comment body text from the first call + const commentBody = createCommentCalls[0]?.[0]?.body as string; + + // Ensure both modules are not included in the body + expect(commentBody).not.toContain('`legacy-module1`'); + expect(commentBody).not.toContain('`legacy-module2`'); + expect(commentBody).toContain('⏸️ Existing tags and releases will be **preserved**'); + }); + + it('should handle cleanup when delete-legacy-tags is enabled but no modules to remove', async () => { + const newCommentId = 12345; + config.set({ deleteLegacyTags: true }); + stubOctokitReturnData('issues.createComment', { + data: { id: newCommentId, html_url: 'https://github.com/org/repo/pull/1#issuecomment-1' }, + }); + stubOctokitReturnData('issues.listComments', { data: [] }); + + await addReleasePlanComment(terraformChangedModules, [], { status: WikiStatus.SUCCESS }); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( expect.objectContaining({ - body: expect.stringContaining('`legacy-module1`, `legacy-module2`'), + body: expect.stringContaining( + 'βœ… All tags and releases are synchronized with the codebase. No cleanup required.', + ), }), ); }); + it('should handle multiple modules to remove with plural warning message', async () => { + const newCommentId = 12345; + const terraformModuleNamesToRemove = ['aws/module1', 'aws/module2', 'gcp/module3']; + config.set({ deleteLegacyTags: true }); + stubOctokitReturnData('issues.createComment', { + data: { id: newCommentId, html_url: 'https://github.com/org/repo/pull/1#issuecomment-1' }, + }); + stubOctokitReturnData('issues.listComments', { data: [] }); + + await addReleasePlanComment(terraformChangedModules, terraformModuleNamesToRemove, { + status: WikiStatus.SUCCESS, + }); + + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('**⚠️ The following modules no longer exist in source but have tags/releases.'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('They will be automatically deleted.'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('- `aws/module1`'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('- `aws/module2`'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('- `gcp/module3`'), + }), + ); + }); + + it('should handle wiki failure status with error message', async () => { + const newCommentId = 12345; + const errorMessage = 'Repository does not have wiki enabled'; + stubOctokitReturnData('issues.createComment', { + data: { id: newCommentId, html_url: 'https://github.com/org/repo/pull/1#issuecomment-1' }, + }); + stubOctokitReturnData('issues.listComments', { data: [] }); + + await addReleasePlanComment([], [], { + status: WikiStatus.FAILURE, + errorMessage, + }); + + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('**⚠️ Failed to checkout wiki:**'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('```'), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining(errorMessage), + }), + ); + expect(context.octokit.rest.issues.createComment).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.stringContaining('Please consult the [README.md]'), + }), + ); + }); + + it('should exclude branding when disabled', async () => { + const newCommentId = 12345; + config.set({ disableBranding: true }); + stubOctokitReturnData('issues.createComment', { + data: { id: newCommentId, html_url: 'https://github.com/org/repo/pull/1#issuecomment-1' }, + }); + stubOctokitReturnData('issues.listComments', { data: [] }); + + await addReleasePlanComment(terraformChangedModules, [], { status: WikiStatus.SUCCESS }); + + const createCommentCalls = vi.mocked(context.octokit.rest.issues.createComment).mock.calls; + expect(createCommentCalls.length).toBeGreaterThanOrEqual(1); + + // Get the comment body text from the first call + const commentBody = createCommentCalls[0]?.[0]?.body as string; + + // Ensure branding is not included + expect(commentBody).not.toContain(BRANDING_COMMENT); + }); + it('should handle different wiki statuses', async () => { const cases = [ { status: WikiStatus.SUCCESS, - expectedContent: 'βœ… Wiki Check', + expectedContent: 'βœ… Enabled', }, { status: WikiStatus.FAILURE, errorMessage: 'Failed to clone', - expectedContent: '⚠️ Wiki Check: Failed to checkout wiki.', + expectedContent: '**⚠️ Failed to checkout wiki:**', }, { status: WikiStatus.DISABLED, - expectedContent: '🚫 Wiki Check: Generation is disabled', + expectedContent: '🚫 Wiki generation **disabled** via `disable-wiki` flag.', }, ]; diff --git a/__tests__/terraform-docs.test.ts b/__tests__/terraform-docs.test.ts index e9b9108..cd8efd2 100644 --- a/__tests__/terraform-docs.test.ts +++ b/__tests__/terraform-docs.test.ts @@ -40,7 +40,7 @@ vi.mock('node:util', () => ({ })); describe('terraform-docs', async () => { - const terraformDocsVersion = 'v0.19.0'; + const terraformDocsVersion = 'v0.20.0'; const mockExecFileSync = vi.mocked(execFileSync); const mockWhichSync = vi.mocked(which.sync); const fsExistsSyncMock = vi.mocked(existsSync); diff --git a/__tests__/terraform-module.test.ts b/__tests__/terraform-module.test.ts index 5ac64fa..19bdf14 100644 --- a/__tests__/terraform-module.test.ts +++ b/__tests__/terraform-module.test.ts @@ -234,10 +234,17 @@ describe('terraform-module', () => { expect(s3Module).toBeDefined(); expect(vpcModule).toBeDefined(); - for (const module of modules) { - expect('isChanged' in module).toBe(false); - expect(module.latestTag).toBeDefined(); - expect(module.latestTagVersion).toBeDefined(); + // Check the s3 module specifically + if (s3Module) { + expect('isChanged' in s3Module).toBe(false); + expect(s3Module.latestTag).toBeDefined(); + expect(s3Module.latestTagVersion).toBeDefined(); + } + // Check the vpc module specifically + if (vpcModule) { + expect('isChanged' in vpcModule).toBe(false); + expect(vpcModule.latestTag).toBeDefined(); + expect(vpcModule.latestTagVersion).toBeDefined(); } }); @@ -250,6 +257,9 @@ describe('terraform-module', () => { }, ]; config.set({ moduleChangeExcludePatterns: ['*.md'] }); + + // Ensure vpc-endpoint has tags so it's not auto-marked as changed due to initial release logic + // This is already covered by mockTags which includes vpc-endpoint tags const modules = getAllTerraformModules(commitsWithExcludedFiles, mockTags, mockReleases); const vpcModule = modules.find((m) => m.moduleName === 'tf-modules/vpc-endpoint'); @@ -267,7 +277,10 @@ describe('terraform-module', () => { ); expect(vi.mocked(info)).toHaveBeenCalledWith('Finished analyzing directory tree, terraform modules, and commits'); expect(vi.mocked(info)).toHaveBeenCalledWith(expect.stringMatching(/Found \d+ terraform modules./)); - expect(vi.mocked(info)).toHaveBeenCalledWith('Found 0 changed Terraform modules.'); + expect(vi.mocked(info)).toHaveBeenCalledWith( + `Marking module 'tf-modules/kms' for initial release (no existing tags found)`, + ); + expect(vi.mocked(info)).toHaveBeenCalledWith('Found 1 changed Terraform module.'); }); it('should handle excluded files based on patterns and changed terraform-files', () => { @@ -388,7 +401,10 @@ describe('terraform-module', () => { }, ]; - const modules = getAllTerraformModules(commitsWithIgnoredPath, mockTags, mockReleases); + // Add a tag for the kms module to prevent it from being auto-marked as changed for initial release + const tagsWithKms = [...mockTags, 'tf-modules/kms/v1.0.0']; + + const modules = getAllTerraformModules(commitsWithIgnoredPath, tagsWithKms, mockReleases); // The module shouldn't be marked as changed even though there are changes in the examples directory const kmsModule = modules.find((m) => m.moduleName === 'tf-modules/kms'); diff --git a/__tests__/utils/constants.test.ts b/__tests__/utils/constants.test.ts index 3c9b103..dcaab89 100644 --- a/__tests__/utils/constants.test.ts +++ b/__tests__/utils/constants.test.ts @@ -32,12 +32,12 @@ describe('constants', () => { }); it('should have the correct branding comment HTML', () => { - const expectedBrandingComment = `

Powered by techpivot/terraform-module-releaser

`; + const expectedBrandingComment = `

Powered by:   techpivot/terraform-module-releaser

`; expect(BRANDING_COMMENT).toBe(expectedBrandingComment); }); it('should have the correct branding wiki HTML', () => { - const expectedBrandingWiki = `

Powered by techpivot/terraform-module-releaser

`; + const expectedBrandingWiki = `

Powered by:   techpivot/terraform-module-releaser

`; expect(BRANDING_WIKI).toBe(expectedBrandingWiki); }); diff --git a/__tests__/utils/string.test.ts b/__tests__/utils/string.test.ts index c31bad2..ac7e862 100644 --- a/__tests__/utils/string.test.ts +++ b/__tests__/utils/string.test.ts @@ -1,4 +1,4 @@ -import { removeTrailingDots, trimSlashes } from '@/utils/string'; +import { removeTrailingCharacters, trimSlashes } from '@/utils/string'; import { describe, expect, it } from 'vitest'; describe('utils/string', () => { @@ -34,23 +34,63 @@ describe('utils/string', () => { }); }); - describe('removeTrailingDots', () => { + describe('removeTrailingDots (deprecated)', () => { it('should remove all trailing dots from a string', () => { - expect(removeTrailingDots('hello...')).toBe('hello'); - expect(removeTrailingDots('module-name..')).toBe('module-name'); - expect(removeTrailingDots('test.....')).toBe('test'); + expect(removeTrailingCharacters('hello...', ['.'])).toBe('hello'); + expect(removeTrailingCharacters('module-name..', ['.'])).toBe('module-name'); + expect(removeTrailingCharacters('test.....', ['.'])).toBe('test'); }); it('should preserve internal dots', () => { - expect(removeTrailingDots('hello.world')).toBe('hello.world'); - expect(removeTrailingDots('module.name.test')).toBe('module.name.test'); + expect(removeTrailingCharacters('hello.world', ['.'])).toBe('hello.world'); + expect(removeTrailingCharacters('module.name.test', ['.'])).toBe('module.name.test'); }); it('should handle edge cases', () => { - expect(removeTrailingDots('')).toBe(''); - expect(removeTrailingDots('...')).toBe(''); - expect(removeTrailingDots('.')).toBe(''); - expect(removeTrailingDots('hello')).toBe('hello'); + expect(removeTrailingCharacters('', ['.'])).toBe(''); + expect(removeTrailingCharacters('...', ['.'])).toBe(''); + expect(removeTrailingCharacters('.', ['.'])).toBe(''); + expect(removeTrailingCharacters('hello', ['.'])).toBe('hello'); + }); + }); + + describe('removeTrailingCharacters', () => { + it('should remove trailing dots', () => { + expect(removeTrailingCharacters('hello...', ['.'])).toBe('hello'); + expect(removeTrailingCharacters('module-name..', ['.'])).toBe('module-name'); + expect(removeTrailingCharacters('test.....', ['.'])).toBe('test'); + }); + + it('should remove trailing hyphens and underscores', () => { + expect(removeTrailingCharacters('module-name--', ['-'])).toBe('module-name'); + expect(removeTrailingCharacters('module_name__', ['_'])).toBe('module_name'); + expect(removeTrailingCharacters('module-name-_', ['-', '_'])).toBe('module-name'); + }); + + it('should remove multiple trailing character types', () => { + expect(removeTrailingCharacters('module-name-_.', ['.', '-', '_'])).toBe('module-name'); + expect(removeTrailingCharacters('test.--__..', ['.', '-', '_'])).toBe('test'); + expect(removeTrailingCharacters('example___...---', ['.', '-', '_'])).toBe('example'); + }); + + it('should preserve internal characters', () => { + expect(removeTrailingCharacters('hello.world', ['.'])).toBe('hello.world'); + expect(removeTrailingCharacters('module-name.test', ['.', '-'])).toBe('module-name.test'); + expect(removeTrailingCharacters('test_module_name', ['_'])).toBe('test_module_name'); + }); + + it('should handle edge cases', () => { + expect(removeTrailingCharacters('', ['.'])).toBe(''); + expect(removeTrailingCharacters('...', ['.'])).toBe(''); + expect(removeTrailingCharacters('---', ['-'])).toBe(''); + expect(removeTrailingCharacters('hello', ['.', '-', '_'])).toBe('hello'); + expect(removeTrailingCharacters('module', [])).toBe('module'); + }); + + it('should handle complex terraform module names', () => { + expect(removeTrailingCharacters('aws-vpc-module-_.', ['.', '-', '_'])).toBe('aws-vpc-module'); + expect(removeTrailingCharacters('tf-modules/vpc-endpoint--', ['-', '_'])).toBe('tf-modules/vpc-endpoint'); + expect(removeTrailingCharacters('modules/networking/vpc__', ['_'])).toBe('modules/networking/vpc'); }); }); }); diff --git a/action.yml b/action.yml index 2278930..9516df5 100644 --- a/action.yml +++ b/action.yml @@ -30,7 +30,7 @@ inputs: See: https://github.com/terraform-docs/terraform-docs/releases required: true - default: v0.19.0 + default: v0.20.0 delete-legacy-tags: description: > Specifies a boolean that determines whether tags from Terraform modules that have been deleted diff --git a/assets/octicons-mark-github.svg b/assets/octicons-mark-github.svg new file mode 100644 index 0000000..a8d1174 --- /dev/null +++ b/assets/octicons-mark-github.svg @@ -0,0 +1,3 @@ + + + diff --git a/package-lock.json b/package-lock.json index 2659607..f694cc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "license": "MIT", "dependencies": { "@actions/core": "^1.11.1", - "@octokit/core": "^6.1.5", - "@octokit/plugin-paginate-rest": "^12.0.0", - "@octokit/plugin-rest-endpoint-methods": "^14.0.0", - "@octokit/request-error": "^6.1.7", + "@octokit/core": "^7.0.2", + "@octokit/plugin-paginate-rest": "^13.0.1", + "@octokit/plugin-rest-endpoint-methods": "^16.0.0", + "@octokit/request-error": "^7.0.0", "minimatch": "^10.0.1", "p-limit": "^6.2.0", "which": "^5.0.0" @@ -22,11 +22,12 @@ "@biomejs/biome": "^1.9.4", "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", - "@types/node": "^22.15.17", + "@types/node": "^22.15.29", "@types/which": "^3.0.4", "@vercel/ncc": "^0.38.3", "@vitest/coverage-v8": "^3.1.3", "make-coverage-badge": "^1.2.0", + "openai": "latest", "textlint": "^14.7.1", "textlint-filter-rule-comments": "^1.2.2", "textlint-rule-terminology": "^5.2.12", @@ -125,13 +126,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", - "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.4.tgz", + "integrity": "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.27.3" }, "bin": { "parser": "bin/babel-parser.js" @@ -141,9 +142,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", - "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", + "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", "dev": true, "license": "MIT", "dependencies": { @@ -186,560 +187,50 @@ "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", - "@biomejs/cli-linux-x64": "1.9.4", - "@biomejs/cli-linux-x64-musl": "1.9.4", - "@biomejs/cli-win32-arm64": "1.9.4", - "@biomejs/cli-win32-x64": "1.9.4" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", - "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-darwin-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", - "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", - "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", - "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", - "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", - "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", - "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", - "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ - "openbsd" + "linux" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", + "license": "MIT OR Apache-2.0", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": ">=18" + "node": ">=14.21.3" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", "cpu": [ "x64" ], @@ -747,7 +238,7 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { "node": ">=18" @@ -854,130 +345,130 @@ } }, "node_modules/@octokit/auth-token": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", - "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "license": "MIT", "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", - "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", + "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", "license": "MIT", "dependencies": { - "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.2.2", - "@octokit/request": "^9.2.3", - "@octokit/request-error": "^6.1.8", + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.1", + "@octokit/request": "^10.0.2", + "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", - "before-after-hook": "^3.0.2", + "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/endpoint": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.4.tgz", - "integrity": "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", "license": "MIT", "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.2.tgz", - "integrity": "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", + "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", "license": "MIT", "dependencies": { - "@octokit/request": "^9.2.3", + "@octokit/request": "^10.0.2", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/openapi-types": { - "version": "25.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.0.0.tgz", - "integrity": "sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==", + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-12.0.0.tgz", - "integrity": "sha512-MPd6WK1VtZ52lFrgZ0R2FlaoiWllzgqFHaSZxvp72NmoDeZ0m8GeJdg4oB6ctqMTYyrnDYp592Xma21mrgiyDA==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.0.1.tgz", + "integrity": "sha512-m1KvHlueScy4mQJWvFDCxFBTIdXS0K1SgFGLmqHyX90mZdCIv6gWBbKRhatxRjhGlONuTK/hztYdaqrTXcFZdQ==", "license": "MIT", "dependencies": { - "@octokit/types": "^14.0.0" + "@octokit/types": "^14.1.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-14.0.0.tgz", - "integrity": "sha512-iQt6ovem4b7zZYZQtdv+PwgbL5VPq37th1m2x2TdkgimIDJpsi2A6Q/OI/23i/hR6z5mL0EgisNR4dcbmckSZQ==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.0.0.tgz", + "integrity": "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g==", "license": "MIT", "dependencies": { - "@octokit/types": "^14.0.0" + "@octokit/types": "^14.1.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "9.2.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.3.tgz", - "integrity": "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.2.tgz", + "integrity": "sha512-iYj4SJG/2bbhh+iIpFmG5u49DtJ4lipQ+aPakjL9OKpsGY93wM8w06gvFbEQxcMsZcCvk5th5KkIm2m8o14aWA==", "license": "MIT", "dependencies": { - "@octokit/endpoint": "^10.1.4", - "@octokit/request-error": "^6.1.8", + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", "@octokit/types": "^14.0.0", - "fast-content-type-parse": "^2.0.0", + "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/request-error": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.8.tgz", - "integrity": "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", "license": "MIT", "dependencies": { "@octokit/types": "^14.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.0.0.tgz", - "integrity": "sha512-VVmZP0lEhbo2O1pdq63gZFiGCKkm8PPp8AUOijlwPO6hojEVjspA0MWKP7E4hbvGxzFKNqKr6p0IYtOH/Wf/zA==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^25.0.0" + "@octokit/openapi-types": "^25.1.0" } }, "node_modules/@octokit/webhooks-types": { @@ -998,220 +489,10 @@ "node": ">=14" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz", - "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz", - "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz", - "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz", - "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz", - "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz", - "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz", - "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz", - "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz", - "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz", - "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz", - "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz", - "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz", - "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz", - "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz", - "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz", - "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz", + "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==", "cpu": [ "x64" ], @@ -1223,9 +504,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz", - "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz", + "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==", "cpu": [ "x64" ], @@ -1236,109 +517,67 @@ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz", - "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz", - "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz", - "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@textlint/ast-node-types": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-14.7.1.tgz", - "integrity": "sha512-7C/xYNZtaG+erIMjNZbRz7av9/S5eC+GAMh0rJ6A9Hik6nS4WyWKblutw2p+O2YWWT2tmOjzu/81fWzzDzmtRg==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-14.7.2.tgz", + "integrity": "sha512-3rZc9vD8y/DlcFe3Y/cyKRRVgBH4ElEUzVFYdRVDwoMSwV/cIyZgYzVG6ZuOItQt+cHSREuijuucZ4VqZynbtg==", "dev": true, "license": "MIT" }, "node_modules/@textlint/ast-tester": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-14.7.1.tgz", - "integrity": "sha512-WMXqBRsEaNJowPCASXOqKhu5zu+DL8I6u4R+j3gTHZiBZFXMCLVmBT6pY9ed1i2Owqzj7akYTqjaHJKagGLwxg==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/ast-tester/-/ast-tester-14.7.2.tgz", + "integrity": "sha512-nlS1xJkIgyBinOx9zlMTU7e+06G8k6Xg8QBBeB+8oiY2UD4XKNHv0A3kOtW30DKtHcw1onZku00gI2ep1er77w==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.7.1", + "@textlint/ast-node-types": "^14.7.2", "debug": "^4.4.0" } }, "node_modules/@textlint/ast-traverse": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-14.7.1.tgz", - "integrity": "sha512-E9uflmEmr9bUbnX5W+KXoH4c2o5Bu6aimeqadIAixkVjVcYFKL7XJ44HJJx/Ern6hcSSYlbIPHjScjAMTT/kqQ==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/ast-traverse/-/ast-traverse-14.7.2.tgz", + "integrity": "sha512-H2pH4NhxBO4nHf9bpEn1v5MZ3E1fKFyrGP3uminXWSqnz+ZEX3fVGYde8EyTdLXpwntUjBg67HBVM4EyTKuhTg==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.7.1" + "@textlint/ast-node-types": "^14.7.2" } }, "node_modules/@textlint/config-loader": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/config-loader/-/config-loader-14.7.1.tgz", - "integrity": "sha512-VBsKuqdrurhS8RUVwmMDShqRYm7oBKPHQxJXDKCY1zT1nXUR0y3/9KcZpKXtG1LEfluIteBmZicR/mwobZW56A==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/config-loader/-/config-loader-14.7.2.tgz", + "integrity": "sha512-06Fqpe36ur7I+SZTtqZbJbsIbbu8uilJuiQG5t+7NIRPNo/d0TrioaMN2Ru/Yr+hq4eSJu+roZ20FNWHMEJb1Q==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/kernel": "^14.7.1", - "@textlint/module-interop": "^14.7.1", - "@textlint/resolver": "^14.7.1", - "@textlint/types": "^14.7.1", - "@textlint/utils": "^14.7.1", + "@textlint/kernel": "^14.7.2", + "@textlint/module-interop": "^14.7.2", + "@textlint/resolver": "^14.7.2", + "@textlint/types": "^14.7.2", + "@textlint/utils": "^14.7.2", "debug": "^4.4.0", "rc-config-loader": "^4.1.3" } }, "node_modules/@textlint/feature-flag": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-14.7.1.tgz", - "integrity": "sha512-yuwNOVPiwDRg+rL0uXzqo9q6g+Ac2T+TETU7RuMxPBlSQWak98I4c8NbjL+aWzL7xj5bZJw5q9MIlOk1jRxl2g==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/feature-flag/-/feature-flag-14.7.2.tgz", + "integrity": "sha512-zfNbBZVrwgDJ4xTWe2FyL/vLvbfw1kTdPRStkFp1tu7I0ypsfwaMh41XZX8rbRqQ5x4p06rLexuOLig9HySyZg==", "dev": true, "license": "MIT" }, "node_modules/@textlint/fixer-formatter": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-14.7.1.tgz", - "integrity": "sha512-AnTLCHnMUNnwRZ3CmeaY15SLzjN3yWH/0OF4R0c+l2v6JF6cdlU907rdcjc9QQnszbaAqn1+fqT+oQ9N9nd5jA==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/fixer-formatter/-/fixer-formatter-14.7.2.tgz", + "integrity": "sha512-KISSckKJD1JAHLy57D+EWRscp/GV7XM/nzPxRARpooIJyGlFNvg/VDkGDzU2ZitH63IduI5qdBxKzvbgk4OgdQ==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/module-interop": "^14.7.1", - "@textlint/resolver": "^14.7.1", - "@textlint/types": "^14.7.1", + "@textlint/module-interop": "^14.7.2", + "@textlint/resolver": "^14.7.2", + "@textlint/types": "^14.7.2", "chalk": "^4.1.2", "debug": "^4.4.0", "diff": "^5.2.0", @@ -1393,36 +632,36 @@ } }, "node_modules/@textlint/kernel": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-14.7.1.tgz", - "integrity": "sha512-aRzw6jdU3UPKxZpeZtM98OBjx0gGUK1QP3RrNBaLSqKOeSn8q2NkfApVIldBV9oQ+z1Drwmati8Pf3xSExTYew==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/kernel/-/kernel-14.7.2.tgz", + "integrity": "sha512-sb2npadp7rsnrVvwv4Ia13/HcwZtVZnY04cJYnDOS6R4TthnYRyP55WF7ftB8GX1bw7FIY/NPvJN/UuWTKQ8XA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.7.1", - "@textlint/ast-tester": "^14.7.1", - "@textlint/ast-traverse": "^14.7.1", - "@textlint/feature-flag": "^14.7.1", - "@textlint/source-code-fixer": "^14.7.1", - "@textlint/types": "^14.7.1", - "@textlint/utils": "^14.7.1", + "@textlint/ast-node-types": "^14.7.2", + "@textlint/ast-tester": "^14.7.2", + "@textlint/ast-traverse": "^14.7.2", + "@textlint/feature-flag": "^14.7.2", + "@textlint/source-code-fixer": "^14.7.2", + "@textlint/types": "^14.7.2", + "@textlint/utils": "^14.7.2", "debug": "^4.4.0", "fast-equals": "^4.0.3", "structured-source": "^4.0.0" } }, "node_modules/@textlint/linter-formatter": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-14.7.1.tgz", - "integrity": "sha512-saAE+e4RZFInRmCF9pu7ukZAHxWaYw9WIA1PptYHItCnlyGS7WB7cYHilkj4coWGr3xGaQ2qAjqX/QIbVE7QGA==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-14.7.2.tgz", + "integrity": "sha512-QZOqft5uK+o/UN8UcEF3cHgfbG1r3+OWqlJojyjGNkEBbBNPSyDfYlVxDjHqnOAwm7jBaeqVGlwvw/7PUFmsmw==", "dev": true, "license": "MIT", "dependencies": { "@azu/format-text": "^1.0.2", "@azu/style-format": "^1.0.1", - "@textlint/module-interop": "^14.7.1", - "@textlint/resolver": "^14.7.1", - "@textlint/types": "^14.7.1", + "@textlint/module-interop": "^14.7.2", + "@textlint/resolver": "^14.7.2", + "@textlint/types": "^14.7.2", "chalk": "^4.1.2", "debug": "^4.4.0", "js-yaml": "^3.14.1", @@ -1480,13 +719,13 @@ } }, "node_modules/@textlint/markdown-to-ast": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-14.7.1.tgz", - "integrity": "sha512-aKIJi1FZj8PYr/n9EYFsifofT2XNPDX9E/24PiFMNBjiOfovCayOpfn6iuqNEo3CEPx7w4d20rQOUnxD+GYzhg==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/markdown-to-ast/-/markdown-to-ast-14.7.2.tgz", + "integrity": "sha512-gfOQaBFoFVTj/9rzhEjnJonWZwWjM1RLmaO4J7GHIOFBAo/qr/EJRO3QijQWaAJFoH8xd/3cpUCEzpRwhwkeoA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.7.1", + "@textlint/ast-node-types": "^14.7.2", "debug": "^4.4.0", "mdast-util-gfm-autolink-literal": "^0.1.3", "neotraverse": "^0.6.15", @@ -1498,74 +737,74 @@ } }, "node_modules/@textlint/module-interop": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-14.7.1.tgz", - "integrity": "sha512-9mfLErTFx8N+tZNTL+46YCY/jnCDOJKpceng5WVwDeZeMJbewhjY3PVcxMoPnvPT10QnE/hDk3b6riUYckgHgw==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-14.7.2.tgz", + "integrity": "sha512-rDQhFERa2+xMqhyrPFvAL9d5Tb4RpQGKQExwrezvtCTREh6Zsp/nKxtK0r6o0P9xn1+zq2sZHW9NZjpe7av3xw==", "dev": true, "license": "MIT" }, "node_modules/@textlint/resolver": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-14.7.1.tgz", - "integrity": "sha512-lQ5ATfpsOgiYnwe2aoS0t9uJ4SrvyiCJpfJdqUQZCVL161O/yMKZBc6nwsyBlruEcFoNxK06F3s3IIV4EsI12A==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-14.7.2.tgz", + "integrity": "sha512-FCZa9XJx5KihK/4gxXLhS/KfOnBD6vD5UxAMtgrvbifn+JFrW9Kh17uZLCcuJDDJJCnZOHq8jdT7AU+rpmJZ+w==", "dev": true, "license": "MIT" }, "node_modules/@textlint/source-code-fixer": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/source-code-fixer/-/source-code-fixer-14.7.1.tgz", - "integrity": "sha512-2teYM26+mwFhKaPKYiKjTH3gInjBFJRMPrd2t+WO8NkZnVCrCq0IdWNJYAP34zBd1JLgXAK0EL93Mo+RPwCtcQ==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/source-code-fixer/-/source-code-fixer-14.7.2.tgz", + "integrity": "sha512-1aWxLIzUIYq9pnrHx7NT7huNbdMONBGORrxYNFC6yIAlSjPo4j44yZabaOMTxjGTPMOvFTUVl2gXTUYqygBGOA==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/types": "^14.7.1", + "@textlint/types": "^14.7.2", "debug": "^4.4.0" } }, "node_modules/@textlint/text-to-ast": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-14.7.1.tgz", - "integrity": "sha512-sSyWYdsX407xMiTKqnB5xWW4ft9SyxwFW2sY3Cpl0emoH5x1CLiYnxpW8uLIN/eFWiSzQlKU9UpyHFfozSC3Ag==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/text-to-ast/-/text-to-ast-14.7.2.tgz", + "integrity": "sha512-8+4WvYnaDR0F7gDszhs9iCQp5kbbEQ9itbvQHcFDhHi4XCDksWdrQz6XJ+Kw/zbLUEz9QtvoXZ1VjZRiwgtTIg==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.7.1" + "@textlint/ast-node-types": "^14.7.2" } }, "node_modules/@textlint/textlint-plugin-markdown": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-14.7.1.tgz", - "integrity": "sha512-FnxwOOvvkIZ7HPV4gV0ZU6mA+G6LhA5QdspUqXAqTPQ0SY7X95PfQYbzk8Yz4RNyXFMhIcviKLO4+eSyBBTSuw==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-14.7.2.tgz", + "integrity": "sha512-s6CR68bK9Y/+c0jvM+9oDT5LgvgRbP2A8IzPwuGVhqwFQBJmrSdG4KvBN4ycsq+h3EACTCeyv9bN9no0td9vgw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/markdown-to-ast": "^14.7.1" + "@textlint/markdown-to-ast": "^14.7.2" } }, "node_modules/@textlint/textlint-plugin-text": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-14.7.1.tgz", - "integrity": "sha512-Cq1pmUWU95W2cYpXr9GDkKm5RN5XAPwVThFeMtj6RE4bCS+qqH/2O08yhUobKo0ryKb7j1zzBi/QdQ2U34YwvQ==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/textlint-plugin-text/-/textlint-plugin-text-14.7.2.tgz", + "integrity": "sha512-ZtO/i4cZ+y9j7KqsP8P3cbj/XpUDMyedkV+fQP8DkJb238vJ/EUrW8wBwvIq0BmGe7DKIgIl3WeFp8AKxNw7aw==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/text-to-ast": "^14.7.1" + "@textlint/text-to-ast": "^14.7.2" } }, "node_modules/@textlint/types": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/types/-/types-14.7.1.tgz", - "integrity": "sha512-j10OEEHRAaqGMC6dK3+H1Eg3bksASGTmGDozsSepYs7qInY+lYBCe5m3JTrKkDnAX4nNy8ninnKzrYKcVkWahw==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-14.7.2.tgz", + "integrity": "sha512-VpsmtJf9+7cnIxmKtAVVGVzI6f2k09kBZnzjdTAO8JZ+HTmV46jeoVrotpSfQbWDpuQk2UFPfrsZL/LNf/99ew==", "dev": true, "license": "MIT", "dependencies": { - "@textlint/ast-node-types": "^14.7.1" + "@textlint/ast-node-types": "^14.7.2" } }, "node_modules/@textlint/utils": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-14.7.1.tgz", - "integrity": "sha512-nvIOltt0U5HQzu28qfYgbXUvZxEZBPRnQZHqlwMsqKp55bZ5L3iSNYwHvCej7fm9GOXH7Yz3UzLSko9eF5m3PA==", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/@textlint/utils/-/utils-14.7.2.tgz", + "integrity": "sha512-NMLtiALWVYtl/zqRB0GIw3CJLNSV5bq8u35v1qaJOLrWhBVxOkbWISItHhQGndCalHTt8uYkntHXYKpk0oSPtQ==", "dev": true, "license": "MIT" }, @@ -1587,9 +826,9 @@ } }, "node_modules/@types/node": { - "version": "22.15.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.17.tgz", - "integrity": "sha512-wIX2aSZL5FE+MR0JlvF87BNVrtFWf6AE6rxSE9X7OwnVvoyCQjpzSRJ+M87se/4QCkCiebQAqrJ0y6fwIyi7nw==", + "version": "22.15.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz", + "integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1621,9 +860,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.3.tgz", - "integrity": "sha512-cj76U5gXCl3g88KSnf80kof6+6w+K4BjOflCl7t6yRJPDuCrHtVu0SgNYOUARJOL5TI8RScDbm5x4s1/P9bvpw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.1.4.tgz", + "integrity": "sha512-G4p6OtioySL+hPV7Y6JHlhpsODbJzt1ndwHAFkyk6vVjpK03PFsKnauZIzcd0PrK4zAbc5lc+jeZ+eNGiMA+iw==", "dev": true, "license": "MIT", "dependencies": { @@ -1644,8 +883,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "3.1.3", - "vitest": "3.1.3" + "@vitest/browser": "3.1.4", + "vitest": "3.1.4" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -1654,14 +893,14 @@ } }, "node_modules/@vitest/expect": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.3.tgz", - "integrity": "sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.4.tgz", + "integrity": "sha512-xkD/ljeliyaClDYqHPNCiJ0plY5YIcM0OlRiZizLhlPmpXWpxnGMyTZXOHFhFeG7w9P5PBeL4IdtJ/HeQwTbQA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.3", - "@vitest/utils": "3.1.3", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -1670,13 +909,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.3.tgz", - "integrity": "sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.4.tgz", + "integrity": "sha512-8IJ3CvwtSw/EFXqWFL8aCMu+YyYXG2WUSrQbViOZkWTKTVicVwZ/YiEZDSqD00kX+v/+W+OnxhNWoeVKorHygA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.1.3", + "@vitest/spy": "3.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -1697,9 +936,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.3.tgz", - "integrity": "sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.4.tgz", + "integrity": "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg==", "dev": true, "license": "MIT", "dependencies": { @@ -1710,13 +949,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.3.tgz", - "integrity": "sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.4.tgz", + "integrity": "sha512-djTeF1/vt985I/wpKVFBMWUlk/I7mb5hmD5oP8K9ACRmVXgKTae3TUOtXAEBfslNKPzUQvnKhNd34nnRSYgLNQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.1.3", + "@vitest/utils": "3.1.4", "pathe": "^2.0.3" }, "funding": { @@ -1724,13 +963,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.3.tgz", - "integrity": "sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.4.tgz", + "integrity": "sha512-JPHf68DvuO7vilmvwdPr9TS0SuuIzHvxeaCkxYcCD4jTk67XwL45ZhEHFKIuCm8CYstgI6LZ4XbwD6ANrwMpFg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.3", + "@vitest/pretty-format": "3.1.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -1739,9 +978,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.3.tgz", - "integrity": "sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.4.tgz", + "integrity": "sha512-Xg1bXhu+vtPXIodYN369M86K8shGLouNjoVI78g8iAq2rFoHFdajNvJJ5A/9bPMFcfQqdaCpOgWKEoMQg/s0Yg==", "dev": true, "license": "MIT", "dependencies": { @@ -1752,13 +991,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.3.tgz", - "integrity": "sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.4.tgz", + "integrity": "sha512-yriMuO1cfFhmiGc8ataN51+9ooHRuURdfAZfwFd3usWynjzpLslZdYnRegTv32qdgtJTsj15FoeZe2g15fY1gg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.1.3", + "@vitest/pretty-format": "3.1.4", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" }, @@ -1881,9 +1120,9 @@ "license": "MIT" }, "node_modules/before-after-hook": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", - "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "license": "Apache-2.0" }, "node_modules/boundary": { @@ -1938,14 +1177,14 @@ } }, "node_modules/cacheable": { - "version": "1.8.10", - "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.8.10.tgz", - "integrity": "sha512-0ZnbicB/N2R6uziva8l6O6BieBklArWyiGx4GkwAhLKhSHyQtRfM9T1nx7HHuHDKkYB/efJQhz3QJ6x/YqoZzA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.9.0.tgz", + "integrity": "sha512-8D5htMCxPDUULux9gFzv30f04Xo3wCnik0oOxKoRTPIBoqA7HtOcJ87uBhQTs3jCfZZTrUBGsYIZOgE0ZRgMAg==", "dev": true, "license": "MIT", "dependencies": { - "hookified": "^1.8.1", - "keyv": "^5.3.2" + "hookified": "^1.8.2", + "keyv": "^5.3.3" } }, "node_modules/ccount": { @@ -2115,9 +1354,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2191,9 +1430,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2204,31 +1443,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" } }, "node_modules/escape-string-regexp": { @@ -2286,9 +1525,9 @@ "license": "MIT" }, "node_modules/fast-content-type-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "funding": [ { "type": "github", @@ -2354,9 +1593,9 @@ } }, "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.5.tgz", + "integrity": "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2369,13 +1608,13 @@ } }, "node_modules/file-entry-cache": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.0.8.tgz", - "integrity": "sha512-FGXHpfmI4XyzbLd3HQ8cbUcsFGohJpZtmQRHr8z8FxxtCe2PcpgIlVLwIgunqjvRmXypBETvwhV4ptJizA+Y1Q==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-10.1.0.tgz", + "integrity": "sha512-Et/ex6smi3wOOB+n5mek+Grf7P2AxZR5ueqRUvAAn4qkyatXi3cUC1cuQXVkX0VlzBVsN4BkWJFmY/fYiRTdww==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^6.1.8" + "flat-cache": "^6.1.9" } }, "node_modules/find-up": { @@ -2392,15 +1631,15 @@ } }, "node_modules/flat-cache": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.8.tgz", - "integrity": "sha512-R6MaD3nrJAtO7C3QOuS79ficm2pEAy++TgEUD8ii1LVlbcgZ9DtASLkt9B+RZSFCzm7QHDMlXPsqqB6W2Pfr1Q==", + "version": "6.1.9", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.9.tgz", + "integrity": "sha512-DUqiKkTlAfhtl7g78IuwqYM+YqvT+as0mY+EVk6mfimy19U79pJCzDZQsnqk3Ou/T6hFXWLGbwbADzD/c8Tydg==", "dev": true, "license": "MIT", "dependencies": { - "cacheable": "^1.8.9", + "cacheable": "^1.9.0", "flatted": "^3.3.3", - "hookified": "^1.8.1" + "hookified": "^1.8.2" } }, "node_modules/flatted": { @@ -2436,21 +1675,6 @@ "node": ">=0.4.x" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -2461,16 +1685,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha512-jZV7n6jGE3Gt7fgSTJoz91Ak5MuTLwMwkoYdjxuJ/AmjIsE1UC03y/IWkZCQGEvVNS9qoRNwy5BCqxImv0FVeA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -2539,9 +1753,9 @@ } }, "node_modules/hookified": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.8.2.tgz", - "integrity": "sha512-5nZbBNP44sFCDjSoB//0N7m508APCgbQ4mGGo1KJGBYyCKNHfry1Pvd0JVHZIxjdnqn8nFRBAN/eFB6Rk/4w5w==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.9.0.tgz", + "integrity": "sha512-2yEEGqphImtKIe1NXWEhu6yD3hlFR4Mxk4Mtp3XEyScpSt4pQ4ymmXA1zzxZpj99QkFK+nN0nzjeb2+RUi/6CQ==", "dev": true, "license": "MIT" }, @@ -3402,6 +2616,28 @@ "semver": "bin/semver" } }, + "node_modules/openai": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-5.0.1.tgz", + "integrity": "sha512-Do6vxhbDv7cXhji/4ct1lrpZYMAOmjYbhyA9LJTuG7OfpbWMpuS+EIXkRT7R+XxpRB1OZhU/op4FU3p3uxU6gw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3654,9 +2890,9 @@ "license": "MIT" }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", + "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", "dev": true, "funding": [ { @@ -3674,7 +2910,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3933,9 +3169,9 @@ } }, "node_modules/rollup": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz", - "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", + "version": "4.41.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz", + "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==", "dev": true, "license": "MIT", "dependencies": { @@ -3949,33 +3185,33 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.2", - "@rollup/rollup-android-arm64": "4.40.2", - "@rollup/rollup-darwin-arm64": "4.40.2", - "@rollup/rollup-darwin-x64": "4.40.2", - "@rollup/rollup-freebsd-arm64": "4.40.2", - "@rollup/rollup-freebsd-x64": "4.40.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.2", - "@rollup/rollup-linux-arm-musleabihf": "4.40.2", - "@rollup/rollup-linux-arm64-gnu": "4.40.2", - "@rollup/rollup-linux-arm64-musl": "4.40.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-gnu": "4.40.2", - "@rollup/rollup-linux-riscv64-musl": "4.40.2", - "@rollup/rollup-linux-s390x-gnu": "4.40.2", - "@rollup/rollup-linux-x64-gnu": "4.40.2", - "@rollup/rollup-linux-x64-musl": "4.40.2", - "@rollup/rollup-win32-arm64-msvc": "4.40.2", - "@rollup/rollup-win32-ia32-msvc": "4.40.2", - "@rollup/rollup-win32-x64-msvc": "4.40.2", + "@rollup/rollup-android-arm-eabi": "4.41.1", + "@rollup/rollup-android-arm64": "4.41.1", + "@rollup/rollup-darwin-arm64": "4.41.1", + "@rollup/rollup-darwin-x64": "4.41.1", + "@rollup/rollup-freebsd-arm64": "4.41.1", + "@rollup/rollup-freebsd-x64": "4.41.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", + "@rollup/rollup-linux-arm-musleabihf": "4.41.1", + "@rollup/rollup-linux-arm64-gnu": "4.41.1", + "@rollup/rollup-linux-arm64-musl": "4.41.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-gnu": "4.41.1", + "@rollup/rollup-linux-riscv64-musl": "4.41.1", + "@rollup/rollup-linux-s390x-gnu": "4.41.1", + "@rollup/rollup-linux-x64-gnu": "4.41.1", + "@rollup/rollup-linux-x64-musl": "4.41.1", + "@rollup/rollup-win32-arm64-msvc": "4.41.1", + "@rollup/rollup-win32-ia32-msvc": "4.41.1", + "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -4231,9 +3467,9 @@ } }, "node_modules/strip-json-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", - "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.2.tgz", + "integrity": "sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==", "dev": true, "license": "MIT", "engines": { @@ -4380,28 +3616,27 @@ "license": "MIT" }, "node_modules/textlint": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/textlint/-/textlint-14.7.1.tgz", - "integrity": "sha512-jhWEeF3SWYPuItcTRLA1KAyxHAX2diWpqtglcagPzPidFi2OMKCNW55FlaIQvOkWdohp5LzFk0mv4XiChqGr0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@textlint/ast-node-types": "^14.7.1", - "@textlint/ast-traverse": "^14.7.1", - "@textlint/config-loader": "^14.7.1", - "@textlint/feature-flag": "^14.7.1", - "@textlint/fixer-formatter": "^14.7.1", - "@textlint/kernel": "^14.7.1", - "@textlint/linter-formatter": "^14.7.1", - "@textlint/module-interop": "^14.7.1", - "@textlint/resolver": "^14.7.1", - "@textlint/textlint-plugin-markdown": "^14.7.1", - "@textlint/textlint-plugin-text": "^14.7.1", - "@textlint/types": "^14.7.1", - "@textlint/utils": "^14.7.1", + "version": "14.7.2", + "resolved": "https://registry.npmjs.org/textlint/-/textlint-14.7.2.tgz", + "integrity": "sha512-SRff/IHVonktwTMm0momjMZeh0WTPWbQR0S0NXHoU7MUwLM9e4VJeQvkq7V0d37GiM1WfXJSkbaTVidOhOE+0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "^14.7.2", + "@textlint/ast-traverse": "^14.7.2", + "@textlint/config-loader": "^14.7.2", + "@textlint/feature-flag": "^14.7.2", + "@textlint/fixer-formatter": "^14.7.2", + "@textlint/kernel": "^14.7.2", + "@textlint/linter-formatter": "^14.7.2", + "@textlint/module-interop": "^14.7.2", + "@textlint/resolver": "^14.7.2", + "@textlint/textlint-plugin-markdown": "^14.7.2", + "@textlint/textlint-plugin-text": "^14.7.2", + "@textlint/types": "^14.7.2", + "@textlint/utils": "^14.7.2", "debug": "^4.4.0", "file-entry-cache": "^10.0.5", - "get-stdin": "^5.0.1", "glob": "^10.4.5", "md5": "^2.3.0", "mkdirp": "^0.5.6", @@ -4479,9 +3714,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4496,9 +3731,9 @@ } }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.0.tgz", + "integrity": "sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==", "dev": true, "license": "MIT", "engines": { @@ -4708,9 +3943,9 @@ } }, "node_modules/universal-user-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", - "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "license": "ISC" }, "node_modules/validate-npm-package-license": { @@ -4856,9 +4091,9 @@ } }, "node_modules/vite-node": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.3.tgz", - "integrity": "sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.4.tgz", + "integrity": "sha512-6enNwYnpyDo4hEgytbmc6mYWHXDHYEn0D1/rw4Q+tnHUGtKTJsn8T1YkX6Q18wI5LCrS8CTYlBaiCqxOy2kvUA==", "dev": true, "license": "MIT", "dependencies": { @@ -4879,19 +4114,19 @@ } }, "node_modules/vitest": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.3.tgz", - "integrity": "sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.4.tgz", + "integrity": "sha512-Ta56rT7uWxCSJXlBtKgIlApJnT6e6IGmTYxYcmxjJ4ujuZDI59GUQgVDObXXJujOmPDBYXHK1qmaGtneu6TNIQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "3.1.3", - "@vitest/mocker": "3.1.3", - "@vitest/pretty-format": "^3.1.3", - "@vitest/runner": "3.1.3", - "@vitest/snapshot": "3.1.3", - "@vitest/spy": "3.1.3", - "@vitest/utils": "3.1.3", + "@vitest/expect": "3.1.4", + "@vitest/mocker": "3.1.4", + "@vitest/pretty-format": "^3.1.4", + "@vitest/runner": "3.1.4", + "@vitest/snapshot": "3.1.4", + "@vitest/spy": "3.1.4", + "@vitest/utils": "3.1.4", "chai": "^5.2.0", "debug": "^4.4.0", "expect-type": "^1.2.1", @@ -4904,7 +4139,7 @@ "tinypool": "^1.0.2", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.3", + "vite-node": "3.1.4", "why-is-node-running": "^2.3.0" }, "bin": { @@ -4920,8 +4155,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.3", - "@vitest/ui": "3.1.3", + "@vitest/browser": "3.1.4", + "@vitest/ui": "3.1.4", "happy-dom": "*", "jsdom": "*" }, diff --git a/package.json b/package.json index 5ae9f70..435bccb 100644 --- a/package.json +++ b/package.json @@ -42,14 +42,16 @@ "package": "ncc build src/index.ts --source-map -o dist", "test": "vitest run --coverage", "test:watch": "vitest", - "coverage": "make-coverage-badge --output-path ./assets/coverage-badge.svg" + "coverage": "make-coverage-badge --output-path ./assets/coverage-badge.svg", + "terraform-docs-version": "gh api repos/terraform-docs/terraform-docs/releases/latest --jq '.tag_name'", + "changelog:test": "node .github/scripts/changelog.js 10.0.0" }, "dependencies": { "@actions/core": "^1.11.1", - "@octokit/core": "^6.1.5", - "@octokit/plugin-paginate-rest": "^12.0.0", - "@octokit/plugin-rest-endpoint-methods": "^14.0.0", - "@octokit/request-error": "^6.1.7", + "@octokit/core": "^7.0.2", + "@octokit/plugin-paginate-rest": "^13.0.1", + "@octokit/plugin-rest-endpoint-methods": "^16.0.0", + "@octokit/request-error": "^7.0.0", "minimatch": "^10.0.1", "p-limit": "^6.2.0", "which": "^5.0.0" @@ -58,11 +60,12 @@ "@biomejs/biome": "^1.9.4", "@octokit/types": "^14.0.0", "@octokit/webhooks-types": "^7.6.1", - "@types/node": "^22.15.17", + "@types/node": "^22.15.29", "@types/which": "^3.0.4", "@vercel/ncc": "^0.38.3", "@vitest/coverage-v8": "^3.1.3", "make-coverage-badge": "^1.2.0", + "openai": "latest", "textlint": "^14.7.1", "textlint-filter-rule-comments": "^1.2.2", "textlint-rule-terminology": "^5.2.12", diff --git a/src/context.ts b/src/context.ts index de1baaa..1245931 100644 --- a/src/context.ts +++ b/src/context.ts @@ -93,10 +93,12 @@ function initializeContext(): Context { // Get required environment variables const eventName = getRequiredEnvironmentVar('GITHUB_EVENT_NAME'); - const serverUrl = getRequiredEnvironmentVar('GITHUB_SERVER_URL'); + const serverUrl = getRequiredEnvironmentVar('GITHUB_SERVER_URL'); // https://github.techpivot.com | https://github.com + const apiUrl = process.env.GITHUB_API_URL ?? 'https://api.github.com'; // https://github.techpivot.com/api/v3 | https://api.github.com const repository = getRequiredEnvironmentVar('GITHUB_REPOSITORY'); const eventPath = getRequiredEnvironmentVar('GITHUB_EVENT_PATH'); const workspaceDir = getRequiredEnvironmentVar('GITHUB_WORKSPACE'); + const [owner, repo] = repository.split('/'); if (eventName !== 'pull_request') { @@ -123,6 +125,7 @@ function initializeContext(): Context { repo: { owner, repo }, repoUrl: `${serverUrl}/${owner}/${repo}`, octokit: new OctokitRestApi({ + baseUrl: apiUrl, auth: `token ${config.githubToken}`, userAgent: `[octokit] terraform-module-releaser/${version} (${homepage})`, }), @@ -138,6 +141,8 @@ function initializeContext(): Context { contextInstance.prBody?.length > 60 ? `${contextInstance.prBody.slice(0, 57)}...` : contextInstance.prBody; info(`Event Name: ${eventName}`); + info(`GitHub Server URL: ${serverUrl}`); + info(`GitHub API URL: ${apiUrl}`); info(`Repository: ${contextInstance.repo.owner}/${contextInstance.repo.repo}`); info(`Repository URL: ${contextInstance.repoUrl}`); info(`Pull Request Number: ${contextInstance.prNumber}`); diff --git a/src/main.ts b/src/main.ts index 421bdff..bb74176 100644 --- a/src/main.ts +++ b/src/main.ts @@ -107,16 +107,45 @@ async function handleMergeEvent( } /** - * Entry point for the GitHub Action. Determines the flow based on whether the event - * is a pull request or a merge, and executes the appropriate operations. + * Executes the main process of the terraform-module-releaser action. * - * @returns {Promise} Resolves when the action completes successfully or fails. + * This function handles the Terraform module release workflow by: + * 1. Checking if a release comment already exists to prevent duplicate releases + * 2. Collecting pull request commits, tags, and existing releases + * 3. Identifying Terraform modules and which ones have changed + * 4. Determining modules that need to be removed + * 5. Handling either release planning (commenting on PR) or the actual merge event + * 6. Setting GitHub Action outputs with information about changed and all modules + * + * The function sets the following outputs: + * - changed-module-names: Names of modules that changed + * - changed-module-paths: Paths to modules that changed + * - changed-modules-map: Detailed map of changed modules with metadata + * - all-module-names: Names of all detected modules + * - all-module-paths: Paths to all detected modules + * - all-modules-map: Detailed map of all modules with metadata + * + * @returns {Promise} A promise that resolves when the process completes + * @throws Will capture and report any errors through setFailed */ export async function run(): Promise { try { const { config, context } = initialize(); if (await hasReleaseComment()) { + // Prevent duplicate releases by checking for existing release comments. + // This serves as a lightweight state mechanism for the Terraform Release Action. + // When a release is completed, a comment is added to the PR. If this comment exists, + // it indicates the release has already been processed, preventing: + // - Manual workflow re-runs from creating duplicate releases + // - Automatic workflow retries from re-releasing the same modules + // - Race conditions in concurrent workflow executions + // + // This approach is preferred over artifact storage as it requires no additional + // dependencies, storage permissions, or cleanup - the comment persists with the PR + // and provides a clear audit trail of release activity. Another potential solution + // might be to add a label to the PR. However, these are easier modified by users + // with write permissions while the comment modification would require an admin. info('Release comment found. Exiting.'); return; } diff --git a/src/pull-request.ts b/src/pull-request.ts index e3368a4..ce65ad7 100644 --- a/src/pull-request.ts +++ b/src/pull-request.ts @@ -2,7 +2,13 @@ import { getPullRequestChangelog } from '@/changelog'; import { config } from '@/config'; import { context } from '@/context'; import type { CommitDetails, GitHubRelease, ReleasePlanCommentOptions, TerraformChangedModule } from '@/types'; -import { BRANDING_COMMENT, GITHUB_ACTIONS_BOT_USER_ID, PR_RELEASE_MARKER, PR_SUMMARY_MARKER } from '@/utils/constants'; +import { + BRANDING_COMMENT, + GITHUB_ACTIONS_BOT_USER_ID, + PROJECT_URL, + PR_RELEASE_MARKER, + PR_SUMMARY_MARKER, +} from '@/utils/constants'; import { WikiStatus, getWikiLink } from '@/wiki'; import { debug, endGroup, info, startGroup } from '@actions/core'; import { RequestError } from '@octokit/request-error'; @@ -233,38 +239,56 @@ export async function addReleasePlanComment( } } - // Wiki Check + // Changelog + if (terraformChangedModules.length > 0) { + commentBody.push('\n# Changelog\n', getPullRequestChangelog(terraformChangedModules)); + } + + // Wiki Status + commentBody.push( + '\n

Wiki Statusℹ️

\n', + ); switch (wikiStatus.status) { + case WikiStatus.DISABLED: + commentBody.push('🚫 Wiki generation **disabled** via `disable-wiki` flag.'); + break; case WikiStatus.SUCCESS: - commentBody.push( - '\n> #### βœ… Wiki Check ℹ️', - ); + commentBody.push('βœ… Enabled'); break; case WikiStatus.FAILURE: + commentBody.push('**⚠️ Failed to checkout wiki:**'); + commentBody.push('```'); + commentBody.push(`${wikiStatus.errorMessage}`); + commentBody.push('```'); commentBody.push( - `\n> #### ⚠️ Wiki Check: Failed to checkout wiki. ${wikiStatus.errorMessage}

Please consult the README for additional information and review logs in the latest GitHub workflow run.`, + `Please consult the [README.md](${PROJECT_URL}/blob/main/README.md#getting-started) for additional information (**Ensure the Wiki is initialized**).`, ); break; - case WikiStatus.DISABLED: - commentBody.push('\n> ##### 🚫 Wiki Check: Generation is disabled.'); - break; } + // Automated Tag Cleanup + commentBody.push( + '\n

Automated Tag Cleanupℹ️

\n', + ); + // Modules to Remove - if (terraformModuleNamesToRemove.length > 0) { + if (!config.deleteLegacyTags) { commentBody.push( - `\n> **Note**: The following Terraform modules no longer exist in source; however, corresponding tags/releases exist.${ - config.deleteLegacyTags - ? ' Automation tag/release deletion is **enabled** and corresponding tags/releases will be automatically deleted.
' - : ' Automation tag/release deletion is **disabled** β€” **no** subsequent action will take place.
' - }`, + '⏸️ Existing tags and releases will be **preserved** as the `delete-legacy-tags` flag is disabled.', ); - commentBody.push(terraformModuleNamesToRemove.map((moduleName) => `\`${moduleName}\``).join(', ')); - } - - // Changelog - if (terraformChangedModules.length > 0) { - commentBody.push('\n# Changelog\n', getPullRequestChangelog(terraformChangedModules)); + } else if (terraformModuleNamesToRemove.length === 0) { + commentBody.push('βœ… All tags and releases are synchronized with the codebase. No cleanup required.'); + } else { + if (terraformModuleNamesToRemove.length === 1) { + commentBody.push( + '**⚠️ The following module no longer exists in source but has tags/releases. It will be automatically deleted.**', + ); + } else { + commentBody.push( + '**⚠️ The following modules no longer exist in source but have tags/releases. They will be automatically deleted.**', + ); + } + commentBody.push(terraformModuleNamesToRemove.map((moduleName) => `- \`${moduleName}\``).join('\n')); } // Branding diff --git a/src/terraform-module.ts b/src/terraform-module.ts index 0029305..f15cd9a 100644 --- a/src/terraform-module.ts +++ b/src/terraform-module.ts @@ -5,7 +5,7 @@ import { context } from '@/context'; import type { CommitDetails, GitHubRelease, TerraformChangedModule, TerraformModule } from '@/types'; import { isTerraformDirectory, shouldExcludeFile, shouldIgnoreModulePath } from '@/utils/file'; import { determineReleaseType, getNextTagVersion } from '@/utils/semver'; -import { removeTrailingDots } from '@/utils/string'; +import { removeTrailingCharacters } from '@/utils/string'; import { debug, endGroup, info, startGroup } from '@actions/core'; /** @@ -62,7 +62,7 @@ function getTerraformModuleNameFromRelativePath(terraformDirectory: string): str .replace(/\s+/g, '') // Remove any remaining whitespace .toLowerCase(); // All of our module names will be lowercase - return removeTrailingDots(cleanedDirectory); + return removeTrailingCharacters(cleanedDirectory, ['.', '-', '_']); } /** @@ -161,7 +161,6 @@ function getReleasesForModule(moduleName: string, allReleases: GitHubRelease[]): * change details. * @throws {Error} - If a module associated with a file is missing from the terraformModulesMap. */ - export function getAllTerraformModules( commits: CommitDetails[], allTags: string[], @@ -278,6 +277,36 @@ export function getAllTerraformModules( } } + // Handle initial release scenario: Mark modules for release if they have no existing tags/releases + // This ensures that on the first run of this action, all discovered modules get released even if + // they weren't modified in the current commit(s). This is necessary because: + // - New repositories may have existing modules that need initial releases + // - Modules without any version history should be tagged with an initial version + // - This allows the action to work correctly on repositories being set up for the first time + for (const [moduleName, module] of Object.entries(terraformModulesMap)) { + // Only process modules that: + // - Haven't been marked as changed by commit analysis above + // - Have no existing tags (indicating they've never been released) + if (!isChangedModule(module) && module.tags.length === 0) { + info(`Marking module '${moduleName}' for initial release (no existing tags found)`); + + // Convert the TerraformModule to TerraformChangedModule for initial release + const releaseType = 'patch'; // Use patch for initial releases (can be configured via config.defaultFirstTag) + const nextTagVersion = getNextTagVersion(null, releaseType); + + Object.assign(module, { + isChanged: true, + // Empty commit messages array for initial releases. Originally set to ['Initial release'], + // but since the changelog generation function automatically includes PR information, + // we leave this empty to avoid redundant messaging in the release notes. + commitMessages: [], + releaseType, + nextTag: `${moduleName}/${nextTagVersion}`, + nextTagVersion, + }); + } + } + // Sort terraform modules by module name const sortedTerraformModules = Object.values(terraformModulesMap) .slice() @@ -332,8 +361,10 @@ export function getTerraformModulesToRemove(allTags: string[], terraformModules: // Get an array of all module names from the terraformModules const moduleNamesFromModules = terraformModules.map((module) => module.moduleName); - // Perform a diff between the two arrays to find the module names that need to be removed - const moduleNamesToRemove = moduleNamesFromTags.filter((moduleName) => !moduleNamesFromModules.includes(moduleName)); + // Perform a diff between the two arrays to find the module names that need to be removed and sort + const moduleNamesToRemove = moduleNamesFromTags + .filter((moduleName) => !moduleNamesFromModules.includes(moduleName)) + .sort((a, b) => a.localeCompare(b)); info('Terraform modules to remove'); info(JSON.stringify(moduleNamesToRemove, null, 2)); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 1dc5e16..c4d7ab3 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -7,9 +7,8 @@ export const PR_RELEASE_MARKER = '