Skip to content

Commit 7a484d2

Browse files
authored
feat: add module path ignore functionality, tests, and update deps (#178)
* feat: add kms module with nested directories * feat: add module path ignore functionality and tests * build(deps): update dependencies to latest versions
1 parent 5e3162d commit 7a484d2

File tree

24 files changed

+953
-425
lines changed

24 files changed

+953
-425
lines changed

.github/scripts/changelog.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import { execFileSync } from 'node:child_process';
2020
import https from 'node:https';
2121

22-
const OPENAI_MODEL = 'gpt-4-turbo-2024-04-09';
22+
const OPENAI_MODEL = 'gpt-4o-mini-2024-07-18';
2323
const PROMPT = `
2424
You're the head of developer relations at a SaaS. Write a concise, professional, and fun changelog, prioritizing important changes.
2525
@@ -33,7 +33,7 @@ For each commit, use this format:
3333
- **Bold 3-5 word Summary** {optional related GitHub emoji}: Continuation with 1-3 sentence description. @author (optional #PR)
3434
- Sub-bullets for key details (include only if necessary)
3535
36-
Place PR/issue numbers matching the exact pattern #\d+ (e.g., #123) at the end of the section in parentheses.
36+
Place PR/issue numbers matching the exact pattern #\\d+ (e.g., #123) at the end of the section in parentheses.
3737
3838
Do not use commit hashes as PR numbers.
3939

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ jobs:
4949
disable-wiki: false
5050
wiki-sidebar-changelog-max: 10
5151
delete-legacy-tags: false # Note: We don't want to delete tags in this repository
52-
module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
52+
terraform-docs-version: v0.19.0
53+
module-path-ignore: tf-modules/kms/examples/complete
54+
module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**,examples/**
5355
module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
5456
use-ssh-source-format: true
5557

README.md

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,24 @@ documentation.</b></sup>
1717
[4]: https://github.com/techpivot/terraform-module-releaser/actions/workflows/codeql-analysis.yml
1818
[5]: https://sonarcloud.io/summary/new_code?id=terraform-module-releaser
1919

20-
Simplify the management of Terraform modules in your monorepo with this **GitHub Action**, designed to automate
21-
module-specific versioning and releases. By streamlining the Terraform module release process, this action allows you to
22-
manage multiple modules in a single repository while still maintaining independence and flexibility. Additionally, it
23-
generates a beautifully crafted wiki for each module, complete with readme information, usage examples, Terraform-docs
24-
details, and a full changelog.
25-
26-
## Key Features
27-
28-
- **Efficient Module Tagging**: Module tags are specifically designed to only include the current Terraform module
29-
directory (and nothing else), thereby dramatically decreasing the size and improving Terraform performance.
30-
- **Automated Release Management**: Identifies Terraform modules affected by changes in a pull request and determines
31-
the necessary release type (major, minor, or patch) based on commit messages.
32-
- **Versioning and Tagging**: Calculates the next version tag for each module and commits, tags, and pushes new versions
33-
for each module individually.
34-
- **Release Notes and Comments**: Generates a pull request comment summarizing module changes and release types, and
35-
creates a GitHub release for each module with a dynamically generated description.
36-
- **Wiki Integration**: Updates the wiki with new release information, including:
37-
- README.md information for each module
38-
- Beautifully crafted module usage examples
39-
- `terraform-docs` details for each module
40-
- Full changelog for each module
41-
- **Deletes Synced**: Automatically removes tags from deleted Terraform modules, keeping your repository organized and
42-
up-to-date.
43-
- **Flexible Configuration**: Offers advanced input options for customization, allowing you to tailor the action to your
44-
specific needs.
20+
Simplify the management of Terraform modules in your monorepo with this **GitHub Action**. It automates module-specific
21+
versioning and releases by creating proper Git tags and GitHub releases based on your commit messages. Each module
22+
maintains independence while living in the same repository, with proper isolation for clean dependency management.
23+
Additionally, the action generates a beautifully crafted wiki for each module, complete with readme information, usage
24+
examples, Terraform-docs details, and a full changelog.
25+
26+
## 🚀 Features
27+
28+
- **Efficient Module Tagging** – Only includes module directory content, dramatically improving Terraform performance.
29+
- **Smart Versioning** – Automatically determines release types (major, minor, patch) based on commit messages.
30+
- **Comprehensive Wiki** – Generates beautiful documentation with usage examples, terraform-docs output, and full
31+
changelogs.
32+
- **Release Automation** – Creates GitHub releases, pull request comments, and version tags with minimal effort.
33+
- **Self-Maintaining** – Automatically removes tags from deleted modules, keeping your repository clean and organized.
34+
- **100% GitHub Native** – No external dependencies or services required for modules or operation, everything stays
35+
within your GitHub ecosystem.
36+
- **Zero Configuration** – Works out-of-the-box with sensible defaults for immediate productivity.
37+
- **Flexible & Extensible** – Customizable settings to precisely match your team's specific workflow requirements.
4538

4639
## Demo
4740

@@ -180,10 +173,38 @@ configuring the following optional input parameters as needed.
180173
| `disable-wiki` | Whether to disable wiki generation for Terraform modules | `false` |
181174
| `wiki-sidebar-changelog-max` | An integer that specifies how many changelog entries are displayed in the sidebar per module | `5` |
182175
| `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` |
176+
| `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) |
183177
| `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/**` |
184178
| `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/**` |
185179
| `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` |
186180

181+
### Understanding the filtering options
182+
183+
- **`module-path-ignore`**: Completely ignores specified module paths. Any module whose path matches any pattern in this
184+
list will not be processed at all by the action. This is useful for:
185+
186+
- Excluding example modules (e.g., `**/examples/**`)
187+
- Skipping test modules (e.g., `**/test/**`)
188+
- Ignoring documentation-focused modules (e.g., `**/docs/**`)
189+
- Excluding entire directories or paths that contain Terraform files but shouldn't be versioned as modules
190+
191+
Example:
192+
193+
```yaml
194+
module-path-ignore: "**/examples/**,**/test/**,root-modules"
195+
```
196+
197+
- **`module-change-exclude-patterns`**: These patterns determine which file changes are _ignored_ when checking if a
198+
module needs a new release. For example, changes to documentation, examples, or workflow files typically don't require
199+
a new module release.
200+
- **`module-asset-exclude-patterns`**: When building a release asset for a module, these patterns determine which files
201+
are _excluded_ from the asset. This helps reduce the asset size by omitting test files, examples, documentation, etc.
202+
203+
All pattern matching is implemented using [minimatch](https://github.com/isaacs/minimatch), which supports glob patterns
204+
similar to those used in `.gitignore` files. For more details on the pattern matching implementation, see our
205+
[source code](https://github.com/techpivot/terraform-module-releaser/blob/main/src/utils/file.ts) or visit the
206+
[minimatch documentation](https://github.com/isaacs/minimatch).
207+
187208
### Example Usage with Inputs
188209

189210
```yml
@@ -219,6 +240,7 @@ jobs:
219240
module-change-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
220241
module-asset-exclude-patterns: .gitignore,*.md,*.tftest.hcl,tests/**
221242
use-ssh-source-format: false
243+
module-path-ignore: path/to/ignore1,path/to/ignore2
222244
```
223245

224246
## Outputs

__mocks__/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const defaultConfig: Config = {
2121
disableWiki: false,
2222
wikiSidebarChangelogMax: 10,
2323
disableBranding: false,
24+
modulePathIgnore: ['tf-modules/kms/examples/complete'],
2425
moduleChangeExcludePatterns: ['.gitignore', '*.md'],
2526
moduleAssetExcludePatterns: ['tests/**', 'examples/**'],
2627
githubToken: 'ghp_test_token_2c6912E7710c838347Ae178B4',
@@ -40,6 +41,7 @@ const validConfigKeys = [
4041
'disableWiki',
4142
'wikiSidebarChangelogMax',
4243
'disableBranding',
44+
'modulePathIgnore',
4345
'moduleChangeExcludePatterns',
4446
'moduleAssetExcludePatterns',
4547
'githubToken',

__tests__/config.test.ts

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import { clearConfigForTesting, config, getConfig } from '@/config';
2-
import { booleanConfigKeys, booleanInputs, requiredInputs, stubInputEnv } from '@/tests/helpers/inputs';
2+
import {
3+
arrayInputs,
4+
booleanInputs,
5+
inputToConfigKey,
6+
inputToConfigKeyMap,
7+
optionalInputs,
8+
requiredInputs,
9+
stringInputs,
10+
stubInputEnv,
11+
} from '@/tests/helpers/inputs';
12+
import type { Config } from '@/types';
313
import { endGroup, getBooleanInput, getInput, info, startGroup } from '@actions/core';
414
import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
515

@@ -26,6 +36,30 @@ describe('config', () => {
2636
});
2737
}
2838

39+
for (const input of optionalInputs) {
40+
it(`should handle optional input "${input}" when not present`, () => {
41+
stubInputEnv({ [input]: null });
42+
// Simply verify it doesn't throw without the specific error object
43+
expect(() => getConfig()).not.toThrow();
44+
45+
// Get the config and check the actual value
46+
const config = getConfig();
47+
// Get the config key using the mapping directly if possible
48+
const configKey = inputToConfigKeyMap[input] || inputToConfigKey(input);
49+
50+
// Type-safe access using the mapping
51+
if (arrayInputs.includes(input)) {
52+
// Cast configKey to keyof Config to ensure type safety
53+
expect(config[configKey as keyof Config]).toEqual([]);
54+
}
55+
if (stringInputs.includes(input)) {
56+
expect(config[configKey as keyof Config]).toEqual('');
57+
}
58+
59+
expect(getInput).toHaveBeenCalled();
60+
});
61+
}
62+
2963
for (const input of booleanInputs) {
3064
it(`should throw error when input "${input}" has an invalid boolean value`, () => {
3165
stubInputEnv({ [input]: 'invalid-boolean' });
@@ -70,8 +104,10 @@ describe('config', () => {
70104

71105
// Check the boolean conversion for each key in booleanInputs
72106
const config = getConfig();
73-
for (const inputKey of booleanConfigKeys) {
74-
expect(config[inputKey]).toBe(booleanValue.toLowerCase() === 'true');
107+
for (const booleanInput of booleanInputs) {
108+
// Get config key from the mapping, which is already typed as keyof Config
109+
const configKey = inputToConfigKeyMap[booleanInput];
110+
expect(config[configKey]).toBe(booleanValue.toLowerCase() === 'true');
75111
}
76112
}
77113
});
@@ -117,11 +153,11 @@ describe('config', () => {
117153
expect(config.githubToken).toBe('ghp_test_token_2c6912E7710c838347Ae178B4');
118154
expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md']);
119155
expect(config.moduleAssetExcludePatterns).toEqual(['tests/**', 'examples/**']);
156+
expect(config.modulePathIgnore).toEqual(['tf-modules/kms/examples/complete']);
120157
expect(config.useSSHSourceFormat).toBe(false);
121158
expect(startGroup).toHaveBeenCalledWith('Initializing Config');
122159
expect(startGroup).toHaveBeenCalledTimes(1);
123160
expect(endGroup).toHaveBeenCalledTimes(1);
124-
expect(info).toHaveBeenCalledTimes(11);
125161
expect(vi.mocked(info).mock.calls).toEqual([
126162
['Major Keywords: MAJOR CHANGE, BREAKING CHANGE, !'],
127163
['Minor Keywords: feat, feature'],
@@ -131,6 +167,7 @@ describe('config', () => {
131167
['Delete Legacy Tags: false'],
132168
['Disable Wiki: false'],
133169
['Wiki Sidebar Changelog Max: 10'],
170+
['Module Paths to Ignore: tf-modules/kms/examples/complete'],
134171
['Module Change Exclude Patterns: .gitignore, *.md'],
135172
['Module Asset Exclude Patterns: tests/**, examples/**'],
136173
['Use SSH Source Format: false'],
@@ -180,5 +217,11 @@ describe('config', () => {
180217
expect(config.majorKeywords).toEqual(['BREAKING CHANGE', '!']);
181218
expect(config.moduleChangeExcludePatterns).toEqual(['.gitignore', '*.md']);
182219
});
220+
221+
it('should handle empty modulePathIgnore', () => {
222+
stubInputEnv({ 'module-path-ignore': '' });
223+
const config = getConfig();
224+
expect(config.modulePathIgnore).toEqual([]);
225+
});
183226
});
184227
});

__tests__/fixtures/Home.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ providing an overview of their functionality and the latest versions.
77

88
| Module Name | Latest Version |
99
| -- | -- |
10+
| [kms](/techpivot/terraform-module-releaser/wiki/kms) | null |
11+
| [kms/examples/complete](/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete) | null |
1012
| [s3-bucket-object](/techpivot/terraform-module-releaser/wiki/s3‒bucket‒object) | null |
1113
| [vpc-endpoint](/techpivot/terraform-module-releaser/wiki/vpc‒endpoint) | v1.0.0 |
1214

__tests__/fixtures/_Sidebar.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@
33
## Terraform Modules
44

55
<ul>
6+
<li>
7+
<details>
8+
<summary><a href="/techpivot/terraform-module-releaser/wiki/kms"><b>kms</b></a></summary>
9+
<ul>
10+
<li><a href="/techpivot/terraform-module-releaser/wiki/kms#usage">Usage</a></li>
11+
<li><a href="/techpivot/terraform-module-releaser/wiki/kms#attributes">Attributes</a></li>
12+
<li><a href="/techpivot/terraform-module-releaser/wiki/kms#changelog">Changelog</a></li>
13+
</ul>
14+
</details>
15+
</li>
16+
<li>
17+
<details>
18+
<summary><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete"><b>kms/examples/complete</b></a></summary>
19+
<ul>
20+
<li><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete#usage">Usage</a></li>
21+
<li><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete#attributes">Attributes</a></li>
22+
<li><a href="/techpivot/terraform-module-releaser/wiki/kms∕examples∕complete#changelog">Changelog</a></li>
23+
</ul>
24+
</details>
25+
</li>
626
<li>
727
<details>
828
<summary><a href="/techpivot/terraform-module-releaser/wiki/s3‒bucket‒object"><b>s3-bucket-object</b></a></summary>

__tests__/fixtures/kms.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Usage
2+
3+
To use this module in your Terraform, refer to the below module example:
4+
5+
```hcl
6+
module "kms" {
7+
source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=null"
8+
9+
# See inputs below for additional required parameters
10+
}
11+
```
12+
13+
# Attributes
14+
15+
<!-- BEGIN_TF_DOCS -->
16+
## Requirements
17+
18+
| Name | Version |
19+
|------|---------|
20+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12 |
21+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.0 |
22+
23+
## Providers
24+
25+
No providers.
26+
27+
## Modules
28+
29+
No modules.
30+
31+
## Resources
32+
33+
No resources.
34+
35+
## Inputs
36+
37+
No inputs.
38+
39+
## Outputs
40+
41+
No outputs.
42+
43+
<!-- END_TF_DOCS -->
44+
45+
# Changelog
46+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Usage
2+
3+
To use this module in your Terraform, refer to the below module example:
4+
5+
```hcl
6+
module "kms_examples_complete" {
7+
source = "git::https://github.com/techpivot/terraform-module-releaser.git?ref=null"
8+
9+
# See inputs below for additional required parameters
10+
}
11+
```
12+
13+
# Attributes
14+
15+
<!-- BEGIN_TF_DOCS -->
16+
## Requirements
17+
18+
| Name | Version |
19+
|------|---------|
20+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.12 |
21+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 3.0 |
22+
23+
## Providers
24+
25+
No providers.
26+
27+
## Modules
28+
29+
No modules.
30+
31+
## Resources
32+
33+
No resources.
34+
35+
## Inputs
36+
37+
No inputs.
38+
39+
## Outputs
40+
41+
No outputs.
42+
43+
<!-- END_TF_DOCS -->
44+
45+
# Changelog
46+

0 commit comments

Comments
 (0)