From 13f70eeab86df77f0e4e57231b2399734cd1259f Mon Sep 17 00:00:00 2001
From: Mark Johnson <739719+virgofx@users.noreply.github.com>
Date: Mon, 25 Nov 2024 00:24:08 +0000
Subject: [PATCH] refactor(wiki): simplify module fetching and enhance test
coverage
* Remove workspace parameter from getAllTerraformModules() in favor of context
* Add comprehensive test coverage for wiki.ts with fixtures
* Update minor package dependencies
---
.github/workflows/lint.yml | 3 +-
.node-version | 1 +
__tests__/fixtures/Home.md | 25 ++
__tests__/fixtures/_Footer.md | 1 +
__tests__/fixtures/_Sidebar.md | 30 ++
...s3\342\200\222bucket\342\200\222object.md" | 81 +++++
.../fixtures/vpc\342\200\222endpoint.md" | 72 ++++
__tests__/terraform-module.test.ts | 43 +--
__tests__/wiki.test.ts | 320 ++++++++++++++++++
package-lock.json | 267 +++++++++------
package.json | 8 +-
sonar-project.properties | 3 +-
src/context.ts | 5 +-
src/main.ts | 2 +-
src/terraform-module.ts | 14 +-
src/types/index.ts | 31 +-
src/wiki.ts | 81 +++--
17 files changed, 808 insertions(+), 179 deletions(-)
create mode 100644 __tests__/fixtures/Home.md
create mode 100644 __tests__/fixtures/_Footer.md
create mode 100644 __tests__/fixtures/_Sidebar.md
create mode 100644 "__tests__/fixtures/s3\342\200\222bucket\342\200\222object.md"
create mode 100644 "__tests__/fixtures/vpc\342\200\222endpoint.md"
create mode 100644 __tests__/wiki.test.ts
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index b03da52..eb97be6 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -11,6 +11,7 @@ on:
permissions:
contents: read
packages: read
+ statuses: write # To report GitHub Actions status checks
jobs:
lint:
@@ -45,7 +46,7 @@ jobs:
env:
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- FILTER_REGEX_EXCLUDE: dist/**/*
+ FILTER_REGEX_EXCLUDE: (dist/**/*)|(__tests__/fixtures/**/*)
FIX_TYPESCRIPT_PRETTIER: false # Using biome
FIX_JAVASCRIPT_PRETTIER: false # Using biome
VALIDATE_ALL_CODEBASE: true
diff --git a/.node-version b/.node-version
index 2bd5a0a..1836aa6 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1,2 @@
22
+# Used by CI only
\ No newline at end of file
diff --git a/__tests__/fixtures/Home.md b/__tests__/fixtures/Home.md
new file mode 100644
index 0000000..dc73fb8
--- /dev/null
+++ b/__tests__/fixtures/Home.md
@@ -0,0 +1,25 @@
+# Terraform Modules Home
+
+Welcome to the Terraform Modules Wiki! This page serves as an index for all the available Terraform modules,
+providing an overview of their functionality and the latest versions.
+
+## Current Terraform Modules
+
+| Module Name | Latest Version |
+| -- | -- |
+| [s3-bucket-object](/techpivot/terraform-module-releaser/wiki/s3‒bucket‒object) | null |
+| [vpc-endpoint](/techpivot/terraform-module-releaser/wiki/vpc‒endpoint) | v1.0.0 |
+
+## How to Use
+
+Each module listed above can be imported into your Terraform configurations. For detailed instructions on
+usage and examples, refer to the documentation links provided in the table.
+
+## Contributing
+If you would like to contribute to these modules or report issues, please visit the
+[GitHub Repository](https://github.com/techpivot/terraform-module-releaser) for more information.
+
+---
+
+*This wiki is automatically generated as part of the [Terraform Module Releaser](https://github.com/techpivot/terraform-module-releaser) project.
+For the latest updates, please refer to the individual module documentation links above.*
\ No newline at end of file
diff --git a/__tests__/fixtures/_Footer.md b/__tests__/fixtures/_Footer.md
new file mode 100644
index 0000000..44b9c89
--- /dev/null
+++ b/__tests__/fixtures/_Footer.md
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/__tests__/fixtures/_Sidebar.md b/__tests__/fixtures/_Sidebar.md
new file mode 100644
index 0000000..fb2f4bb
--- /dev/null
+++ b/__tests__/fixtures/_Sidebar.md
@@ -0,0 +1,30 @@
+[Home](/techpivot/terraform-module-releaser/wiki/Home)
+
+## Terraform Modules
+
+
\ No newline at end of file
diff --git "a/__tests__/fixtures/s3\342\200\222bucket\342\200\222object.md" "b/__tests__/fixtures/s3\342\200\222bucket\342\200\222object.md"
new file mode 100644
index 0000000..6a13471
--- /dev/null
+++ "b/__tests__/fixtures/s3\342\200\222bucket\342\200\222object.md"
@@ -0,0 +1,81 @@
+# Usage
+
+To use this module in your Terraform, refer to the below module example:
+
+```hcl
+module "s3_bucket_object" {
+ source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=null"
+
+ # See inputs below for additional required parameters
+}
+```
+
+# Attributes
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.0 |
+| [aws](#requirement\_aws) | >= 5.24 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 5.24 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_s3_object.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [acl](#input\_acl) | The canned ACL to apply. Valid values are private, public-read, public-read-write, aws-exec-read, authenticated-read, bucket-owner-read, and bucket-owner-full-control. Defaults to private. | `string` | `null` | no |
+| [bucket](#input\_bucket) | The name of the bucket to put the file in. Alternatively, an S3 access point ARN can be specified. | `string` | `""` | no |
+| [bucket\_key\_enabled](#input\_bucket\_key\_enabled) | Whether or not to use Amazon S3 Bucket Keys for SSE-KMS. | `bool` | `null` | no |
+| [cache\_control](#input\_cache\_control) | Specifies caching behavior along the request/reply chain. | `string` | `null` | no |
+| [content](#input\_content) | Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text. | `string` | `null` | no |
+| [content\_base64](#input\_content\_base64) | Base64-encoded data that will be decoded and uploaded as raw bytes for the object content. This allows safely uploading non-UTF8 binary data, but is recommended only for small content such as the result of the gzipbase64 function with small text strings. For larger objects, use source to stream the content from a disk file. | `string` | `null` | no |
+| [content\_disposition](#input\_content\_disposition) | Specifies presentational information for the object. | `string` | `null` | no |
+| [content\_encoding](#input\_content\_encoding) | Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. | `string` | `null` | no |
+| [content\_language](#input\_content\_language) | The language the content is in e.g. en-US or en-GB. | `string` | `null` | no |
+| [content\_type](#input\_content\_type) | A standard MIME type describing the format of the object data, e.g. application/octet-stream. All Valid MIME Types are valid for this input. | `string` | `null` | no |
+| [create](#input\_create) | Whether to create this resource or not? | `bool` | `true` | no |
+| [etag](#input\_etag) | Used to trigger updates. This attribute is not compatible with KMS encryption, kms\_key\_id or server\_side\_encryption = "aws:kms". | `string` | `null` | no |
+| [file\_source](#input\_file\_source) | The path to a file that will be read and uploaded as raw bytes for the object content. | `string` | `null` | no |
+| [force\_destroy](#input\_force\_destroy) | Allow the object to be deleted by removing any legal hold on any object version. Default is false. This value should be set to true only if the bucket has S3 object lock enabled. | `bool` | `false` | no |
+| [key](#input\_key) | The name of the object once it is in the bucket. | `string` | `""` | no |
+| [kms\_key\_id](#input\_kms\_key\_id) | Amazon Resource Name (ARN) of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the aws\_kms\_key resource, use the arn attribute. If referencing the aws\_kms\_alias data source or resource, use the target\_key\_arn attribute. Terraform will only perform drift detection if a configuration value is provided. | `string` | `null` | no |
+| [metadata](#input\_metadata) | A map of keys/values to provision metadata (will be automatically prefixed by x-amz-meta-, note that only lowercase label are currently supported by the AWS Go API). | `map(string)` | `{}` | no |
+| [object\_lock\_legal\_hold\_status](#input\_object\_lock\_legal\_hold\_status) | The legal hold status that you want to apply to the specified object. Valid values are ON and OFF. | `string` | `null` | no |
+| [object\_lock\_mode](#input\_object\_lock\_mode) | The object lock retention mode that you want to apply to this object. Valid values are GOVERNANCE and COMPLIANCE. | `string` | `null` | no |
+| [object\_lock\_retain\_until\_date](#input\_object\_lock\_retain\_until\_date) | The date and time, in RFC3339 format, when this object's object lock will expire. | `string` | `null` | no |
+| [override\_default\_tags](#input\_override\_default\_tags) | Ignore provider default\_tags. S3 objects support a maximum of 10 tags. | `bool` | `false` | no |
+| [server\_side\_encryption](#input\_server\_side\_encryption) | Specifies server-side encryption of the object in S3. Valid values are "AES256" and "aws:kms". | `string` | `null` | no |
+| [source\_hash](#input\_source\_hash) | Triggers updates like etag but useful to address etag encryption limitations. Set using filemd5("path/to/source") (Terraform 0.11.12 or later). (The value is only stored in state and not saved by AWS.) | `string` | `null` | no |
+| [storage\_class](#input\_storage\_class) | Specifies the desired Storage Class for the object. Can be either STANDARD, REDUCED\_REDUNDANCY, ONEZONE\_IA, INTELLIGENT\_TIERING, GLACIER, DEEP\_ARCHIVE, or STANDARD\_IA. Defaults to STANDARD. | `string` | `null` | no |
+| [tags](#input\_tags) | A map of tags to assign to the object. | `map(string)` | `{}` | no |
+| [website\_redirect](#input\_website\_redirect) | Specifies a target URL for website redirect. | `string` | `null` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [s3\_object\_etag](#output\_s3\_object\_etag) | The ETag generated for the object (an MD5 sum of the object content). |
+| [s3\_object\_id](#output\_s3\_object\_id) | The key of S3 object |
+| [s3\_object\_version\_id](#output\_s3\_object\_version\_id) | A unique version ID value for the object, if bucket versioning is enabled. |
+
+
+
+# Changelog
+
diff --git "a/__tests__/fixtures/vpc\342\200\222endpoint.md" "b/__tests__/fixtures/vpc\342\200\222endpoint.md"
new file mode 100644
index 0000000..e04dc99
--- /dev/null
+++ "b/__tests__/fixtures/vpc\342\200\222endpoint.md"
@@ -0,0 +1,72 @@
+# Usage
+
+To use this module in your Terraform, refer to the below module example:
+
+```hcl
+module "vpc_endpoint" {
+ source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=vpc-endpoint/v1.0.0"
+
+ # See inputs below for additional required parameters
+}
+```
+
+# Attributes
+
+
+## Requirements
+
+| Name | Version |
+|------|---------|
+| [terraform](#requirement\_terraform) | >= 1.0 |
+| [aws](#requirement\_aws) | >= 5.46 |
+
+## Providers
+
+| Name | Version |
+|------|---------|
+| [aws](#provider\_aws) | >= 5.46 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|------|------|
+| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
+| [aws_security_group_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
+| [aws_vpc_endpoint.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource |
+| [aws_vpc_endpoint_service.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/vpc_endpoint_service) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------|-------------|------|---------|:--------:|
+| [endpoints](#input\_endpoints) | A map of interface and/or gateway endpoints containing their properties and configurations | `any` | `{}` | no |
+| [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no |
+| [security\_group\_ids](#input\_security\_group\_ids) | Default security group IDs to associate with the VPC endpoints | `list(string)` | `[]` | no |
+| [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created. Conflicts with `security_group_name_prefix` | `string` | `null` | no |
+| [security\_group\_name\_prefix](#input\_security\_group\_name\_prefix) | Name prefix to use on security group created. Conflicts with `security_group_name` | `string` | `null` | no |
+| [security\_group\_rules](#input\_security\_group\_rules) | Security group rules to add to the security group created | `any` | `{}` | no |
+| [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
+| [subnet\_ids](#input\_subnet\_ids) | Default subnets IDs to associate with the VPC endpoints | `list(string)` | `[]` | no |
+| [tags](#input\_tags) | A map of tags to use on all resources | `map(string)` | `{}` | no |
+| [timeouts](#input\_timeouts) | Define maximum timeout for creating, updating, and deleting VPC endpoint resources | `map(string)` | `{}` | no |
+| [vpc\_id](#input\_vpc\_id) | The ID of the VPC in which the endpoint will be used | `string` | `null` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [endpoints](#output\_endpoints) | Array containing the full resource object and attributes for all endpoints created |
+| [security\_group\_arn](#output\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group |
+| [security\_group\_id](#output\_security\_group\_id) | ID of the security group |
+
+
+
+# Changelog
+
+Sample Body
+## Heading
+Sample Release
\ No newline at end of file
diff --git a/__tests__/terraform-module.test.ts b/__tests__/terraform-module.test.ts
index 4c3c858..96deb6b 100644
--- a/__tests__/terraform-module.test.ts
+++ b/__tests__/terraform-module.test.ts
@@ -2,6 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { config } from '@/mocks/config';
+import { context } from '@/mocks/context';
import {
getAllTerraformModules,
getTerraformChangedModules,
@@ -84,17 +85,15 @@ describe('terraform-module', () => {
});
describe('getAllTerraformModules() - with temporary directory', () => {
- let tempDir: string;
+ let tmpDir: string;
let moduleDir: string;
- const origCwd = process.cwd();
-
beforeEach(() => {
// Create a temporary directory with a random suffix
- tempDir = mkdtempSync(join(tmpdir(), 'terraform-test-'));
+ tmpDir = mkdtempSync(join(tmpdir(), 'terraform-test-'));
// Create the module directory structure
- moduleDir = join(tempDir, 'tf-modules', 'test-module');
+ moduleDir = join(tmpDir, 'tf-modules', 'test-module');
mkdirSync(moduleDir, { recursive: true });
// Create a main.tf file in the module directory
@@ -105,13 +104,14 @@ describe('terraform-module', () => {
`;
writeFileSync(join(moduleDir, 'variables.tf'), mainTfContent);
- process.chdir(tempDir);
+ context.set({
+ workspaceDir: tmpDir,
+ });
});
afterEach(() => {
// Clean up the temporary directory and all its contents
- rmSync(tempDir, { recursive: true, force: true });
- process.chdir(origCwd);
+ rmSync(tmpDir, { recursive: true, force: true });
});
it('should handle single module with no changes', () => {
@@ -119,7 +119,7 @@ describe('terraform-module', () => {
{
message: 'docs: variables update',
sha: 'xyz789',
- files: [`${moduleDir}/variables.tf`],
+ files: [`${moduleDir}/variables.tf`], // testing with absolute path which works also
},
];
const mockTags: string[] = ['tf-modules/test-module/v1.0.0'];
@@ -127,7 +127,7 @@ describe('terraform-module', () => {
config.set({ moduleChangeExcludePatterns: ['*.md'] });
- const modules = getAllTerraformModules(tempDir, mockCommits, mockTags, mockReleases);
+ const modules = getAllTerraformModules(mockCommits, mockTags, mockReleases);
expect(modules).toHaveLength(1);
expect(modules[0].moduleName).toBe('tf-modules/test-module');
@@ -144,7 +144,6 @@ describe('terraform-module', () => {
});
describe('getAllTerraformModules', () => {
- // Test Constants
const workspaceDir = process.cwd();
// Type-safe mock data
@@ -172,8 +171,14 @@ describe('terraform-module', () => {
},
];
+ beforeEach(() => {
+ context.set({
+ workspaceDir,
+ });
+ });
+
it('should identify terraform modules and track their changes', () => {
- const modules = getAllTerraformModules(workspaceDir, mockCommits, mockTags, mockReleases);
+ const modules = getAllTerraformModules(mockCommits, mockTags, mockReleases);
expect(modules).toHaveLength(2); // Length of our mock modules
expect(startGroup).toHaveBeenCalledWith('Finding all Terraform modules with corresponding changes');
@@ -214,7 +219,7 @@ describe('terraform-module', () => {
it('should handle modules with no changes', () => {
const noChangeCommits: CommitDetails[] = [];
- const modules = getAllTerraformModules(workspaceDir, noChangeCommits, mockTags, mockReleases);
+ const modules = getAllTerraformModules(noChangeCommits, mockTags, mockReleases);
expect(modules).toHaveLength(2);
for (const module of modules) {
expect('isChanged' in module).toBe(false);
@@ -232,7 +237,7 @@ describe('terraform-module', () => {
},
];
config.set({ moduleChangeExcludePatterns: ['*.md'] });
- const modules = getAllTerraformModules(workspaceDir, commitsWithExcludedFiles, mockTags, mockReleases);
+ const modules = getAllTerraformModules(commitsWithExcludedFiles, mockTags, mockReleases);
expect(modules).toHaveLength(2);
for (const module of modules) {
if (module.moduleName === 'tf-modules/vpc-endpoint') {
@@ -266,7 +271,7 @@ describe('terraform-module', () => {
},
];
config.set({ moduleChangeExcludePatterns: ['*.md'] });
- const modules = getAllTerraformModules(workspaceDir, commitsWithExcludedFiles, mockTags, mockReleases);
+ const modules = getAllTerraformModules(commitsWithExcludedFiles, mockTags, mockReleases);
expect(modules).toHaveLength(2);
for (const module of modules) {
if (module.moduleName === 'tf-modules/vpc-endpoint') {
@@ -317,7 +322,7 @@ describe('terraform-module', () => {
},
];
- const modules = getAllTerraformModules(workspaceDir, mockCommits, mockTags, mockReleases);
+ const modules = getAllTerraformModules(mockCommits, mockTags, mockReleases);
// Find the vpc-endpoint module
const vpcModule = modules.find((module) => module.moduleName === 'tf-modules/vpc-endpoint');
@@ -338,7 +343,7 @@ describe('terraform-module', () => {
files: ['main.tf'],
},
];
- getAllTerraformModules(workspaceDir, commits, mockTags, mockReleases);
+ getAllTerraformModules(commits, mockTags, mockReleases);
expect(info).toHaveBeenCalledWith('Analyzing file: main.tf');
});
@@ -352,7 +357,7 @@ describe('terraform-module', () => {
},
];
- const modules = getAllTerraformModules(workspaceDir, nestedModuleCommit, mockTags, mockReleases);
+ const modules = getAllTerraformModules(nestedModuleCommit, mockTags, mockReleases);
for (const module of modules) {
if (module.moduleName === 'tf-modules/s3-bucket-object') {
@@ -414,7 +419,7 @@ describe('terraform-module', () => {
},
];
- const modules = getAllTerraformModules(workspaceDir, commits, tags, releases);
+ const modules = getAllTerraformModules(commits, tags, releases);
const testModule = modules.find((m) => m.moduleName === moduleName);
expect(testModule).toBeDefined();
diff --git a/__tests__/wiki.test.ts b/__tests__/wiki.test.ts
new file mode 100644
index 0000000..4f98e66
--- /dev/null
+++ b/__tests__/wiki.test.ts
@@ -0,0 +1,320 @@
+import { execFileSync } from 'node:child_process';
+import type { ExecFileSyncOptions } from 'node:child_process';
+import { existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node:fs';
+import { cpus, tmpdir } from 'node:os';
+import { basename, join } from 'node:path';
+import { config } from '@/mocks/config';
+import { context } from '@/mocks/context';
+import { installTerraformDocs } from '@/terraform-docs';
+import { getAllTerraformModules } from '@/terraform-module';
+import type { ExecSyncError } from '@/types';
+import { checkoutWiki, commitAndPushWikiChanges, generateWikiFiles, getWikiLink } from '@/wiki';
+import { endGroup, info, startGroup } from '@actions/core';
+import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
+
+vi.mock('node:child_process', async () => {
+ const actual = await vi.importActual('node:child_process');
+ return {
+ ...actual,
+ execFileSync: vi.fn(),
+ };
+});
+
+describe('wiki', async () => {
+ let tmpDir: string;
+ let wikiDir: string;
+
+ const originalNodeChildProcess = (await vi.importActual('node:child_process')) as typeof import('node:child_process');
+ const originalExecFileSync = originalNodeChildProcess.execFileSync;
+
+ // Grab the original set of modules by moving the workspaceDir to tf-modules
+ context.workspaceDir = join(process.cwd(), '/tf-modules');
+ const terraformModules = getAllTerraformModules(
+ [
+ {
+ message: 'Update VPC endpoint',
+ sha: 'sha00234',
+ files: ['/vpc-endpoint/main.tf'],
+ },
+ ],
+ ['vpc-endpoint/v1.0.0'],
+ [
+ {
+ id: 123434,
+ title: 'vpc-endpoint/v1.0.0',
+ body: 'Sample Body\n## Heading\nSample Release',
+ tagName: 'vpc-endpoint/v1.0.0',
+ },
+ ],
+ );
+
+ beforeEach(() => {
+ // Reset mocks and context
+ vi.clearAllMocks();
+
+ tmpDir = mkdtempSync(join(tmpdir(), 'wiki-test-'));
+ wikiDir = join(tmpDir, '.wiki');
+ mkdirSync(wikiDir);
+
+ context.set({
+ repo: { owner: 'techpivot', repo: 'terraform-module-releaser' },
+ repoUrl: 'https://github.com/techpivot/terraform-module-releaser',
+ workspaceDir: tmpDir,
+ prBody: 'Test PR body',
+ prNumber: 123,
+ prTitle: 'Test PR title',
+ });
+ });
+
+ afterEach(() => {
+ if (existsSync(tmpDir)) {
+ rmSync(tmpDir, { recursive: true });
+ }
+ });
+
+ describe('checkoutWiki()', () => {
+ it('should properly initialize and configure wiki repository', () => {
+ checkoutWiki();
+
+ // Verify git commands were called in correct order
+ const gitCalls = vi.mocked(execFileSync).mock.calls.map((call) => call?.[1]?.join(' ') || '');
+
+ expect(gitCalls).toEqual([
+ `config --global --add safe.directory ${wikiDir}`,
+ `init --initial-branch=master ${wikiDir}`,
+ 'remote add origin https://github.com/techpivot/terraform-module-releaser.wiki',
+ 'config --local --unset-all http.https://github.com/.extraheader',
+ expect.stringContaining('config --local http.https://github.com/.extraheader Authorization: Basic'),
+ 'fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +refs/heads/master*:refs/remotes/origin/master* +refs/tags/master*:refs/tags/master*',
+ 'checkout master',
+ ]);
+
+ expect(startGroup).toHaveBeenCalledWith(
+ 'Checking out wiki repository [https://github.com/techpivot/terraform-module-releaser.wiki]',
+ );
+ expect(endGroup).toHaveBeenCalled();
+ });
+
+ it('should handle unsetting config extraheader and throwing error accordingly', () => {
+ const mockExecFileSync = vi.fn(
+ (command: string, args?: readonly string[] | undefined, options?: ExecFileSyncOptions) => {
+ if (args?.includes('--unset-all') && args.includes('http.https://github.com/.extraheader')) {
+ const error = new Error('git config error') as ExecSyncError;
+ error.status = 10;
+ throw error;
+ }
+
+ // Default return for other cases
+ return Buffer.from('');
+ },
+ );
+ vi.mocked(execFileSync).mockImplementation(mockExecFileSync);
+
+ try {
+ checkoutWiki();
+ } catch (error) {
+ // Check for ExecException properties
+ expect(error).toEqual(
+ expect.objectContaining({
+ message: 'git config error',
+ status: 10,
+ }),
+ );
+ }
+ });
+
+ it('should handle unsetting config extraheader gracefully', () => {
+ const mockExecFileSync = vi.fn(
+ (command: string, args?: readonly string[] | undefined, options?: ExecFileSyncOptions) => {
+ if (args?.includes('--unset-all') && args.includes('http.https://github.com/.extraheader')) {
+ const error = new Error('git config error') as ExecSyncError;
+ error.status = 5;
+ throw error;
+ }
+
+ // Default return for other cases
+ return Buffer.from('');
+ },
+ );
+ vi.mocked(execFileSync).mockImplementation(mockExecFileSync);
+
+ expect(() => checkoutWiki()).not.toThrow();
+ });
+
+ it('should handle wiki clone failures gracefully', () => {
+ vi.mocked(execFileSync).mockImplementationOnce(() => {
+ throw new Error('Repository not found');
+ });
+
+ expect(() => checkoutWiki()).toThrow('Repository not found');
+ });
+
+ it('should handle create wiki directory if it does not exist', () => {
+ vi.resetAllMocks();
+ rmSync(wikiDir, { recursive: true });
+ checkoutWiki();
+ expect(existsSync(wikiDir)).toBe(true);
+ });
+ });
+
+ describe('getWikiLink()', () => {
+ it.each([
+ [true, '/techpivot/terraform-module-releaser/wiki/test‒module'],
+ [false, 'https://github.com/techpivot/terraform-module-releaser/wiki/test‒module'],
+ ])('should generate correct %s link', (relative, expected) => {
+ expect(getWikiLink('test-module', relative)).toBe(expected);
+ });
+ });
+
+ describe('generateWikiFiles()', () => {
+ beforeAll(() => {
+ vi.mocked(execFileSync).mockImplementation(originalExecFileSync);
+ // Actually install terraform-docs as we're actually going to generate using terraform docs.
+ installTerraformDocs(config.terraformDocsVersion);
+
+ // We generate some console.log statements. Let's keep the tests cleaner
+ vi.spyOn(console, 'log').mockImplementation(() => {});
+ });
+
+ afterAll(() => {
+ vi.mocked(execFileSync).mockImplementation(vi.fn());
+ });
+
+ it('should generate all required wiki files', async () => {
+ const files = await generateWikiFiles(terraformModules);
+
+ // Get all expected file basenames from fixtures
+ const fixturesDir = join(process.cwd(), '__tests__', 'fixtures');
+ const expectedFiles = readdirSync(fixturesDir).map((file) => basename(file));
+ expect(expectedFiles.length).equals(files.length);
+
+ // Compare each generated file to its corresponding fixture
+ for (const file of files) {
+ const generatedContent = readFileSync(file, 'utf8');
+ const expectedFilePath = join(fixturesDir, basename(file));
+ const expectedContent = readFileSync(expectedFilePath, 'utf8');
+
+ // Assert that the contents match
+ expect(expectedContent).toEqual(generatedContent);
+ }
+
+ expect(startGroup).toHaveBeenCalledWith('Generating wiki ...');
+ expect(endGroup).toHaveBeenCalled();
+
+ const expectedCalls = [
+ ['Removing existing wiki files...'],
+ [`Removed contents of directory [${wikiDir}], preserving items: .git`],
+ [`Using parallelism: ${cpus().length + 2}`],
+ ['Generating tf-docs for: s3-bucket-object'],
+ ['Generating tf-docs for: vpc-endpoint'],
+ ['Finished tf-docs for: vpc-endpoint'],
+ ['Generated: vpc‒endpoint.md'],
+ ['Finished tf-docs for: s3-bucket-object'],
+ ['Generated: s3‒bucket‒object.md'],
+ ['Generated: Home.md'],
+ ['Generated: _Sidebar.md'],
+ ['Generated: _Footer.md'],
+ ['Wiki files generated:'],
+ ];
+ for (const call of expectedCalls) {
+ expect(info).toHaveBeenCalledWith(...call);
+ }
+
+ expect(vi.mocked(info).mock.calls).toHaveLength(expectedCalls.length);
+ });
+
+ it('should not generate branding for footer when disableBranding enabled', async () => {
+ config.set({ disableBranding: true });
+ await generateWikiFiles(terraformModules);
+ expect(info).toHaveBeenCalledWith('Skipping footer generation as branding is disabled');
+ });
+
+ it('should generate proper wiki link [relative]', () => {
+ expect(getWikiLink('aws/vpc', true)).toEqual('/techpivot/terraform-module-releaser/wiki/aws∕vpc');
+ });
+
+ it('should generate proper wiki link [absolute]', () => {
+ expect(getWikiLink('aws/vpc', false)).toEqual(
+ 'https://github.com/techpivot/terraform-module-releaser/wiki/aws∕vpc',
+ );
+ });
+ });
+
+ describe('commitAndPushWikiChanges()', () => {
+ it('should commit and push changes when changes are detected', () => {
+ // Mock git status to indicate changes exist
+ vi.mocked(execFileSync).mockImplementationOnce(() => Buffer.from('M _Sidebar.md\n'));
+
+ commitAndPushWikiChanges();
+
+ // Verify git commands were called in correct order
+ const gitCalls = vi.mocked(execFileSync).mock.calls.map((call) => call?.[1]?.join(' ') || '');
+
+ expect(gitCalls).toEqual([
+ 'status --porcelain',
+ 'config --local user.name GitHub Actions',
+ 'config --local user.email 41898282+github-actions[bot]@users.noreply.github.com',
+ 'add .',
+ 'commit -m PR #123 - Test PR title\n\nTest PR body',
+ 'push origin',
+ ]);
+
+ expect(startGroup).toHaveBeenCalledWith('Committing and pushing changes to wiki');
+ expect(info).toHaveBeenCalledWith('Checking for changes in wiki repository');
+ expect(info).toHaveBeenCalledWith('git status output: M _Sidebar.md');
+ expect(info).toHaveBeenCalledWith('Changes committed and pushed to wiki repository');
+ expect(endGroup).toHaveBeenCalled();
+ });
+
+ it('should skip commit and push when no changes are detected', () => {
+ // Mock git status to indicate no changes
+ vi.mocked(execFileSync).mockImplementationOnce(() => Buffer.from(''));
+
+ commitAndPushWikiChanges();
+
+ // Verify only status check was called
+ const gitCalls = vi.mocked(execFileSync).mock.calls.map((call) => call?.[1]?.join(' ') || '');
+ expect(gitCalls).toEqual(['status --porcelain']);
+
+ expect(startGroup).toHaveBeenCalledWith('Committing and pushing changes to wiki');
+ expect(info).toHaveBeenCalledWith('Checking for changes in wiki repository');
+ expect(info).toHaveBeenCalledWith('git status output: ');
+ expect(info).toHaveBeenCalledWith('No changes detected, skipping commit and push');
+ expect(endGroup).toHaveBeenCalled();
+ });
+
+ it('should handle git command failures gracefully', () => {
+ // Mock git status to indicate changes exist but make add command fail
+ vi.mocked(execFileSync)
+ .mockImplementationOnce(() => Buffer.from('M _Sidebar.md\n'))
+ .mockImplementationOnce(() => {
+ throw new Error('Git command failed');
+ });
+
+ expect(() => commitAndPushWikiChanges()).toThrow('Git command failed');
+
+ expect(startGroup).toHaveBeenCalledWith('Committing and pushing changes to wiki');
+ expect(info).toHaveBeenCalledWith('Checking for changes in wiki repository');
+ expect(info).toHaveBeenCalledWith('git status output: M _Sidebar.md');
+ expect(endGroup).toHaveBeenCalled();
+ });
+
+ it('should use complete PR information in commit message', () => {
+ // Set up PR context with multiline body
+ context.set({
+ prBody: 'Line 1\nLine 2\nLine 3',
+ prNumber: 456,
+ prTitle: 'Complex PR title',
+ });
+
+ // Mock git status to indicate changes exist
+ vi.mocked(execFileSync).mockImplementationOnce(() => Buffer.from('M _Sidebar.md\n'));
+
+ commitAndPushWikiChanges();
+
+ // Verify commit message format
+ const commitCall = vi.mocked(execFileSync).mock.calls.find((call) => call?.[1]?.includes('commit'));
+ expect(commitCall?.[1]).toEqual(['commit', '-m', 'PR #456 - Complex PR title\n\nLine 1\nLine 2\nLine 3']);
+ });
+ });
+});
diff --git a/package-lock.json b/package-lock.json
index a0fd112..e503bde 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -333,6 +333,7 @@
"ppc64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"aix"
@@ -349,6 +350,7 @@
"arm"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"android"
@@ -365,6 +367,7 @@
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"android"
@@ -381,6 +384,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"android"
@@ -397,6 +401,7 @@
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -413,6 +418,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -429,6 +435,7 @@
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -445,6 +452,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -461,6 +469,7 @@
"arm"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -477,6 +486,7 @@
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -493,6 +503,7 @@
"ia32"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -509,6 +520,7 @@
"loong64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -525,6 +537,7 @@
"mips64el"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -541,6 +554,7 @@
"ppc64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -557,6 +571,7 @@
"riscv64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -573,6 +588,7 @@
"s390x"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -589,6 +605,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
@@ -605,6 +622,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"netbsd"
@@ -621,6 +639,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"openbsd"
@@ -637,6 +656,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"sunos"
@@ -653,6 +673,7 @@
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"win32"
@@ -669,6 +690,7 @@
"ia32"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"win32"
@@ -685,6 +707,7 @@
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"win32"
@@ -928,234 +951,252 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.2.tgz",
- "integrity": "sha512-Tj+j7Pyzd15wAdSJswvs5CJzJNV+qqSUcr/aCD+jpQSBtXvGnV0pnrjoc8zFTe9fcKCatkpFpOO7yAzpO998HA==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz",
+ "integrity": "sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==",
"cpu": [
"arm"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.2.tgz",
- "integrity": "sha512-xsPeJgh2ThBpUqlLgRfiVYBEf/P1nWlWvReG+aBWfNv3XEBpa6ZCmxSVnxJgLgkNz4IbxpLy64h2gCmAAQLneQ==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz",
+ "integrity": "sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.2.tgz",
- "integrity": "sha512-KnXU4m9MywuZFedL35Z3PuwiTSn/yqRIhrEA9j+7OSkji39NzVkgxuxTYg5F8ryGysq4iFADaU5osSizMXhU2A==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz",
+ "integrity": "sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.2.tgz",
- "integrity": "sha512-Hj77A3yTvUeCIx/Vi+4d4IbYhyTwtHj07lVzUgpUq9YpJSEiGJj4vXMKwzJ3w5zp5v3PFvpJNgc/J31smZey6g==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz",
+ "integrity": "sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==",
"cpu": [
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.2.tgz",
- "integrity": "sha512-RjgKf5C3xbn8gxvCm5VgKZ4nn0pRAIe90J0/fdHUsgztd3+Zesb2lm2+r6uX4prV2eUByuxJNdt647/1KPRq5g==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz",
+ "integrity": "sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.2.tgz",
- "integrity": "sha512-duq21FoXwQtuws+V9H6UZ+eCBc7fxSpMK1GQINKn3fAyd9DFYKPJNcUhdIKOrMFjLEJgQskoMoiuizMt+dl20g==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz",
+ "integrity": "sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==",
"cpu": [
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.2.tgz",
- "integrity": "sha512-6npqOKEPRZkLrMcvyC/32OzJ2srdPzCylJjiTJT2c0bwwSGm7nz2F9mNQ1WrAqCBZROcQn91Fno+khFhVijmFA==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz",
+ "integrity": "sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==",
"cpu": [
"arm"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.2.tgz",
- "integrity": "sha512-V9Xg6eXtgBtHq2jnuQwM/jr2mwe2EycnopO8cbOvpzFuySCGtKlPCI3Hj9xup/pJK5Q0388qfZZy2DqV2J8ftw==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz",
+ "integrity": "sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==",
"cpu": [
"arm"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.2.tgz",
- "integrity": "sha512-uCFX9gtZJoQl2xDTpRdseYuNqyKkuMDtH6zSrBTA28yTfKyjN9hQ2B04N5ynR8ILCoSDOrG/Eg+J2TtJ1e/CSA==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz",
+ "integrity": "sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.2.tgz",
- "integrity": "sha512-/PU9P+7Rkz8JFYDHIi+xzHabOu9qEWR07L5nWLIUsvserrxegZExKCi2jhMZRd0ATdboKylu/K5yAXbp7fYFvA==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz",
+ "integrity": "sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.2.tgz",
- "integrity": "sha512-eCHmol/dT5odMYi/N0R0HC8V8QE40rEpkyje/ZAXJYNNoSfrObOvG/Mn+s1F/FJyB7co7UQZZf6FuWnN6a7f4g==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz",
+ "integrity": "sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==",
"cpu": [
"ppc64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.2.tgz",
- "integrity": "sha512-DEP3Njr9/ADDln3kNi76PXonLMSSMiCir0VHXxmGSHxCxDfQ70oWjHcJGfiBugzaqmYdTC7Y+8Int6qbnxPBIQ==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz",
+ "integrity": "sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==",
"cpu": [
"riscv64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.2.tgz",
- "integrity": "sha512-NHGo5i6IE/PtEPh5m0yw5OmPMpesFnzMIS/lzvN5vknnC1sXM5Z/id5VgcNPgpD+wHmIcuYYgW+Q53v+9s96lQ==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz",
+ "integrity": "sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==",
"cpu": [
"s390x"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.2.tgz",
- "integrity": "sha512-PaW2DY5Tan+IFvNJGHDmUrORadbe/Ceh8tQxi8cmdQVCCYsLoQo2cuaSj+AU+YRX8M4ivS2vJ9UGaxfuNN7gmg==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz",
+ "integrity": "sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==",
"cpu": [
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.2.tgz",
- "integrity": "sha512-dOlWEMg2gI91Qx5I/HYqOD6iqlJspxLcS4Zlg3vjk1srE67z5T2Uz91yg/qA8sY0XcwQrFzWWiZhMNERylLrpQ==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz",
+ "integrity": "sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==",
"cpu": [
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.2.tgz",
- "integrity": "sha512-euMIv/4x5Y2/ImlbGl88mwKNXDsvzbWUlT7DFky76z2keajCtcbAsN9LUdmk31hAoVmJJYSThgdA0EsPeTr1+w==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz",
+ "integrity": "sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==",
"cpu": [
"arm64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.2.tgz",
- "integrity": "sha512-RsnE6LQkUHlkC10RKngtHNLxb7scFykEbEwOFDjr3CeCMG+Rr+cKqlkKc2/wJ1u4u990urRHCbjz31x84PBrSQ==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz",
+ "integrity": "sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==",
"cpu": [
"ia32"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.2.tgz",
- "integrity": "sha512-foJM5vv+z2KQmn7emYdDLyTbkoO5bkHZE1oth2tWbQNGW7mX32d46Hz6T0MqXdWS2vBZhaEtHqdy9WYwGfiliA==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz",
+ "integrity": "sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==",
"cpu": [
"x64"
],
"dev": true,
+ "license": "MIT",
"optional": true,
"os": [
"win32"
@@ -1456,7 +1497,8 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@types/mdast": {
"version": "3.0.15",
@@ -1469,9 +1511,9 @@
}
},
"node_modules/@types/node": {
- "version": "22.9.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
- "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
+ "version": "22.9.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.3.tgz",
+ "integrity": "sha512-F3u1fs/fce3FFk+DAxbxc78DF8x0cY09RRL8GnXLmkJ1jvx3TtPdWoTT5/NiYfI5ASqXBmfqJi9dZ3gxMx4lzw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1497,6 +1539,7 @@
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.3.tgz",
"integrity": "sha512-rnK6hJBS6mwc+Bkab+PGPs9OiS0i/3kdTO+CkI8V0/VrW3vmz7O2Pxjw/owOlmo6PKEIxRSeZKv/kuL9itnpYA==",
"dev": true,
+ "license": "MIT",
"bin": {
"ncc": "dist/ncc/cli.js"
}
@@ -1506,6 +1549,7 @@
"resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.5.tgz",
"integrity": "sha512-/RoopB7XGW7UEkUndRXF87A9CwkoZAJW01pj8/3pgmDVsjMH2IKy6H1A38po9tmUlwhSyYs0az82rbKd9Yaynw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"@bcoe/v8-coverage": "^0.2.3",
@@ -1538,6 +1582,7 @@
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.5.tgz",
"integrity": "sha512-nZSBTW1XIdpZvEJyoP/Sy8fUg0b8od7ZpGDkTUcfJ7wz/VoZAFzFfLyxVxGFhUjJzhYqSbIpfMtl/+k/dpWa3Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@vitest/spy": "2.1.5",
"@vitest/utils": "2.1.5",
@@ -1553,6 +1598,7 @@
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.5.tgz",
"integrity": "sha512-XYW6l3UuBmitWqSUXTNXcVBUCRytDogBsWuNXQijc00dtnU/9OqpXWp4OJroVrad/gLIomAq9aW8yWDBtMthhQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@vitest/spy": "2.1.5",
"estree-walker": "^3.0.3",
@@ -1579,6 +1625,7 @@
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.5.tgz",
"integrity": "sha512-4ZOwtk2bqG5Y6xRGHcveZVr+6txkH7M2e+nPFd6guSoN638v/1XQ0K06eOpi0ptVU/2tW/pIU4IoPotY/GZ9fw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"tinyrainbow": "^1.2.0"
},
@@ -1591,6 +1638,7 @@
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.5.tgz",
"integrity": "sha512-pKHKy3uaUdh7X6p1pxOkgkVAFW7r2I818vHDthYLvUyjRfkKOU6P45PztOch4DZarWQne+VOaIMwA/erSSpB9g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@vitest/utils": "2.1.5",
"pathe": "^1.1.2"
@@ -1604,6 +1652,7 @@
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.5.tgz",
"integrity": "sha512-zmYw47mhfdfnYbuhkQvkkzYroXUumrwWDGlMjpdUr4jBd3HZiV2w7CQHj+z7AAS4VOtWxI4Zt4bWt4/sKcoIjg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@vitest/pretty-format": "2.1.5",
"magic-string": "^0.30.12",
@@ -1618,6 +1667,7 @@
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.5.tgz",
"integrity": "sha512-aWZF3P0r3w6DiYTVskOYuhBc7EMc3jvn1TkBg8ttylFFRqNN2XGD7V5a4aQdk6QiUzZQ4klNBSpCLJgWNdIiNw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"tinyspy": "^3.0.2"
},
@@ -1630,6 +1680,7 @@
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.5.tgz",
"integrity": "sha512-yfj6Yrp0Vesw2cwJbP+cl04OC+IHFsuQsrsJBL9pyGeQXE56v1UAOQco+SR55Vf1nQzfV0QJg1Qum7AaWUwwYg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@vitest/pretty-format": "2.1.5",
"loupe": "^3.1.2",
@@ -1700,6 +1751,7 @@
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=12"
}
@@ -1758,6 +1810,7 @@
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -1778,6 +1831,7 @@
"resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz",
"integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"assertion-error": "^2.0.1",
"check-error": "^2.1.1",
@@ -1854,6 +1908,7 @@
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
"integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 16"
}
@@ -1886,9 +1941,9 @@
"license": "MIT"
},
"node_modules/cross-spawn": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz",
- "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1956,6 +2011,7 @@
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
"integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -2005,7 +2061,8 @@
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/esbuild": {
"version": "0.21.5",
@@ -2013,6 +2070,7 @@
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
@@ -2077,6 +2135,7 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0"
}
@@ -2227,6 +2286,7 @@
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -2654,7 +2714,8 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz",
"integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/lru-cache": {
"version": "10.4.3",
@@ -2664,9 +2725,9 @@
"license": "ISC"
},
"node_modules/magic-string": {
- "version": "0.30.12",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz",
- "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==",
+ "version": "0.30.13",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.13.tgz",
+ "integrity": "sha512-8rYBO+MsWkgjDSOvLomYnzhdwEG51olQ4zL5KXnNJWV5MNmrb4rTZdrtkhxjnD/QyZUqR/Z/XDsUs/4ej2nx0g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3125,6 +3186,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -3363,13 +3425,15 @@
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
"integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/pathval": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
"integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">= 14.16"
}
@@ -3378,7 +3442,8 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true
+ "dev": true,
+ "license": "ISC"
},
"node_modules/pify": {
"version": "2.3.0",
@@ -3439,6 +3504,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.1.1",
@@ -3756,10 +3822,11 @@
}
},
"node_modules/rollup": {
- "version": "4.27.2",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.2.tgz",
- "integrity": "sha512-KreA+PzWmk2yaFmZVwe6GB2uBD86nXl86OsDkt1bJS9p3vqWuEQ6HnJJ+j/mZi/q0920P99/MVRlB4L3crpF5w==",
+ "version": "4.27.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.4.tgz",
+ "integrity": "sha512-RLKxqHEMjh/RGLsDxAEsaLO3mWgyoU6x9w6n1ikAzet4B3gI2/3yP6PWY2p9QzRTh6MfEIXB3MwsOY0Iv3vNrw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@types/estree": "1.0.6"
},
@@ -3771,24 +3838,24 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.27.2",
- "@rollup/rollup-android-arm64": "4.27.2",
- "@rollup/rollup-darwin-arm64": "4.27.2",
- "@rollup/rollup-darwin-x64": "4.27.2",
- "@rollup/rollup-freebsd-arm64": "4.27.2",
- "@rollup/rollup-freebsd-x64": "4.27.2",
- "@rollup/rollup-linux-arm-gnueabihf": "4.27.2",
- "@rollup/rollup-linux-arm-musleabihf": "4.27.2",
- "@rollup/rollup-linux-arm64-gnu": "4.27.2",
- "@rollup/rollup-linux-arm64-musl": "4.27.2",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.27.2",
- "@rollup/rollup-linux-riscv64-gnu": "4.27.2",
- "@rollup/rollup-linux-s390x-gnu": "4.27.2",
- "@rollup/rollup-linux-x64-gnu": "4.27.2",
- "@rollup/rollup-linux-x64-musl": "4.27.2",
- "@rollup/rollup-win32-arm64-msvc": "4.27.2",
- "@rollup/rollup-win32-ia32-msvc": "4.27.2",
- "@rollup/rollup-win32-x64-msvc": "4.27.2",
+ "@rollup/rollup-android-arm-eabi": "4.27.4",
+ "@rollup/rollup-android-arm64": "4.27.4",
+ "@rollup/rollup-darwin-arm64": "4.27.4",
+ "@rollup/rollup-darwin-x64": "4.27.4",
+ "@rollup/rollup-freebsd-arm64": "4.27.4",
+ "@rollup/rollup-freebsd-x64": "4.27.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.27.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.27.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.27.4",
+ "@rollup/rollup-linux-arm64-musl": "4.27.4",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.27.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.27.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.27.4",
+ "@rollup/rollup-linux-x64-gnu": "4.27.4",
+ "@rollup/rollup-linux-x64-musl": "4.27.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.27.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.27.4",
+ "@rollup/rollup-win32-x64-msvc": "4.27.4",
"fsevents": "~2.3.2"
}
},
@@ -4299,9 +4366,9 @@
"license": "MIT"
},
"node_modules/tinypool": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz",
- "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+ "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4323,6 +4390,7 @@
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
"integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=14.0.0"
}
@@ -4339,9 +4407,9 @@
}
},
"node_modules/ts-deepmerge": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-7.0.1.tgz",
- "integrity": "sha512-JBFCmNenZdUCc+TRNCtXVM6N8y/nDQHAcpj5BlwXG/gnogjam1NunulB9ia68mnqYI446giMfpqeBFFkOleh+g==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/ts-deepmerge/-/ts-deepmerge-7.0.2.tgz",
+ "integrity": "sha512-akcpDTPuez4xzULo5NwuoKwYRtjQJ9eoNfBACiBMaXwNAx7B1PKfe5wqUFJuW5uKzQ68YjDFwPaWHDG1KnFGsA==",
"dev": true,
"license": "ISC",
"engines": {
@@ -4371,9 +4439,9 @@
}
},
"node_modules/typescript": {
- "version": "5.6.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
- "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
+ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -4587,6 +4655,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz",
"integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -4646,6 +4715,7 @@
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.5.tgz",
"integrity": "sha512-rd0QIgx74q4S1Rd56XIiL2cYEdyWn13cunYBIuqh9mpmQr7gGS0IxXoP8R6OaZtNQQLyXSWbd4rXKYUbhFpK5w==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"cac": "^6.7.14",
"debug": "^4.3.7",
@@ -4668,6 +4738,7 @@
"resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.5.tgz",
"integrity": "sha512-P4ljsdpuzRTPI/kbND2sDZ4VmieerR2c9szEZpjc+98Z9ebvnXmM5+0tHEKqYZumXqlvnmfWsjeFOjXVriDG7A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@vitest/expect": "2.1.5",
"@vitest/mocker": "2.1.5",
diff --git a/package.json b/package.json
index 619bc1f..f02d3ff 100644
--- a/package.json
+++ b/package.json
@@ -13,13 +13,7 @@
"bugs": {
"url": "https://github.com/techpivot/terraform-module-releaser/issues"
},
- "keywords": [
- "terraform",
- "module",
- "releaser",
- "github-action",
- "monorepo"
- ],
+ "keywords": ["terraform", "module", "releaser", "github-action", "monorepo"],
"license": "MIT",
"exports": {
".": "./dist/index.js"
diff --git a/sonar-project.properties b/sonar-project.properties
index f22903a..7cbc9b3 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -11,5 +11,4 @@ sonar.sources=src/
sonar.tests=__tests__/
# Test coverage path in GitHub action
-sonar.javascript.lcov.reportPaths=/coverage/lcov.info
-
+sonar.javascript.lcov.reportPaths=./coverage/lcov.info
diff --git a/src/context.ts b/src/context.ts
index a3b234b..de1baaa 100644
--- a/src/context.ts
+++ b/src/context.ts
@@ -134,12 +134,15 @@ function initializeContext(): Context {
isPrMergeEvent: payload.action === 'closed' && payload.pull_request.merged === true,
};
+ const truncatedBody =
+ contextInstance.prBody?.length > 60 ? `${contextInstance.prBody.slice(0, 57)}...` : contextInstance.prBody;
+
info(`Event Name: ${eventName}`);
info(`Repository: ${contextInstance.repo.owner}/${contextInstance.repo.repo}`);
info(`Repository URL: ${contextInstance.repoUrl}`);
info(`Pull Request Number: ${contextInstance.prNumber}`);
info(`Pull Request Title: ${contextInstance.prTitle}`);
- info(`Pull Request Body: ${contextInstance.prBody}`);
+ info(`Pull Request Body: ${truncatedBody}`);
info(`Issue Number: ${contextInstance.issueNumber}`);
info(`Workspace Directory: ${contextInstance.workspaceDir}`);
info(`Is Pull Request Merge Event: ${contextInstance.isPrMergeEvent}`);
diff --git a/src/main.ts b/src/main.ts
index 4059e7f..aecd4d0 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -48,7 +48,7 @@ export async function run(): Promise {
const allReleases = await getAllReleases();
// Get all Terraform modules in this repository including changed metadata
- const terraformModules = getAllTerraformModules(context.workspaceDir, commits, allTags, allReleases);
+ const terraformModules = getAllTerraformModules(commits, allTags, allReleases);
// Create a new array of only changed Terraform modules
const terraformChangedModules = getTerraformChangedModules(terraformModules);
diff --git a/src/terraform-module.ts b/src/terraform-module.ts
index 4d1a338..3399dca 100644
--- a/src/terraform-module.ts
+++ b/src/terraform-module.ts
@@ -1,6 +1,7 @@
import { readdirSync, statSync } from 'node:fs';
-import { dirname, join, relative, resolve } from 'node:path';
+import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
import { config } from '@/config';
+import { context } from '@/context';
import type { CommitDetails, GitHubRelease, TerraformChangedModule, TerraformModule } from '@/types';
import { isTerraformDirectory, shouldExcludeFile } from '@/utils/file';
import { determineReleaseType, getNextTagVersion } from '@/utils/semver';
@@ -75,14 +76,14 @@ function getTerraformModuleNameFromRelativePath(terraformDirectory: string): str
* if no directory is found.
*/
function getTerraformModuleDirectoryRelativePath(filePath: string): string | null {
- let directory = resolve(dirname(filePath)); // Convert to absolute path
- const cwd = process.cwd();
- const rootDir = resolve(cwd); // Get absolute path to current working directory
+ const rootDir = resolve(context.workspaceDir);
+ const absoluteFilePath = isAbsolute(filePath) ? filePath : resolve(context.workspaceDir, filePath); // Handle relative/absolute
+ let directory = dirname(absoluteFilePath);
// Traverse upward until the current working directory (rootDir) is reached
while (directory !== rootDir && directory !== resolve(directory, '..')) {
if (isTerraformDirectory(directory)) {
- return relative(cwd, directory);
+ return relative(rootDir, directory);
}
directory = resolve(directory, '..'); // Move up a directory
@@ -153,7 +154,6 @@ function getReleasesForModule(moduleName: string, allReleases: GitHubRelease[]):
* Retrieves all Terraform modules within the specified workspace directory and any changes based on commits.
* Analyzes the directory structure to identify modules and checks commit history for changes.
*
- * @param {string} workspaceDir - The root directory of the workspace containing Terraform modules.
* @param {CommitDetails[]} commits - Array of commit details to analyze for changes.
* @param {string[]} allTags - List of all tags associated with the modules.
* @param {GitHubRelease[]} allReleases - GitHub releases for the modules.
@@ -163,7 +163,6 @@ function getReleasesForModule(moduleName: string, allReleases: GitHubRelease[]):
*/
export function getAllTerraformModules(
- workspaceDir: string,
commits: CommitDetails[],
allTags: string[],
allReleases: GitHubRelease[],
@@ -172,6 +171,7 @@ export function getAllTerraformModules(
console.time('Elapsed time finding terraform modules'); // Start timing
const terraformModulesMap: Record = {};
+ const workspaceDir = context.workspaceDir;
// Helper function to recursively search for Terraform modules
const searchDirectory = (dir: string) => {
diff --git a/src/types/index.ts b/src/types/index.ts
index 0474e9a..eb64c0a 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -108,7 +108,7 @@ export interface CommitDetails {
sha: string;
/**
- * An array of relative file paths associated with the commit.
+ * An array of relative file paths associated with the commit. Important Note: Files are relative
*/
files: string[];
}
@@ -261,3 +261,32 @@ export interface Context {
*/
isPrMergeEvent: boolean;
}
+
+export interface ExecSyncError extends Error {
+ /**
+ * Pid of the child process.
+ */
+ pid: number;
+ /**
+ * The exit code of the subprocess, or null if the subprocess terminated due to a signal.
+ */
+ status: number | null;
+ /**
+ * The contents of output[1].
+ */
+ stdout: Buffer | string;
+
+ /**
+ * The contents of output[2].
+ */
+ stderr: Buffer | string;
+ /**
+ * The signal used to kill the subprocess, or null if the subprocess did not terminate due to a signal.
+ */
+ signal: string | null;
+
+ /**
+ * The error object if the child process failed or timed out.
+ */
+ error: Error;
+}
diff --git a/src/wiki.ts b/src/wiki.ts
index 860776f..b6c7fc2 100644
--- a/src/wiki.ts
+++ b/src/wiki.ts
@@ -8,7 +8,7 @@ import { getModuleReleaseChangelog } from '@/changelog';
import { config } from '@/config';
import { context } from '@/context';
import { generateTerraformDocs } from '@/terraform-docs';
-import type { TerraformModule } from '@/types';
+import type { ExecSyncError, TerraformModule } from '@/types';
import {
BRANDING_WIKI,
GITHUB_ACTIONS_BOT_EMAIL,
@@ -56,6 +56,7 @@ export function checkoutWiki(): void {
execFileSync(gitPath, ['config', '--global', '--add', 'safe.directory', wikiDirectory], { stdio: 'inherit' });
info('Initializing the repository');
+
if (!existsSync(wikiDirectory)) {
mkdirSync(wikiDirectory);
}
@@ -71,8 +72,15 @@ export function checkoutWiki(): void {
try {
execFileSync(gitPath, ['config', '--local', '--unset-all', 'http.https://github.com/.extraheader'], execWikiOpts);
} catch (error) {
- // This returns exit code 5 if not set. Not a problem. Let's ignore.
+ // Type guard to ensure we're handling the correct error type
+ if (error instanceof Error) {
+ // Only ignore specific status code if needed
+ if ((error as unknown as ExecSyncError).status !== 5) {
+ throw error;
+ }
+ }
}
+
execFileSync(
gitPath,
['config', '--local', 'http.https://github.com/.extraheader', `Authorization: Basic ${basicCredential}`],
@@ -195,40 +203,35 @@ export function getWikiLink(moduleName: string, relative = true): string {
async function generateWikiModule(terraformModule: TerraformModule): Promise {
const { moduleName, latestTag } = terraformModule;
- try {
- const wikiSlugFile = `${getWikiSlug(moduleName)}.md`;
- const wikiFile = join(context.workspaceDir, WIKI_SUBDIRECTORY_NAME, wikiSlugFile);
-
- // Generate a module changelog
- const changelog = getModuleReleaseChangelog(terraformModule);
- const tfDocs = await generateTerraformDocs(terraformModule);
- const wikiContent = [
- '# Usage\n',
- 'To use this module in your Terraform, refer to the below module example:\n',
- '```hcl',
- `module "${moduleName.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}" {`,
- ` source = "git::${context.repoUrl}.git?ref=${latestTag}"`,
- '\n # See inputs below for additional required parameters',
- '}',
- '```',
- '\n# Attributes\n',
- '',
- tfDocs,
- '',
- '\n# Changelog\n',
- changelog,
- ].join('\n');
+ const wikiSlugFile = `${getWikiSlug(moduleName)}.md`;
+ const wikiFile = join(context.workspaceDir, WIKI_SUBDIRECTORY_NAME, wikiSlugFile);
+
+ // Generate a module changelog
+ const changelog = getModuleReleaseChangelog(terraformModule);
+ const tfDocs = await generateTerraformDocs(terraformModule);
+ const wikiContent = [
+ '# Usage\n',
+ 'To use this module in your Terraform, refer to the below module example:\n',
+ '```hcl',
+ `module "${moduleName.replace(/[^a-zA-Z0-9]/g, '_').toLowerCase()}" {`,
+ ` source = "git::${context.repoUrl}.git?ref=${latestTag}"`,
+ '\n # See inputs below for additional required parameters',
+ '}',
+ '```',
+ '\n# Attributes\n',
+ '',
+ tfDocs,
+ '',
+ '\n# Changelog\n',
+ changelog,
+ ].join('\n');
- // Write the markdown content to the wiki file, overwriting if it exists
- await fsp.writeFile(wikiFile, wikiContent, 'utf8');
+ // Write the markdown content to the wiki file, overwriting if it exists
+ await fsp.writeFile(wikiFile, wikiContent, 'utf8');
- info(`Generated: ${wikiSlugFile}`);
+ info(`Generated: ${wikiSlugFile}`);
- return wikiFile;
- } catch (error) {
- console.error(`Error writing wiki file for module: ${moduleName}`, error);
- throw error;
- }
+ return wikiFile;
}
/**
@@ -366,15 +369,9 @@ async function generateWikiFooter(): Promise {
}
const footerFile = join(context.workspaceDir, WIKI_SUBDIRECTORY_NAME, '_Footer.md');
-
- try {
- // If the file doesn't exist, create and write content to it
- await fsp.writeFile(footerFile, BRANDING_WIKI, 'utf8');
- info('Generated: _Footer.md');
- return footerFile;
- } catch (error) {
- console.error(`Error updating _Footer.md: ${error instanceof Error ? error.message : String(error)}`);
- }
+ await fsp.writeFile(footerFile, BRANDING_WIKI, 'utf8');
+ info('Generated: _Footer.md');
+ return footerFile;
}
/**