From 4047b8bfb6840c6bddec8b6582309047bb6a73e0 Mon Sep 17 00:00:00 2001 From: Mark Johnson <739719+virgofx@users.noreply.github.com> Date: Fri, 7 Feb 2025 06:35:47 +0000 Subject: [PATCH] feat: add useSSHSourceFormat option to configure source URL format in Wiki documentation --- .github/workflows/ci.yml | 1 + .github/workflows/test.yml | 4 +-- README.md | 2 ++ __mocks__/config.ts | 2 ++ __tests__/config.test.ts | 5 ++-- __tests__/helpers/inputs.ts | 3 +- __tests__/terraform-docs.test.ts | 19 +++++++++---- __tests__/wiki.test.ts | 47 ++++++++++++++++++++++++++++++++ action.yml | 4 +++ package.json | 8 +----- sonar-project.properties | 1 + src/config.ts | 2 ++ src/types/index.ts | 12 ++++++++ src/wiki.ts | 20 +++++++++++++- 14 files changed, 112 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da6840f..0eac067 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,3 +51,4 @@ jobs: delete-legacy-tags: false # Note: We don't want to delete tags in this repository module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** + use-ssh-source-format: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7cc2282..bfcf8df 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,7 +35,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO_CI_TESTING }} - - name: SonarCloud Scan - uses: sonarsource/sonarcloud-github-action@v4 + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v4 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/README.md b/README.md index a6fcc64..c8863b7 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ configuring the following optional input parameters as needed. | `disable-branding` | Controls whether a small branding link to the action's repository is added to PR comments. Recommended to leave enabled to support OSS. | `false` | | `module-change-exclude-patterns` | A comma-separated list of file patterns to exclude from triggering version changes in Terraform modules. Patterns follow glob syntax (e.g., `.gitignore,_.md`) and are relative to each Terraform module directory. Files matching these patterns will not affect version changes. **WARNING**: Avoid excluding '`_.tf`' files, as they are essential for module detection and versioning processes. | `.gitignore, *.md, *.tftest.hcl, tests/**` | | `module-asset-exclude-patterns` | A comma-separated list of file patterns to exclude when bundling a Terraform module for tag/release. Patterns follow glob syntax (e.g., `tests/\*\*`) and are relative to each Terraform module directory. Files matching these patterns will be excluded from the bundled output. | `.gitignore, *.md, *.tftest.hcl, tests/**` | +| `use-ssh-source-format` | If enabled, all links to source code in generated Wiki documentation will use SSH standard format (e.g., `git::ssh://git@github.com/owner/repo.git`) instead of HTTPS format (`git::https://github.com/owner/repo.git`) | `false` | ### Example Usage with Inputs @@ -217,6 +218,7 @@ jobs: wiki-sidebar-changelog-max: 10 module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/** + use-ssh-source-format: false ``` ## Inspiration diff --git a/__mocks__/config.ts b/__mocks__/config.ts index c3f0d5e..4743323 100644 --- a/__mocks__/config.ts +++ b/__mocks__/config.ts @@ -24,6 +24,7 @@ const defaultConfig: Config = { moduleChangeExcludePatterns: ['.gitignore', '*.md'], moduleAssetExcludePatterns: ['tests/**', 'examples/**'], githubToken: 'ghp_test_token_2c6912E7710c838347Ae178B4', + useSSHSourceFormat: false, }; /** @@ -42,6 +43,7 @@ const validConfigKeys = [ 'moduleChangeExcludePatterns', 'moduleAssetExcludePatterns', 'githubToken', + 'useSSHSourceFormat', ] as const; type ValidConfigKey = (typeof validConfigKeys)[number]; diff --git a/__tests__/config.test.ts b/__tests__/config.test.ts index 64d8fe7..2389646 100644 --- a/__tests__/config.test.ts +++ b/__tests__/config.test.ts @@ -117,10 +117,11 @@ describe('config', () => { expect(config.githubToken).toBe('ghp_test_token_2c6912E7710c838347Ae178B4'); expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md']); expect(config.moduleAssetExcludePatterns).toEqual(['tests/**', 'examples/**']); + expect(config.useSSHSourceFormat).toBe(false); expect(startGroup).toHaveBeenCalledWith('Initializing Config'); expect(startGroup).toHaveBeenCalledTimes(1); expect(endGroup).toHaveBeenCalledTimes(1); - expect(info).toHaveBeenCalledTimes(10); + expect(info).toHaveBeenCalledTimes(11); expect(vi.mocked(info).mock.calls).toEqual([ ['Major Keywords: MAJOR CHANGE, BREAKING CHANGE, !'], ['Minor Keywords: feat, feature'], @@ -132,8 +133,8 @@ describe('config', () => { ['Wiki Sidebar Changelog Max: 10'], ['Module Change Exclude Patterns: .gitignore, *.md'], ['Module Asset Exclude Patterns: tests/**, examples/**'], + ['Use SSH Source Format: false'], ]); - expect(info).toHaveBeenCalledTimes(10); }); }); diff --git a/__tests__/helpers/inputs.ts b/__tests__/helpers/inputs.ts index f5ee200..f7a71e5 100644 --- a/__tests__/helpers/inputs.ts +++ b/__tests__/helpers/inputs.ts @@ -21,9 +21,10 @@ export const defaultInputs = { 'module-change-exclude-patterns': '.gitignore,*.md', 'module-asset-exclude-patterns': 'tests/**,examples/**', github_token: 'ghp_test_token_2c6912E7710c838347Ae178B4', + 'use-ssh-source-format': 'false', }; export const requiredInputs = Object.keys(defaultInputs); -export const booleanInputs = ['delete-legacy-tags', 'disable-wiki', 'disable-branding']; +export const booleanInputs = ['delete-legacy-tags', 'disable-wiki', 'disable-branding', 'use-ssh-source-format']; export const booleanConfigKeys: BooleanConfigKeys[] = ['deleteLegacyTags', 'disableWiki', 'disableBranding']; /** diff --git a/__tests__/terraform-docs.test.ts b/__tests__/terraform-docs.test.ts index 9526d26..fa4344a 100644 --- a/__tests__/terraform-docs.test.ts +++ b/__tests__/terraform-docs.test.ts @@ -7,7 +7,7 @@ import { context } from '@/mocks/context'; import { ensureTerraformDocsConfigDoesNotExist, generateTerraformDocs, installTerraformDocs } from '@/terraform-docs'; import type { TerraformModule } from '@/types'; import { info } from '@actions/core'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; import which from 'which'; const execFilePromisified = promisify(execFile); @@ -50,7 +50,6 @@ describe('terraform-docs', async () => { afterEach(() => { Object.defineProperty(process, 'platform', { value: realPlatform }); Object.defineProperty(process, 'arch', { value: realArch }); - vi.resetAllMocks(); }); describe('install terraform-docs (linux, darwin, freebsd)', () => { @@ -184,6 +183,10 @@ describe('terraform-docs', async () => { expect(() => installTerraformDocs(terraformDocsVersion)).toThrow('not found: invalid-non-existent-binary'); }); + + afterAll(() => { + mockWhichSync.mockRestore(); + }); }); describe('terraform-docs version validation', () => { @@ -219,7 +222,7 @@ describe('terraform-docs', async () => { join('C:\\Windows\\System32', 'terraform-docs.exe'), ]; - beforeEach(async () => { + beforeAll(async () => { // Get real implementations const realChildProcess = (await vi.importActual('node:child_process')) as typeof import('node:child_process'); const realFs = (await vi.importActual('node:fs')) as typeof import('node:fs'); @@ -232,6 +235,14 @@ describe('terraform-docs', async () => { mockWhichSync.mockImplementation(realWhich.sync); }); + afterAll(() => { + // Restore original mock implementations + mockExecFileSync.mockRestore(); + fsExistsSyncMock.mockRestore(); + mockFsUnlinkSync.mockRestore(); + mockWhichSync.mockRestore(); + }); + afterEach(() => { // Cleanup downloaded/installed files for (const file of cleanupFiles) { @@ -241,8 +252,6 @@ describe('terraform-docs', async () => { // Ignore cleanup errors } } - - // Restore original mock implementations (handled via global resetAllMocks()) }); it(`should install terraform-docs on the real system ${process.arch}/${process.platform}`, () => { diff --git a/__tests__/wiki.test.ts b/__tests__/wiki.test.ts index 8275cdb..95db7a8 100644 --- a/__tests__/wiki.test.ts +++ b/__tests__/wiki.test.ts @@ -316,4 +316,51 @@ describe('wiki', async () => { expect(commitCall?.[1]).toEqual(['commit', '-m', 'PR #456 - Complex PR title\n\nLine 1\nLine 2\nLine 3']); }); }); + + describe('formatModuleSource()', () => { + beforeEach(() => { + context.set({ + repo: { owner: 'techpivot', repo: 'terraform-module-releaser' }, + repoUrl: 'https://github.com/techpivot/terraform-module-releaser', + }); + }); + + it('should format source URL as HTTPS when useSSHSourceFormat is false', async () => { + config.set({ useSSHSourceFormat: false }); + const files = await generateWikiFiles(terraformModules); + + // Read each generated .md file and verify it contains HTTPS format + for (const file of files) { + if (file.endsWith('.md')) { + const content = readFileSync(file, 'utf8'); + if (content.includes('source =')) { + expect(content).toContain('source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref='); + expect(content).not.toContain( + 'source = "git::ssh://git@github.com/techpivot/terraform-module-releaser.git?ref=', + ); + } + } + } + }); + + it('should format source URL as SSH when useSSHSourceFormat is true', async () => { + config.set({ useSSHSourceFormat: true }); + const files = await generateWikiFiles(terraformModules); + + // Read each generated .md file and verify it contains SSH format + for (const file of files) { + if (file.endsWith('.md')) { + const content = readFileSync(file, 'utf8'); + if (content.includes('source =')) { + expect(content).toContain( + 'source = "git::ssh://git@github.com/techpivot/terraform-module-releaser.git?ref=', + ); + expect(content).not.toContain( + 'source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=', + ); + } + } + } + }); + }); }); diff --git a/action.yml b/action.yml index 4905464..f3a0f5f 100644 --- a/action.yml +++ b/action.yml @@ -83,6 +83,10 @@ inputs: The minimatch syntax is used for pattern matching. Files matching these patterns will be excluded from the bundled output. required: true default: ".gitignore,*.md,*.tftest.hcl,tests/**" + use-ssh-source-format: + description: If enabled, all links to source code in generated Wiki documentation will use SSH format instead of HTTPS format + required: true + default: "false" github_token: description: | Required for retrieving pull request metadata, tags, releases, updating PR comments, wiki, and creating tags/releases. diff --git a/package.json b/package.json index 6b4c3df..5feab28 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 7cbc9b3..891633d 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,7 @@ sonar.organization=techpivot sonar.projectKey=terraform-module-releaser sonar.projectName=Terraform Module Releaser +sonar.projectDescription=A GitHub Action for managing Terraform modules in GitHub monorepos, automating versioning, releases, and documentation. sonar.sourceEncoding=UTF-8 diff --git a/src/config.ts b/src/config.ts index 3a33778..be28619 100644 --- a/src/config.ts +++ b/src/config.ts @@ -71,6 +71,7 @@ function initializeConfig(): Config { githubToken: getInput('github_token', { required: true }), moduleChangeExcludePatterns: getArrayInput('module-change-exclude-patterns'), moduleAssetExcludePatterns: getArrayInput('module-asset-exclude-patterns'), + useSSHSourceFormat: getBooleanInput('use-ssh-source-format', { required: true }), }; // Validate that *.tf is not in excludePatterns @@ -96,6 +97,7 @@ function initializeConfig(): Config { info(`Wiki Sidebar Changelog Max: ${configInstance.wikiSidebarChangelogMax}`); info(`Module Change Exclude Patterns: ${configInstance.moduleChangeExcludePatterns.join(', ')}`); info(`Module Asset Exclude Patterns: ${configInstance.moduleAssetExcludePatterns.join(', ')}`); + info(`Use SSH Source Format: ${configInstance.useSSHSourceFormat}`); return configInstance; } finally { diff --git a/src/types/index.ts b/src/types/index.ts index 2a3ce1d..9aa3c12 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -194,6 +194,18 @@ export interface Config { * tests and other non-functional files as needed. */ moduleAssetExcludePatterns: string[]; + + /** + * If true, the wiki will use the SSH format for the source URL of the repository. + * This changes the format of the source URL in the generated wiki documentation to use the SSH format. + * + * Example: + * - SSH format: git::ssh://git@github.com/techpivot/terraform-module-releaser.git + * - HTTPS format: git::https://github.com/techpivot/terraform-module-releaser.git + * + * When set to true, the SSH standard format (non scp variation) will be used. Otherwise, the HTTPS format will be used. + */ + useSSHSourceFormat: boolean; } /** diff --git a/src/wiki.ts b/src/wiki.ts index b6c7fc2..cfd2b58 100644 --- a/src/wiki.ts +++ b/src/wiki.ts @@ -190,6 +190,23 @@ export function getWikiLink(moduleName: string, relative = true): string { return `${baseUrl}/wiki/${getWikiSlug(moduleName)}`; } +/** + * Formats the module source URL based on configuration settings. + * + * @param repoUrl - The repository URL + * @param useSSH - Whether to use SSH format + * @returns The formatted source URL for the module + */ +function formatModuleSource(repoUrl: string, useSSH: boolean): string { + if (useSSH) { + // Convert HTTPS URL to SSH format + // From: https://github.com/owner/repo + // To: ssh://git@github.com/owner/repo + return `ssh://${repoUrl.replace(/^https:\/\/github\.com/, 'git@github.com')}.git`; + } + return `${repoUrl}.git`; +} + /** * Generates the wiki file associated with the specified Terraform module. * Ensures that the directory structure is created if it doesn't exist and handles overwriting @@ -209,12 +226,13 @@ async function generateWikiModule(terraformModule: TerraformModule): Promise