Skip to content

feat: add module path ignore functionality, tests, and update deps #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/scripts/changelog.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import { execFileSync } from 'node:child_process';
import https from 'node:https';

const OPENAI_MODEL = 'gpt-4-turbo-2024-04-09';
const OPENAI_MODEL = 'gpt-4o-mini-2024-07-18';
const PROMPT = `
You're the head of developer relations at a SaaS. Write a concise, professional, and fun changelog, prioritizing important changes.

Expand All @@ -33,7 +33,7 @@ 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.
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.

Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ jobs:
disable-wiki: false
wiki-sidebar-changelog-max: 10
delete-legacy-tags: false # Note: We don't want to delete tags in this repository
module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
terraform-docs-version: v0.19.0
module-path-ignore: tf-modules/kms/examples/complete
module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**,examples/**
module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
use-ssh-source-format: true

Expand Down
72 changes: 47 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,24 @@ documentation.</b></sup>
[4]: https://github.com/techpivot/terraform-module-releaser/actions/workflows/codeql-analysis.yml
[5]: https://sonarcloud.io/summary/new_code?id=terraform-module-releaser

Simplify the management of Terraform modules in your monorepo with this **GitHub Action**, designed to automate
module-specific versioning and releases. By streamlining the Terraform module release process, this action allows you to
manage multiple modules in a single repository while still maintaining independence and flexibility. Additionally, it
generates a beautifully crafted wiki for each module, complete with readme information, usage examples, Terraform-docs
details, and a full changelog.

## Key Features

- **Efficient Module Tagging**: Module tags are specifically designed to only include the current Terraform module
directory (and nothing else), thereby dramatically decreasing the size and improving Terraform performance.
- **Automated Release Management**: Identifies Terraform modules affected by changes in a pull request and determines
the necessary release type (major, minor, or patch) based on commit messages.
- **Versioning and Tagging**: Calculates the next version tag for each module and commits, tags, and pushes new versions
for each module individually.
- **Release Notes and Comments**: Generates a pull request comment summarizing module changes and release types, and
creates a GitHub release for each module with a dynamically generated description.
- **Wiki Integration**: Updates the wiki with new release information, including:
- README.md information for each module
- Beautifully crafted module usage examples
- `terraform-docs` details for each module
- Full changelog for each module
- **Deletes Synced**: Automatically removes tags from deleted Terraform modules, keeping your repository organized and
up-to-date.
- **Flexible Configuration**: Offers advanced input options for customization, allowing you to tailor the action to your
specific needs.
Simplify the management of Terraform modules in your monorepo with this **GitHub Action**. It automates module-specific
versioning and releases by creating proper Git tags and GitHub releases based on your commit messages. Each module
maintains independence while living in the same repository, with proper isolation for clean dependency management.
Additionally, the action generates a beautifully crafted wiki for each module, complete with readme information, usage
examples, Terraform-docs details, and a full changelog.

## 🚀 Features

- **Efficient Module Tagging** – Only includes module directory content, dramatically improving Terraform performance.
- **Smart Versioning** – Automatically determines release types (major, minor, patch) based on commit messages.
- **Comprehensive Wiki** – Generates beautiful documentation with usage examples, terraform-docs output, and full
changelogs.
- **Release Automation** – Creates GitHub releases, pull request comments, and version tags with minimal effort.
- **Self-Maintaining** – Automatically removes tags from deleted modules, keeping your repository clean and organized.
- **100% GitHub Native** – No external dependencies or services required for modules or operation, everything stays
within your GitHub ecosystem.
- **Zero Configuration** – Works out-of-the-box with sensible defaults for immediate productivity.
- **Flexible & Extensible** – Customizable settings to precisely match your team's specific workflow requirements.

## Demo

Expand Down Expand Up @@ -180,10 +173,38 @@ configuring the following optional input parameters as needed.
| `disable-wiki` | Whether to disable wiki generation for Terraform modules | `false` |
| `wiki-sidebar-changelog-max` | An integer that specifies how many changelog entries are displayed in the sidebar per module | `5` |
| `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-path-ignore` | Comma separated list of module paths to completely ignore (relative to working directory). This will prevent any versioning, release, or documentation for these modules. | `` (empty) |
| `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` |

### Understanding the filtering options

- **`module-path-ignore`**: Completely ignores specified module paths. Any module whose path matches any pattern in this
list will not be processed at all by the action. This is useful for:

- Excluding example modules (e.g., `**/examples/**`)
- Skipping test modules (e.g., `**/test/**`)
- Ignoring documentation-focused modules (e.g., `**/docs/**`)
- Excluding entire directories or paths that contain Terraform files but shouldn't be versioned as modules

Example:

```yaml
module-path-ignore: "**/examples/**,**/test/**,root-modules"
```

- **`module-change-exclude-patterns`**: These patterns determine which file changes are _ignored_ when checking if a
module needs a new release. For example, changes to documentation, examples, or workflow files typically don't require
a new module release.
- **`module-asset-exclude-patterns`**: When building a release asset for a module, these patterns determine which files
are _excluded_ from the asset. This helps reduce the asset size by omitting test files, examples, documentation, etc.

All pattern matching is implemented using [minimatch](https://github.com/isaacs/minimatch), which supports glob patterns
similar to those used in `.gitignore` files. For more details on the pattern matching implementation, see our
[source code](https://github.com/techpivot/terraform-module-releaser/blob/main/src/utils/file.ts) or visit the
[minimatch documentation](https://github.com/isaacs/minimatch).

### Example Usage with Inputs

```yml
Expand Down Expand Up @@ -219,6 +240,7 @@ jobs:
module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
use-ssh-source-format: false
module-path-ignore: path/to/ignore1,path/to/ignore2
```

## Outputs
Expand Down
2 changes: 2 additions & 0 deletions __mocks__/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const defaultConfig: Config = {
disableWiki: false,
wikiSidebarChangelogMax: 10,
disableBranding: false,
modulePathIgnore: ['tf-modules/kms/examples/complete'],
moduleChangeExcludePatterns: ['.gitignore', '*.md'],
moduleAssetExcludePatterns: ['tests/**', 'examples/**'],
githubToken: 'ghp_test_token_2c6912E7710c838347Ae178B4',
Expand All @@ -40,6 +41,7 @@ const validConfigKeys = [
'disableWiki',
'wikiSidebarChangelogMax',
'disableBranding',
'modulePathIgnore',
'moduleChangeExcludePatterns',
'moduleAssetExcludePatterns',
'githubToken',
Expand Down
51 changes: 47 additions & 4 deletions __tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { clearConfigForTesting, config, getConfig } from '@/config';
import { booleanConfigKeys, booleanInputs, requiredInputs, stubInputEnv } from '@/tests/helpers/inputs';
import {
arrayInputs,
booleanInputs,
inputToConfigKey,
inputToConfigKeyMap,
optionalInputs,
requiredInputs,
stringInputs,
stubInputEnv,
} from '@/tests/helpers/inputs';
import type { Config } from '@/types';
import { endGroup, getBooleanInput, getInput, info, startGroup } from '@actions/core';
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

Expand All @@ -26,6 +36,30 @@ describe('config', () => {
});
}

for (const input of optionalInputs) {
it(`should handle optional input "${input}" when not present`, () => {
stubInputEnv({ [input]: null });
// Simply verify it doesn't throw without the specific error object
expect(() => getConfig()).not.toThrow();

// Get the config and check the actual value
const config = getConfig();
// Get the config key using the mapping directly if possible
const configKey = inputToConfigKeyMap[input] || inputToConfigKey(input);

// Type-safe access using the mapping
if (arrayInputs.includes(input)) {
// Cast configKey to keyof Config to ensure type safety
expect(config[configKey as keyof Config]).toEqual([]);
}
if (stringInputs.includes(input)) {
expect(config[configKey as keyof Config]).toEqual('');
}

expect(getInput).toHaveBeenCalled();
});
}

for (const input of booleanInputs) {
it(`should throw error when input "${input}" has an invalid boolean value`, () => {
stubInputEnv({ [input]: 'invalid-boolean' });
Expand Down Expand Up @@ -70,8 +104,10 @@ describe('config', () => {

// Check the boolean conversion for each key in booleanInputs
const config = getConfig();
for (const inputKey of booleanConfigKeys) {
expect(config[inputKey]).toBe(booleanValue.toLowerCase() === 'true');
for (const booleanInput of booleanInputs) {
// Get config key from the mapping, which is already typed as keyof Config
const configKey = inputToConfigKeyMap[booleanInput];
expect(config[configKey]).toBe(booleanValue.toLowerCase() === 'true');
}
}
});
Expand Down Expand Up @@ -117,11 +153,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.modulePathIgnore).toEqual(['tf-modules/kms/examples/complete']);
expect(config.useSSHSourceFormat).toBe(false);
expect(startGroup).toHaveBeenCalledWith('Initializing Config');
expect(startGroup).toHaveBeenCalledTimes(1);
expect(endGroup).toHaveBeenCalledTimes(1);
expect(info).toHaveBeenCalledTimes(11);
expect(vi.mocked(info).mock.calls).toEqual([
['Major Keywords: MAJOR CHANGE, BREAKING CHANGE, !'],
['Minor Keywords: feat, feature'],
Expand All @@ -131,6 +167,7 @@ describe('config', () => {
['Delete Legacy Tags: false'],
['Disable Wiki: false'],
['Wiki Sidebar Changelog Max: 10'],
['Module Paths to Ignore: tf-modules/kms/examples/complete'],
['Module Change Exclude Patterns: .gitignore, *.md'],
['Module Asset Exclude Patterns: tests/**, examples/**'],
['Use SSH Source Format: false'],
Expand Down Expand Up @@ -180,5 +217,11 @@ describe('config', () => {
expect(config.majorKeywords).toEqual(['BREAKING CHANGE', '!']);
expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md']);
});

it('should handle empty modulePathIgnore', () => {
stubInputEnv({ 'module-path-ignore': '' });
const config = getConfig();
expect(config.modulePathIgnore).toEqual([]);
});
});
});
2 changes: 2 additions & 0 deletions __tests__/fixtures/Home.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ providing an overview of their functionality and the latest versions.

| Module Name | Latest Version |
| -- | -- |
| [kms](/techpivot/terraform-module-releaser/wiki/kms) | null |
| [kms/examples/complete](/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete) | null |
| [s3-bucket-object](/techpivot/terraform-module-releaser/wiki/s3‒bucket‒object) | null |
| [vpc-endpoint](/techpivot/terraform-module-releaser/wiki/vpc‒endpoint) | v1.0.0 |

Expand Down
20 changes: 20 additions & 0 deletions __tests__/fixtures/_Sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
## Terraform Modules

<ul>
<li>
<details>
<summary><a href="/techpivot/terraform-module-releaser/wiki/kms"><b>kms</b></a></summary>
<ul>
<li><a href="/techpivot/terraform-module-releaser/wiki/kms#usage">Usage</a></li>
<li><a href="/techpivot/terraform-module-releaser/wiki/kms#attributes">Attributes</a></li>
<li><a href="/techpivot/terraform-module-releaser/wiki/kms#changelog">Changelog</a></li>
</ul>
</details>
</li>
<li>
<details>
<summary><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete"><b>kms/examples/complete</b></a></summary>
<ul>
<li><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete#usage">Usage</a></li>
<li><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete#attributes">Attributes</a></li>
<li><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete#changelog">Changelog</a></li>
</ul>
</details>
</li>
<li>
<details>
<summary><a href="/techpivot/terraform-module-releaser/wiki/s3‒bucket‒object"><b>s3-bucket-object</b></a></summary>
Expand Down
46 changes: 46 additions & 0 deletions __tests__/fixtures/kms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Usage

To use this module in your Terraform, refer to the below module example:

```hcl
module "kms" {
source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=null"

# See inputs below for additional required parameters
}
```

# Attributes

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.0 |

## Providers

No providers.

## Modules

No modules.

## Resources

No resources.

## Inputs

No inputs.

## Outputs

No outputs.

<!-- END_TF_DOCS -->

# Changelog

46 changes: 46 additions & 0 deletions __tests__/fixtures/kms∕examples∕complete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Usage

To use this module in your Terraform, refer to the below module example:

```hcl
module "kms_examples_complete" {
source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=null"

# See inputs below for additional required parameters
}
```

# Attributes

<!-- BEGIN_TF_DOCS -->
## Requirements

| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.0 |

## Providers

No providers.

## Modules

No modules.

## Resources

No resources.

## Inputs

No inputs.

## Outputs

No outputs.

<!-- END_TF_DOCS -->

# Changelog

Loading
Loading