diff --git a/.github/actions/build-website/action.yml b/.github/actions/build-website/action.yml index c95ae1a79..f8d5a3207 100644 --- a/.github/actions/build-website/action.yml +++ b/.github/actions/build-website/action.yml @@ -59,6 +59,13 @@ runs: make init pip install -r scripts/docs-collator/requirements.txt + - name: "Install terraform-docs" + uses: jaxxstorm/action-install-gh-release@v1.12.0 + with: + repo: terraform-docs/terraform-docs + tag: v0.18.0 + cache: enable + - name: "Render Documentation for Terraform Components" shell: bash run: | diff --git a/.gitignore b/.gitignore index 619ab5c0d..12f1a3c6a 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,12 @@ /resources /themes /tmp +/docs/generated/components/library +/docs/generated/modules/library +/docs/generated/github-actions/library +/docs/components/library +/docs/modules/library +/docs/github-actions/library /static/css /static/js diff --git a/docs/generated/components/index.mdx b/docs/components/index.mdx similarity index 100% rename from docs/generated/components/index.mdx rename to docs/components/index.mdx diff --git a/docs/generated/components/library/aws/_category_.json b/docs/generated/components/library/aws/_category_.json deleted file mode 100644 index 6936f441c..000000000 --- a/docs/generated/components/library/aws/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "aws", - "position": 100, - "collapsible": true, - "collapsed": false, - "link": { - "type": "generated-index", - "description": "aws" - } -} diff --git a/docs/generated/components/library/aws/account/README.mdx b/docs/generated/components/library/aws/account/README.mdx deleted file mode 100644 index 63876a9d0..000000000 --- a/docs/generated/components/library/aws/account/README.mdx +++ /dev/null @@ -1,498 +0,0 @@ ---- -title: account -sidebar_label: account -sidebar_class_name: command -custom_edit_url: https://github.com/cloudposse/terraform-aws-components/blob/master/modules/account/README.md -tags: [terraform, aws, account] ---- - -# Component: `account` - -This component is responsible for provisioning the full account hierarchy along with Organizational Units (OUs). It -includes the ability to associate Service Control Policies (SCPs) to the Organization, each Organizational Unit and -account. - -:::info Part of a -[cold start](https://docs.cloudposse.com/reference-architecture/how-to-guides/implementation/enterprise/implement-aws-cold-start) -so it has to be initially run with `SuperAdmin` role. - -::: - -In addition, it enables -[AWS IAM Access Analyzer](https://docs.aws.amazon.com/IAM/latest/UserGuide/what-is-access-analyzer.html), which helps -you identify the resources in your organization and accounts, such as Amazon S3 buckets or IAM roles, that are shared -with an external entity. This lets you identify unintended access to your resources and data, which is a security risk. -Access Analyzer identifies resources that are shared with external principals by using logic-based reasoning to analyze -the resource-based policies in your AWS environment. For each instance of a resource that is shared outside of your -account, Access Analyzer generates a finding. Findings include information about the access and the external principal -that it is granted to. You can review findings to determine whether the access is intended and safe, or the access is -unintended and a security risk. - -## Usage - -**Stack Level**: Global - -**IMPORTANT**: Account Name building blocks (such as tenant, stage, environment) must not contain dashes. Doing so will -lead to unpredictable resource names as a `-` is the default delimiter. Additionally, account names must be lower case -alpha-numeric with no special characters. For example: - -| Key | Value | Correctness | -| ---------------- | --------------- | ----------- | -| **Tenant** | foo | ✅ | -| **Tenant** | foo-bar | ❌ | -| **Environment** | use1 | ✅ | -| **Environment** | us-east-1 | ❌ | -| **Account Name** | `core-identity` | ✅ | - -Here is an example snippet for how to use this component. Include this snippet in the stack configuration for the -management account (typically `root`) in the management tenant/OU (usually something like `mgmt` or `core`) in the -global region (`gbl`). You can insert the content directly, or create a `stacks/catalog/account.yaml` file and import it -from there. - -```yaml -components: - terraform: - account: - settings: - spacelift: - workspace_enabled: false - backend: - s3: - role_arn: null - vars: - enabled: true - account_email_format: aws+%s@example.net - account_iam_user_access_to_billing: ALLOW - organization_enabled: true - aws_service_access_principals: - - cloudtrail.amazonaws.com - - guardduty.amazonaws.com - - ipam.amazonaws.com - - ram.amazonaws.com - - securityhub.amazonaws.com - - servicequotas.amazonaws.com - - sso.amazonaws.com - - securityhub.amazonaws.com - - auditmanager.amazonaws.com - enabled_policy_types: - - SERVICE_CONTROL_POLICY - - TAG_POLICY - organization_config: - root_account: - name: core-root - stage: root - tenant: core - tags: - eks: false - accounts: [] - organization: - service_control_policies: - - DenyEC2InstancesWithoutEncryptionInTransit - organizational_units: - - name: core - accounts: - - name: core-artifacts - tenant: core - stage: artifacts - tags: - eks: false - - name: core-audit - tenant: core - stage: audit - tags: - eks: false - - name: core-auto - tenant: core - stage: auto - tags: - eks: true - - name: core-corp - tenant: core - stage: corp - tags: - eks: true - - name: core-dns - tenant: core - stage: dns - tags: - eks: false - - name: core-identity - tenant: core - stage: identity - tags: - eks: false - - name: core-network - tenant: core - stage: network - tags: - eks: false - - name: core-security - tenant: core - stage: security - tags: - eks: false - service_control_policies: - - DenyLeavingOrganization - - name: plat - accounts: - - name: plat-dev - tenant: plat - stage: dev - tags: - eks: true - - name: plat-sandbox - tenant: plat - stage: sandbox - tags: - eks: true - - name: plat-staging - tenant: plat - stage: staging - tags: - eks: true - - name: plat-prod - tenant: plat - stage: prod - tags: - eks: true - service_control_policies: - - DenyLeavingOrganization - service_control_policies_config_paths: - # These paths specify where to find the service control policies identified by SID in the service_control_policies sections above. - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/cloudwatch-logs-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/deny-all-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/iam-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/kms-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/organization-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/route53-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/s3-policies.yaml" - - "https://raw.githubusercontent.com/cloudposse/terraform-aws-service-control-policies/0.12.0/catalog/ec2-policies.yaml" -``` - -## First Time Organization Setup - -Your AWS Organization is managed by the `account` component, along with accounts and organizational units. - -However, because the AWS defaults for an Organization and its accounts are not exactly what we want, and there is no way -to change them via Terraform, we have to first provision the AWS Organization, then take some steps on the AWS console, -and then we can provision the rest. - -### Use AWS Console to create and set up the Organization - -Unfortunately, there are some tasks that need to be done via the console. Log into the AWS Console with the root (not -SuperAdmin) credentials you have saved in 1Password. - -#### Request an increase in the maximum number of accounts allowed - -:::caution Make sure your support plan for the _root_ account was upgraded to the "Business" level (or Higher). This is -necessary to expedite the quota increase requests, which could take several days on a basic support plan. Without it, -AWS support will claim that since we’re not currently utilizing any of the resources, so they do not want to approve the -requests. AWS support is not aware of your other organization. If AWS still gives you problems, please escalate to your -AWS TAM. See [AWS](https://docs.cloudposse.com/reference-architecture/reference/aws). - -::: - -1. From the region list, select "US East (N. Virginia) us-east-1". - -2. From the account dropdown menu, select "My Service Quotas". - -3. From the Sidebar, select "AWS Services". - -4. Type "org" in the search field under "AWS services" - -5. Click on "AWS Organizations" in the "Service" list - -6. Click on "Default maximum number of accounts", which should take you to a new view - -7. Click on "Request quota increase" on the right side of the view, which should pop us a request form - -8. At the bottom of the form, under "Change quota value", enter the number you decided on in the previous step (probably - "20") and click "Request" - -#### (Optional) Create templates to request other quota increases - -New accounts start with a low limit on the number of instances you can create. However, as you add accounts, and use -more instances, the numbers automatically adjust up. So you may or may not want to create a template to generate -automatic quota increase requests, depending on how many instances per account you expect to want to provision right -away. - -Create a -[Quota request template](https://docs.aws.amazon.com/servicequotas/latest/userguide/organization-templates.html) for the -organization. From the Sidebar, click "Quota request template" - -Add each EC2 quota increase request you want to make: - -1. Click "Add Quota" on the right side of the view - -2. Under "Region", select your default region (repeat with the backup region if you are using one) - -3. Under "Service", type "EC2" and select "Amazon Elastic Compute Cloud (Amazon EC2)" - -4. Under "Quota", find the quota you want to increase. The likely candidates are: - -5. type "stand" and select "Running On-Demand Standard (A, C, D, H, I, M, R, T, Z) Instances" - -6. type "stand" and select "All Standard (A, C, D, H, I, M, R, T, Z) Spot Instance Request" - -7. type "g i" and select "Running On-Demand G Instances" - -8. type "all g" and select "All G Spot Instance Requests" - -9. Under "Desired quota value" enter your desired default quota - -10. Click "Add" - -After you have added all the templates, click "Enable" on the Quota request template screen to enable the templates. - -#### Enable resource sharing with AWS Organization - -[AWS Resource Access Manager (RAM)](https://docs.aws.amazon.com/ram/latest/userguide/what-is.html) lets you share your -resources with any AWS account or through AWS Organizations. - -
- -If you have multiple AWS accounts, you can create resources centrally and use AWS RAM to share those resources with -other accounts. - -Resource sharing through AWS Organization will be used to share the Transit Gateway deployed in the `network` account -with other accounts to connect their VPCs to the shared Transit Gateway. - -This is a one-time manual step in the AWS Resource Access Manager console. When you share resources within your -organization, AWS RAM does not send invitations to principals. Principals in your organization get access to shared -resources without exchanging invitations. - -To enable resource sharing with AWS Organization via AWS Management Console - -- Open the Settings page of AWS Resource Access Manager console at - [https://console.aws.amazon.com/ram/home#Settings](https://console.aws.amazon.com/ram/home#Settings) - -- Choose "Enable sharing with AWS Organizations" - -To enable resource sharing with AWS Organization via AWS CLI - -``` - √ . [xamp-SuperAdmin] (HOST) infra ⨠ aws ram enable-sharing-with-aws-organization -{ - "returnValue": true -} -``` - -For more information, see: - -- [https://docs.aws.amazon.com/ram/latest/userguide/what-is.html](https://docs.aws.amazon.com/ram/latest/userguide/what-is.html) - -- [https://docs.aws.amazon.com/ram/latest/userguide/getting-started-sharing.html](https://docs.aws.amazon.com/ram/latest/userguide/getting-started-sharing.html) - -- [https://docs.aws.amazon.com/organizations/latest/userguide/services-that-can-integrate-ram.html](https://docs.aws.amazon.com/organizations/latest/userguide/services-that-can-integrate-ram.html) - -### Import the organization into Terraform using the `account` component - -After we are done with the above ClickOps and the Service Quota Increase for maximum number of accounts has been -granted, we can then do the rest via Terraform. - -In the Geodesic shell, as SuperAdmin, execute the following command to get the AWS Organization ID that will be used to -import the organization: - -``` -aws organizations describe-organization -``` - -From the output, identify the _organization-id_: - -``` -{ - "Organization": { - "Id": "o-7qcakq6zxw", - "Arn": "arn:aws:organizations:: - ... -``` - -Using the example above, the _organization-id_ is o-7qcakq6zxw. - -In the Geodesic shell, as SuperAdmin, execute the following command to import the AWS Organization, changing the stack -name `core-gbl-root` if needed, to reflect the stack where the organization management account is defined, and changing -the last argument to reflect the _organization-id_ from the output of the previous command. - -``` -atmos terraform import account --stack core-gbl-root 'aws_organizations_organization.this[0]' 'o-7qcakq6zxw' -``` - -### Provision AWS OUs and Accounts using the `account` component - -AWS accounts and organizational units are generated dynamically by the `terraform/account` component using the -configuration in the `gbl-root` stack. - -:::info _**Special note:**_ \*\*\*\* In the rare case where you will need to be enabling non-default AWS Regions, -temporarily comment out the `DenyRootAccountAccess` service control policy setting in `gbl-root.yaml`. You will restore -it later, after enabling the optional Regions. See related: -[Decide on Opting Into Non-default Regions](https://docs.cloudposse.com/reference-architecture/design-decisions/cold-start/decide-on-opting-into-non-default-regions) - -::: - -:::caution **You must wait until your quota increase request has been granted.** If you try to create the accounts -before the quota increase is granted, you can expect to see failures like `ACCOUNT_NUMBER_LIMIT_EXCEEDED`. - -::: - -In the Geodesic shell, execute the following commands to provision AWS Organizational Units and AWS accounts: - -``` -atmos terraform apply account --stack gbl-root -``` - -Review the Terraform plan, _**ensure that no new organization will be created**_ (look for -`aws_organizations_organization.this[0]`), type "yes" to approve and apply. This creates the AWS organizational units -and AWS accounts. - -### Configure root account credentials for each account - -Note: unless you need to enable non-default AWS regions (see next step), this step can be done later or in parallel with -other steps, for example while waiting for Terraform to create resources. - -**For** _**each**_ **new account:** - -1. Perform a password reset by attempting to [log in to the AWS console](https://signin.aws.amazon.com/signin) as a - "root user", using that account's email address, and then clicking the "Forgot password?" link. You will receive a - password reset link via email, which should be forwarded to the shared Slack channel for automated messages. Click - the link and enter a new password. (Use 1Password or [Random.org](https://www.random.org/passwords) to create a - password 26-38 characters long, including at least 3 of each class of character: lower case, uppercase, digit, and - symbol. You may need to manually combine or add to the generated password to ensure 3 symbols and digits are - present.) Save the email address and generated password as web login credentials in 1Password. While you are at it, - save the account number in a separate field. - -2. Log in using the new password, choose "My Security Credentials" from the account dropdown menu and set up - Multi-Factor Authentication (MFA) to use a Virutal MFA device. Save the MFA TOTP key in 1Password by using - 1Password's TOTP field and built-in screen scanner. Also, save the Virutal MFA ARN (sometimes shown as "serial - number"). - -3. While logged in, enable optional regions as described in the next step, if needed. - -4. (Optional, but highly recommended): [Unsubscribe](https://pages.awscloud.com/communication-preferences.html) the - account's email address from all marketing emails. - -### (Optional) Enable regions - -Most AWS regions are enabled by default. If you are using a region that is not enabled by default (such as Middle -East/Bahrain), you need to take extra steps. - -1. While logged in using root credentials (see the previous step), in the account dropdown menu, select "My Account" to - get to the [Billing home page](https://console.aws.amazon.com/billing/home?#/account). - -2. In the "AWS Regions" section, enable the regions you want to enable. - -3. Go to the IAM [account settings page](https://console.aws.amazon.com/iam/home?#/account_settings) and edit the STS - Global endpoint to create session tokens valid in all AWS regions. - -You will need to wait a few minutes for the regions to be enabled before you can proceed to the next step. Until they -are enabled, you may get what look like AWS authentication or permissions errors. - -After enabling the regions in all accounts, re-enable the `DenyRootAccountAccess` service control policy setting in -`gbl-root.yaml` and rerun - -``` -atmos terraform apply account --stack gbl-root -``` - - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0.0 | -| [aws](#requirement\_aws) | >= 4.9.0 | - -## Providers - -| Name | Version | -|------|---------| -| [aws](#provider\_aws) | >= 4.9.0 | - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [accounts\_service\_control\_policies](#module\_accounts\_service\_control\_policies) | cloudposse/service-control-policies/aws | 0.9.2 | -| [organization\_service\_control\_policies](#module\_organization\_service\_control\_policies) | cloudposse/service-control-policies/aws | 0.9.2 | -| [organizational\_units\_service\_control\_policies](#module\_organizational\_units\_service\_control\_policies) | cloudposse/service-control-policies/aws | 0.9.2 | -| [service\_control\_policy\_statements\_yaml\_config](#module\_service\_control\_policy\_statements\_yaml\_config) | cloudposse/config/yaml | 1.0.2 | -| [this](#module\_this) | cloudposse/label/null | 0.25.0 | - -## Resources - -| Name | Type | -|------|------| -| [aws_organizations_account.organization_accounts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_account) | resource | -| [aws_organizations_account.organizational_units_accounts](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_account) | resource | -| [aws_organizations_organization.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_organization) | resource | -| [aws_organizations_organizational_unit.child](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_organizational_unit) | resource | -| [aws_organizations_organizational_unit.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/organizations_organizational_unit) | resource | -| [aws_organizations_organization.existing](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/organizations_organization) | data source | - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [account\_email\_format](#input\_account\_email\_format) | Email address format for the accounts (e.g. `aws+%s@example.com`) | `string` | n/a | yes | -| [account\_iam\_user\_access\_to\_billing](#input\_account\_iam\_user\_access\_to\_billing) | If set to `ALLOW`, the new account enables IAM users to access account billing information if they have the required permissions. If set to `DENY`, then only the root user of the new account can access account billing information | `string` | `"DENY"` | no | -| [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no | -| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | -| [aws\_service\_access\_principals](#input\_aws\_service\_access\_principals) | List of AWS service principal names for which you want to enable integration with your organization. This is typically in the form of a URL, such as service-abbreviation.amazonaws.com. Organization must have `feature_set` set to ALL. For additional information, see the [AWS Organizations User Guide](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_integrate_services.html) | `list(string)` | n/a | yes | - -| [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | -| [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | -| [enabled\_policy\_types](#input\_enabled\_policy\_types) | List of Organizations policy types to enable in the Organization Root. Organization must have feature\_set set to ALL. For additional information about valid policy types (e.g. SERVICE\_CONTROL\_POLICY and TAG\_POLICY), see the [AWS Organizations API Reference](https://docs.aws.amazon.com/organizations/latest/APIReference/API_EnablePolicyType.html) | `list(string)` | n/a | yes | -| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | -| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | -| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | -| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | -| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | -| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` |
[
"default"
]
| no | -| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | -| [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | -| [organization\_config](#input\_organization\_config) | Organization, Organizational Units and Accounts configuration | `any` | n/a | yes | -| [organization\_enabled](#input\_organization\_enabled) | A boolean flag indicating whether to create an Organization or use the existing one | `bool` | `true` | no | -| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | -| [region](#input\_region) | AWS Region | `string` | n/a | yes | -| [service\_control\_policies\_config\_paths](#input\_service\_control\_policies\_config\_paths) | List of paths to Service Control Policy configurations | `list(string)` | n/a | yes | -| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | -| [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no | -| [tenant](#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | - -## Outputs - -| Name | Description | -|------|-------------| -| [account\_arns](#output\_account\_arns) | List of account ARNs (excluding root account) | -| [account\_ids](#output\_account\_ids) | List of account IDs (excluding root account) | -| [account\_info\_map](#output\_account\_info\_map) | Map of account names to
eks: boolean, account hosts at least one EKS cluster
id: account id (number)
stage: (optional) the account "stage"
tenant: (optional) the account "tenant" | -| [account\_names\_account\_arns](#output\_account\_names\_account\_arns) | Map of account names to account ARNs (excluding root account) | -| [account\_names\_account\_ids](#output\_account\_names\_account\_ids) | Map of account names to account IDs (excluding root account) | -| [account\_names\_account\_scp\_arns](#output\_account\_names\_account\_scp\_arns) | Map of account names to SCP ARNs for accounts with SCPs | -| [account\_names\_account\_scp\_ids](#output\_account\_names\_account\_scp\_ids) | Map of account names to SCP IDs for accounts with SCPs | -| [eks\_accounts](#output\_eks\_accounts) | List of EKS accounts | -| [non\_eks\_accounts](#output\_non\_eks\_accounts) | List of non EKS accounts | -| [organization\_arn](#output\_organization\_arn) | Organization ARN | -| [organization\_id](#output\_organization\_id) | Organization ID | -| [organization\_master\_account\_arn](#output\_organization\_master\_account\_arn) | Organization master account ARN | -| [organization\_master\_account\_email](#output\_organization\_master\_account\_email) | Organization master account email | -| [organization\_master\_account\_id](#output\_organization\_master\_account\_id) | Organization master account ID | -| [organization\_scp\_arn](#output\_organization\_scp\_arn) | Organization Service Control Policy ARN | -| [organization\_scp\_id](#output\_organization\_scp\_id) | Organization Service Control Policy ID | -| [organizational\_unit\_arns](#output\_organizational\_unit\_arns) | List of Organizational Unit ARNs | -| [organizational\_unit\_ids](#output\_organizational\_unit\_ids) | List of Organizational Unit IDs | -| [organizational\_unit\_names\_organizational\_unit\_arns](#output\_organizational\_unit\_names\_organizational\_unit\_arns) | Map of Organizational Unit names to Organizational Unit ARNs | -| [organizational\_unit\_names\_organizational\_unit\_ids](#output\_organizational\_unit\_names\_organizational\_unit\_ids) | Map of Organizational Unit names to Organizational Unit IDs | -| [organizational\_unit\_names\_organizational\_unit\_scp\_arns](#output\_organizational\_unit\_names\_organizational\_unit\_scp\_arns) | Map of OU names to SCP ARNs | -| [organizational\_unit\_names\_organizational\_unit\_scp\_ids](#output\_organizational\_unit\_names\_organizational\_unit\_scp\_ids) | Map of OU names to SCP IDs | - - - -## References - -- [cloudposse/terraform-aws-components](https://github.com/cloudposse/terraform-aws-components/tree/main/modules/account) - - Cloud Posse's upstream component - - - - diff --git a/docs/generated/github-actions/library/actions/_category_.json b/docs/generated/github-actions/library/actions/_category_.json deleted file mode 100644 index b318ed08a..000000000 --- a/docs/generated/github-actions/library/actions/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "actions", - "collapsible": true, - "collapsed": false, - "className": "command", - "link": { - "type": "generated-index", - "title": "GitHub Actions" - } -} diff --git a/docs/generated/github-actions/library/actions/docker-build-push/README.mdx b/docs/generated/github-actions/library/actions/docker-build-push/README.mdx deleted file mode 100644 index 2c058e0cd..000000000 --- a/docs/generated/github-actions/library/actions/docker-build-push/README.mdx +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: docker-build-push -sidebar_label: docker-build-push -sidebar_class_name: command -description: |- - Build Docker image and push it -tags: - - github-action - -custom_edit_url: https://github.com/cloudposse/github-action-docker-build-push/blob/main/README.yaml ---- - -# GitHub Action: `docker-build-push` -Build Docker image and push it - - - - -## Introduction - -Build Docker image and push it. - - - -## Usage - -```yaml - name: github-action-docker-build-push - on: - push: - branches: [ master ] - - jobs: - context: - runs-on: ubuntu-latest - steps: - - name: github-action-docker-build-push - uses: actions/checkout@v4 - - - name: github-action-docker-build-push - id: build - uses: cloudposse/github-action-docker-build-push@main - with: - registry: registry.hub.docker.com - organization: "${{ github.event.repository.owner.login }}" - repository: "${{ github.event.repository.name }}" - login: "${{ secrets.DOCKERHUB_USERNAME }}" - password: "${{ secrets.DOCKERHUB_PASSWORD }}" - platforms: linux/amd64,linux/arm64 - - outputs: - image: ${{ steps.build.outputs.image }} - tag: ${{ steps.build.outputs.tag }} -``` -> [!TIP] -> If omitted, `cache-from` and `cache-to` will default to `gha`. -> In an AWS environment, we recommend using [ECR as a remote cache](https://aws.amazon.com/blogs/containers/announcing-remote-cache-support-in-amazon-ecr-for-buildkit-clients/). - -```diff - - name: github-action-docker-build-push - id: build - uses: cloudposse/github-action-docker-build-push@main - with: - registry: registry.hub.docker.com - organization: "${{ github.event.repository.owner.login }}" - repository: "${{ github.event.repository.name }}" -+ cache-from: "type=registry,ref=registry.hub.docker.com/${{ github.event.repository.owner.login }}/${{ github.event.repository.name }}:cache" -+ cache-to: "mode=max,image-manifest=true,oci-mediatypes=true,type=registry,ref=registry.hub.docker.com/${{ github.event.repository.owner.login }}/${{ github.event.repository.name }}:cache" -``` - - - - - - - - -## Inputs - -| Name | Description | Default | Required | -|------|-------------|---------|----------| -| build-args | List of build-time variables | N/A | false | -| cache-from | List of external cache sources for buildx (e.g., user/app:cache, type=local,src=path/to/dir) | type=gha | false | -| cache-to | List of cache export destinations for buildx (e.g., user/app:cache, type=local,dest=path/to/dir) | type=gha,mode=max | false | -| docker-metadata-pr-head-sha | Set to `true` to tag images with the PR HEAD SHA instead of the merge commit SHA within pull requests. | false | false | -| file | Dockerfile name | Dockerfile | false | -| login | Docker login | | false | -| no-cache | Send the --no-cache flag to the docker build process | false | false | -| organization | Organization | N/A | true | -| password | Docker password | | false | -| platforms | List of target platforms for build (e.g. linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,etc) | linux/amd64 | false | -| provenance | Generate provenance attestation for the build | N/A | false | -| registry | Docker registry | N/A | true | -| repository | Repository | N/A | true | -| secret-files | List of secret files to expose to the build (e.g., key=filename, MY\_SECRET=./secret.txt) | N/A | false | -| secrets | List of secrets to expose to the build (e.g., key=string, GIT\_AUTH\_TOKEN=mytoken) | N/A | false | -| ssh | List of SSH agent socket or keys to expose to the build | N/A | false | -| tags | List of tags (supports https://github.com/docker/metadata-action#tags-input) | N/A | false | -| target | Sets the target stage to build | | false | -| workdir | Working directory | ./ | false | - - -## Outputs - -| Name | Description | -|------|-------------| -| image | Docker image name | -| metadata | Docker image metadata | -| tag | Docker image tag | - - diff --git a/docs/generated/modules/library/aws/_category_.json b/docs/generated/modules/library/aws/_category_.json deleted file mode 100644 index 51a02423f..000000000 --- a/docs/generated/modules/library/aws/_category_.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "label": "aws", - "collapsible": true, - "collapsed": true, - "className": "command", - "link": { - "type": "generated-index", - "title": "Provider: aws" - } -} diff --git a/docs/generated/modules/library/aws/dms/README.mdx b/docs/generated/modules/library/aws/dms/README.mdx deleted file mode 100644 index 457969c9a..000000000 --- a/docs/generated/modules/library/aws/dms/README.mdx +++ /dev/null @@ -1,404 +0,0 @@ ---- -title: dms -sidebar_label: dms -sidebar_class_name: command -description: |- - Terraform modules for provisioning and managing AWS [DMS](https://aws.amazon.com/dms/) resources. - - The following DMS resources are supported: - - - [IAM Roles for DMS](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-iam) - - [DMS Endpoints](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-endpoint) - - [DMS Replication Instances](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-replication-instance) - - [DMS Replication Tasks](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-replication-task) - - [DMS Event Subscriptions](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-event-subscription) - - Refer to [modules](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules) for more details. -custom_edit_url: https://github.com/cloudposse/terraform-aws-dms/blob/main/README.yaml ---- - -# Module: `dms` -Terraform modules for provisioning and managing AWS [DMS](https://aws.amazon.com/dms/) resources. - -The following DMS resources are supported: - - - [IAM Roles for DMS](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-iam) - - [DMS Endpoints](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-endpoint) - - [DMS Replication Instances](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-replication-instance) - - [DMS Replication Tasks](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-replication-task) - - [DMS Event Subscriptions](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules/dms-event-subscription) - -Refer to [modules](https://github.com/cloudposse/terraform-aws-dms/tree/main/modules) for more details. - - - - - - -## Usage - - -For a complete example, see [examples/complete](https://github.com/cloudposse/terraform-aws-dms/tree/main/examples/complete). - -For automated tests of the example using [bats](https://github.com/bats-core/bats-core) and [Terratest](https://github.com/gruntwork-io/terratest) -(which tests and deploys the example on AWS), see [test](https://github.com/cloudposse/terraform-aws-dms/tree/main/test). - - - - -## Examples - - -```hcl - module "dms_iam" { - source = "cloudposse/dms/aws//modules/dms-iam" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - context = module.this.context - } - - module "vpc" { - source = "cloudposse/vpc/aws" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - ipv4_primary_cidr_block = "172.19.0.0/16" - - context = module.this.context - } - - module "subnets" { - source = "cloudposse/dynamic-subnets/aws" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - availability_zones = ["us-east-2a", "us-east-2b"] - vpc_id = local.vpc_id - igw_id = [module.vpc.igw_id] - ipv4_cidr_block = [module.vpc.vpc_cidr_block] - nat_gateway_enabled = false - nat_instance_enabled = false - - context = module.this.context - } - - module "dms_replication_instance" { - source = "cloudposse/dms/aws//modules/dms-replication-instance" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - engine_version = "3.4" - replication_instance_class = "dms.t2.small" - allocated_storage = 50 - apply_immediately = true - auto_minor_version_upgrade = true - allow_major_version_upgrade = false - multi_az = false - publicly_accessible = false - preferred_maintenance_window = "sun:10:30-sun:14:30" - vpc_security_group_ids = [module.vpc.vpc_default_security_group_id, module.aurora_postgres_cluster.security_group_id] - subnet_ids = module.subnets.private_subnet_ids - - context = module.this.context - - depends_on = [ - # The required DMS roles must be present before replication instances can be provisioned - module.dms_iam, - aws_vpc_endpoint.s3 - ] - } - - module "aurora_postgres_cluster" { - source = "cloudposse/rds-cluster/aws" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - engine = "aurora-postgresql" - engine_mode = "provisioned" - engine_version = "13.4" - cluster_family = "aurora-postgresql13" - cluster_size = 1 - admin_user = "admin_user" - admin_password = "admin_password" - db_name = "postgres" - db_port = 5432 - instance_type = "db.t3.medium" - vpc_id = module.vpc.vpc_id - subnets = module.subnets.private_subnet_ids - security_groups = [module.vpc.vpc_default_security_group_id] - deletion_protection = false - autoscaling_enabled = false - storage_encrypted = false - intra_security_group_traffic_enabled = false - skip_final_snapshot = true - enhanced_monitoring_role_enabled = false - iam_database_authentication_enabled = false - - cluster_parameters = [ - { - name = "rds.logical_replication" - value = "1" - apply_method = "pending-reboot" - }, - { - name = "max_replication_slots" - value = "10" - apply_method = "pending-reboot" - }, - { - name = "wal_sender_timeout" - value = "0" - apply_method = "pending-reboot" - }, - { - name = "max_worker_processes" - value = "8" - apply_method = "pending-reboot" - }, - { - name = "max_logical_replication_workers" - value = "10" - apply_method = "pending-reboot" - }, - { - name = "max_parallel_workers" - value = "8" - apply_method = "pending-reboot" - }, - { - name = "max_parallel_workers" - value = "8" - apply_method = "pending-reboot" - } - ] - - context = module.this.context - } - - module "dms_endpoint_aurora_postgres" { - source = "cloudposse/dms/aws//modules/dms-endpoint" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - endpoint_type = "source" - engine_name = "aurora-postgresql" - server_name = module.aurora_postgres_cluster.reader_endpoint - database_name = "postgres" - port = 5432 - username = "admin_user" - password = "admin_password" - extra_connection_attributes = "" - secrets_manager_access_role_arn = null - secrets_manager_arn = null - ssl_mode = "none" - - attributes = ["source"] - context = module.this.context - } - - resource "aws_vpc_endpoint" "s3" { - vpc_endpoint_type = "Gateway" - vpc_id = module.vpc.vpc_id - service_name = "com.amazonaws.${var.region}.s3" - route_table_ids = module.subnets.private_route_table_ids - - tags = module.this.tags - } - - module "s3_bucket" { - source = "cloudposse/s3-bucket/aws" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - acl = "private" - versioning_enabled = false - allow_encrypted_uploads_only = false - allow_ssl_requests_only = false - force_destroy = true - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - restrict_public_buckets = true - - context = module.this.context - } - - module "dms_endpoint_s3_bucket" { - source = "cloudposse/dms/aws//modules/dms-endpoint" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - endpoint_type = "target" - engine_name = "s3" - - s3_settings = { - bucket_name = module.s3_bucket.bucket_id - bucket_folder = null - cdc_inserts_only = false - csv_row_delimiter = " " - csv_delimiter = "," - data_format = "parquet" - compression_type = "GZIP" - date_partition_delimiter = "NONE" - date_partition_enabled = true - date_partition_sequence = "YYYYMMDD" - include_op_for_full_load = true - parquet_timestamp_in_millisecond = true - timestamp_column_name = "timestamp" - service_access_role_arn = aws_iam_role.s3.arn - } - - extra_connection_attributes = "" - - attributes = ["target"] - context = module.this.context - } - - module "dms_replication_task" { - source = "cloudposse/dms/aws//modules/dms-replication-task" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - replication_instance_arn = module.dms_replication_instance.replication_instance_arn - start_replication_task = true - migration_type = "full-load-and-cdc" - source_endpoint_arn = module.dms_endpoint_aurora_postgres.endpoint_arn - target_endpoint_arn = module.dms_endpoint_s3_bucket.endpoint_arn - - replication_task_settings = file("${path.module}/config/replication-task-settings.json") - table_mappings = file("${path.module}/config/replication-task-table-mappings.json") - - context = module.this.context - } - - module "sns_topic" { - source = "cloudposse/sns-topic/aws" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - sqs_dlq_enabled = false - fifo_topic = false - fifo_queue_enabled = false - encryption_enabled = false - - allowed_aws_services_for_sns_published = [ - "cloudwatch.amazonaws.com", - "dms.amazonaws.com" - ] - - context = module.this.context - } - - module "dms_replication_instance_event_subscription" { - source = "cloudposse/dms/aws//modules/dms-event-subscription" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - event_subscription_enabled = true - source_type = "replication-instance" - source_ids = [module.dms_replication_instance.replication_instance_id] - sns_topic_arn = module.sns_topic.sns_topic_arn - - # https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dms/describe-event-categories.html - event_categories = [ - "low storage", - "configuration change", - "maintenance", - "deletion", - "creation", - "failover", - "failure" - ] - - attributes = ["instance"] - context = module.this.context - } - - module "dms_replication_task_event_subscription" { - source = "cloudposse/dms/aws//modules/dms-event-subscription" - # Cloud Posse recommends pinning every module to a specific version - # version = "x.x.x" - - event_subscription_enabled = true - source_type = "replication-task" - source_ids = [module.dms_replication_task.replication_task_id] - sns_topic_arn = module.sns_topic.sns_topic_arn - - # https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dms/describe-event-categories.html - event_categories = [ - "configuration change", - "state change", - "deletion", - "creation", - "failure" - ] - - attributes = ["task"] - context = module.this.context - } -``` - -__NOTE:__ If a replication task is in "Failed" state (for any reason, e.g. network connectivity issues, database table issues, configuration issues), -it can't be destroyed with Terraform (but can be updated). -The task needs to be updated/fixed and moved to any other state like "Running", "Stopped", "Starting", "Ready", etc. - -You can monitor the progress of your task by checking the task status and by monitoring the task's control table. -The task status indicates the condition of an AWS DMS task and its associated resources. -It includes such indications as if the task is being created, starting, running, stopped, or failed. -It also includes the current state of the tables that the task is migrating, such as if a full load of a table has begun -or is in progress and details such as the number of inserts, deletes, and updates have occurred for the table. - -Refer to [Monitoring DMS Task Status](https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Monitoring.html#CHAP_Tasks.Status) for more information. - - - - -## Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.0 | -| [aws](#requirement\_aws) | >= 5.0 | - -## Providers - -No providers. - -## Modules - -| Name | Source | Version | -|------|--------|---------| -| [this](#module\_this) | cloudposse/label/null | 0.25.0 | - -## Resources - -No resources. - -## Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [additional\_tag\_map](#input\_additional\_tag\_map) | Additional key-value pairs to add to each map in `tags_as_list_of_maps`. Not added to `tags` or `id`.
This is for some rare cases where resources want additional configuration of tags
and therefore take a list of maps with tag key, value, and additional configuration. | `map(string)` | `{}` | no | -| [attributes](#input\_attributes) | ID element. Additional attributes (e.g. `workers` or `cluster`) to add to `id`,
in the order they appear in the list. New attributes are appended to the
end of the list. The elements of the list are joined by the `delimiter`
and treated as a single ID element. | `list(string)` | `[]` | no | -| [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.
Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no | -| [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.
Map of maps. Keys are names of descriptors. Values are maps of the form
`{
format = string
labels = list(string)
}`
(Type is `any` so the map values can later be enhanced to provide additional options.)
`format` is a Terraform format string to be passed to the `format()` function.
`labels` is a list of labels, in order, to pass to `format()` function.
Label values will be normalized before being passed to `format()` so they will be
identical to how they appear in `id`.
Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no | -| [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no | -| [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no | -| [id\_length\_limit](#input\_id\_length\_limit) | Limit `id` to this many characters (minimum 6).
Set to `0` for unlimited length.
Set to `null` for keep the existing setting, which defaults to `0`.
Does not affect `id_full`. | `number` | `null` | no | -| [label\_key\_case](#input\_label\_key\_case) | Controls the letter case of the `tags` keys (label names) for tags generated by this module.
Does not affect keys of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper`.
Default value: `title`. | `string` | `null` | no | -| [label\_order](#input\_label\_order) | The order in which the labels (ID elements) appear in the `id`.
Defaults to ["namespace", "environment", "stage", "name", "attributes"].
You can omit any of the 6 labels ("tenant" is the 6th), but at least one must be present. | `list(string)` | `null` | no | -| [label\_value\_case](#input\_label\_value\_case) | Controls the letter case of ID elements (labels) as included in `id`,
set as tag values, and output by this module individually.
Does not affect values of tags passed in via the `tags` input.
Possible values: `lower`, `title`, `upper` and `none` (no transformation).
Set this to `title` and set `delimiter` to `""` to yield Pascal Case IDs.
Default value: `lower`. | `string` | `null` | no | -| [labels\_as\_tags](#input\_labels\_as\_tags) | Set of labels (ID elements) to include as tags in the `tags` output.
Default is to include all labels.
Tags with empty values will not be included in the `tags` output.
Set to `[]` to suppress all generated tags.
**Notes:**
The value of the `name` tag, if included, will be the `id`, not the `name`.
Unlike other `null-label` inputs, the initial setting of `labels_as_tags` cannot be
changed in later chained modules. Attempts to change it will be silently ignored. | `set(string)` |
[
"default"
]
| no | -| [name](#input\_name) | ID element. Usually the component or solution name, e.g. 'app' or 'jenkins'.
This is the only ID element not also included as a `tag`.
The "name" tag is set to the full `id` string. There is no tag with the value of the `name` input. | `string` | `null` | no | -| [namespace](#input\_namespace) | ID element. Usually an abbreviation of your organization name, e.g. 'eg' or 'cp', to help ensure generated IDs are globally unique | `string` | `null` | no | -| [regex\_replace\_chars](#input\_regex\_replace\_chars) | Terraform regular expression (regex) string.
Characters matching the regex will be removed from the ID elements.
If not set, `"/[^a-zA-Z0-9-]/"` is used to remove all characters other than hyphens, letters and digits. | `string` | `null` | no | -| [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no | -| [tags](#input\_tags) | Additional tags (e.g. `{'BusinessUnit': 'XYZ'}`).
Neither the tag keys nor the tag values will be modified by this module. | `map(string)` | `{}` | no | -| [tenant](#input\_tenant) | ID element \_(Rarely used, not included by default)\_. A customer identifier, indicating who this instance of a resource is for | `string` | `null` | no | - -## Outputs - -No outputs. - - diff --git a/docs/generated/github-actions/_category_.json b/docs/github-actions/_category_.json similarity index 100% rename from docs/generated/github-actions/_category_.json rename to docs/github-actions/_category_.json diff --git a/docs/generated/github-actions/index.mdx b/docs/github-actions/index.mdx similarity index 100% rename from docs/generated/github-actions/index.mdx rename to docs/github-actions/index.mdx diff --git a/docs/generated/modules/_category_.json b/docs/modules/_category_.json similarity index 100% rename from docs/generated/modules/_category_.json rename to docs/modules/_category_.json diff --git a/docs/generated/modules/index.mdx b/docs/modules/index.mdx similarity index 100% rename from docs/generated/modules/index.mdx rename to docs/modules/index.mdx diff --git a/package-lock.json b/package-lock.json index d2dcc2ee0..898f4f0d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6874,8 +6874,8 @@ "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/custom-loaders": { - "resolved": "plugins/custom-loaders", - "link": true + "version": "0.0.0", + "resolved": "file:plugins/custom-loaders" }, "node_modules/cytoscape": { "version": "3.30.0", @@ -22543,9 +22543,6 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } - }, - "plugins/custom-loaders": { - "version": "0.0.0" } } } diff --git a/scripts/docs-collator/AbstractFetcher.py b/scripts/docs-collator/AbstractFetcher.py index 0729b64d3..bb4045db7 100644 --- a/scripts/docs-collator/AbstractFetcher.py +++ b/scripts/docs-collator/AbstractFetcher.py @@ -1,8 +1,8 @@ import os -DOCS_DIR = 'docs' -TARGETS_MD = 'targets.md' -README_YAML = 'README.yaml' +DOCS_DIR = "docs" +TARGETS_MD = "targets.md" +README_YAML = "README.yaml" class MissingReadmeYamlException(Exception): @@ -20,7 +20,9 @@ def _fetch_readme_yaml(self, repo, module_download_dir): self.github_provider.fetch_file(repo, README_YAML, module_download_dir) def _fetch_docs(self, repo, module_download_dir, submodule_dir=""): - remote_files = self.github_provider.list_repo_dir(repo, os.path.join(submodule_dir, DOCS_DIR)) + remote_files = self.github_provider.list_repo_dir( + repo, os.path.join(submodule_dir, DOCS_DIR) + ) for remote_file in remote_files: if os.path.basename(remote_file) == TARGETS_MD: # skip targets.md diff --git a/scripts/docs-collator/AbstractRenderer.py b/scripts/docs-collator/AbstractRenderer.py index c30fa7e7e..73f772eb5 100644 --- a/scripts/docs-collator/AbstractRenderer.py +++ b/scripts/docs-collator/AbstractRenderer.py @@ -4,8 +4,8 @@ from utils import io from utils import rendering -README_YAML = 'README.yaml' -DOCS_DIR = 'docs' +README_YAML = "README.yaml" +DOCS_DIR = "docs" class TerraformDocsRenderingError(Exception): @@ -22,7 +22,9 @@ def _pre_rendering_fixes(self, repo, module_download_dir, submodule_dir=""): if submodule_dir == "": content = rendering.rename_name(repo.name, content) else: - content = rendering.rename_name("pre-fix-" + os.path.basename(submodule_dir), content) + content = rendering.rename_name( + "pre-fix-" + os.path.basename(submodule_dir), content + ) io.save_string_to_file(readme_yaml_file, content) def _post_rendering_fixes(self, repo, readme_md_file, submodule_dir=""): @@ -30,19 +32,25 @@ def _post_rendering_fixes(self, repo, readme_md_file, submodule_dir=""): content = rendering.fix_self_non_closing_br_tags(content) content = rendering.fix_custom_non_self_closing_tags_in_pre(content) content = rendering.fix_github_edit_url(content, repo, submodule_dir) - content = rendering.fix_sidebar_label(content, repo, os.path.basename(submodule_dir)) - content = rendering.replace_relative_links_with_github_links(repo, content, submodule_dir) + content = rendering.fix_sidebar_label( + content, repo, os.path.basename(submodule_dir) + ) + content = rendering.replace_relative_links_with_github_links( + repo, content, submodule_dir + ) io.save_string_to_file(readme_md_file, content) def _copy_extra_resources_for_docs(self, module_download_dir, module_docs_dir): extra_resources_dir = os.path.join(module_download_dir, DOCS_DIR) - files = io.get_filenames_in_dir(extra_resources_dir, '*', True) + files = io.get_filenames_in_dir(extra_resources_dir, "*", True) for file in files: - if os.path.basename(file).lower().endswith('.md') or os.path.isdir(file): + if os.path.basename(file).lower().endswith(".md") or os.path.isdir(file): continue - dest_file = os.path.join(module_docs_dir, DOCS_DIR, os.path.relpath(file, extra_resources_dir)) + dest_file = os.path.join( + module_docs_dir, DOCS_DIR, os.path.relpath(file, extra_resources_dir) + ) io.copy_file(file, dest_file) logging.info(f"Copied extra file: {dest_file}") diff --git a/scripts/docs-collator/ComponentRenderer.py b/scripts/docs-collator/ComponentRenderer.py index 912442d17..196d75c82 100644 --- a/scripts/docs-collator/ComponentRenderer.py +++ b/scripts/docs-collator/ComponentRenderer.py @@ -2,17 +2,17 @@ from utils import io, rendering, templating -README_MD = 'README.md' -CHANGELOG_MD = 'CHANGELOG.md' -GITHUB_REPO = 'cloudposse/terraform-aws-components' -INDEX_CATEGORY_JSON = '_category_.json' +README_MD = "README.md" +CHANGELOG_MD = "CHANGELOG.md" +GITHUB_REPO = "cloudposse/terraform-aws-components" +INDEX_CATEGORY_JSON = "_category_.json" SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) -TEMPLATES_DIR = os.path.join(SCRIPT_DIR, 'templates/components') +TEMPLATES_DIR = os.path.join(SCRIPT_DIR, "templates/components") jenv = templating.init_templating(TEMPLATES_DIR) -DOC_TEMPLATE = jenv.get_template('readme.md') -INDEX_CATEGORY_TEMPLATE = jenv.get_template('index_category.json') +DOC_TEMPLATE = jenv.get_template("readme.md") +INDEX_CATEGORY_TEMPLATE = jenv.get_template("index_category.json") class ComponentRenderer: @@ -21,12 +21,12 @@ def __init__(self, download_dir, docs_dir): self.docs_dir = docs_dir def render(self, component): - module_download_dir = os.path.join(self.download_dir, 'modules', component) + module_download_dir = os.path.join(self.download_dir, "modules", component) files = io.get_filenames_in_dir(module_download_dir, README_MD, True) - files += io.get_filenames_in_dir(module_download_dir, '*.md', True) + files += io.get_filenames_in_dir(module_download_dir, "*.md", True) - images = io.get_filenames_in_dir(module_download_dir, '*.png', True) + images = io.get_filenames_in_dir(module_download_dir, "*.png", True) for file in files: if file.endswith(CHANGELOG_MD): @@ -34,48 +34,85 @@ def render(self, component): self.__render_doc(component, file) for image in images: - io.copy_file(image, os.path.join(self.docs_dir, component, os.path.relpath(image, module_download_dir))) + io.copy_file( + image, + os.path.join( + self.docs_dir, + component, + os.path.relpath(image, module_download_dir), + ), + ) def __render_doc(self, component, file): - module_download_dir = os.path.join(self.download_dir, 'modules') + module_download_dir = os.path.join(self.download_dir, "modules") + # Render Terraform docs using template for website doc format + # This will update the given README in place + module_path = os.path.join(module_download_dir, component) + rendering.render_terraform_docs( + module_path, os.path.join(TEMPLATES_DIR, "terraform-docs.yml") + ) + + # Static replacement and corrections for docusaurus content = io.read_file_to_string(file) content = rendering.fix_self_non_closing_br_tags(content) content = rendering.fix_custom_non_self_closing_tags_in_pre(content) content = rendering.remove_logo_from_the_bottom(content) + content = rendering.fix_mdx_format(content) change_log_file = os.path.join(os.path.dirname(file), CHANGELOG_MD) - change_log_content = io.read_file_to_string(change_log_file) if os.path.exists(change_log_file) else '' + change_log_content = ( + io.read_file_to_string(change_log_file) + if os.path.exists(change_log_file) + else "" + ) change_log_content = rendering.shift_headings(change_log_content) relative_path = os.path.relpath(file, module_download_dir) - result_file = os.path.join(self.docs_dir, os.path.relpath(file, module_download_dir)) # /README.md - - name = component if os.path.basename(file) == "README.md" else os.path.basename(file).replace(".md", "") + result_file = os.path.join( + self.docs_dir, os.path.relpath(file, module_download_dir) + ) # /README.md + + name = ( + component + if os.path.basename(file) == "README.md" + else os.path.basename(file).replace(".md", "") + ) label = name title = name - github_edit_url = f"https://github.com/{GITHUB_REPO}/blob/master/modules/{relative_path}" + github_edit_url = ( + f"https://github.com/{GITHUB_REPO}/blob/main/modules/{relative_path}" + ) - if len(relative_path.split('/')) > 2 and relative_path.split('/')[1] != 'docs' : # this is submodule + if ( + len(relative_path.split("/")) > 2 and relative_path.split("/")[1] != "docs" + ): # this is submodule submodule_name = os.path.basename(os.path.dirname(result_file)) label = submodule_name title = submodule_name - # renaming final file /.md - result_file = os.path.join(os.path.dirname(result_file), f"{submodule_name}.md") + # renaming final file /.mdx + result_file = os.path.join( + os.path.dirname(result_file), f"{submodule_name}.mdx" + ) + else: + # renaming final file /README.mdx + result_file = os.path.join(os.path.dirname(result_file), f"{name}.mdx") io.create_dirs(os.path.dirname(result_file)) - tags = ['terraform', 'aws', component] + tags = ["terraform", "aws", component] - doc_content = DOC_TEMPLATE.render(label=label, - title=title, - content=content, - change_log_content=change_log_content, - github_repository=GITHUB_REPO, - github_edit_url=github_edit_url, - tags=tags) + doc_content = DOC_TEMPLATE.render( + label=label, + title=title, + content=content, + change_log_content=change_log_content, + github_repository=GITHUB_REPO, + github_edit_url=github_edit_url, + tags=tags, + ) io.save_string_to_file(result_file, doc_content) @@ -89,7 +126,6 @@ def __create_indexes_for_subfolder(self, component): def __render_category_index(self, dir): name = os.path.basename(dir) - content = INDEX_CATEGORY_TEMPLATE.render(label=name, - title=name) + content = INDEX_CATEGORY_TEMPLATE.render(label=name, title=name) io.save_string_to_file(os.path.join(dir, INDEX_CATEGORY_JSON), content) diff --git a/scripts/docs-collator/GitHubActionFetcher.py b/scripts/docs-collator/GitHubActionFetcher.py index f730422f5..b30072686 100644 --- a/scripts/docs-collator/GitHubActionFetcher.py +++ b/scripts/docs-collator/GitHubActionFetcher.py @@ -2,10 +2,10 @@ from AbstractFetcher import AbstractFetcher, MissingReadmeYamlException -DOCS_DIR = 'docs' -ACTIONS_DIR = 'actions' -README_YAML = 'README.yaml' -README_MD = 'README.md' +DOCS_DIR = "docs" +ACTIONS_DIR = "actions" +README_YAML = "README.yaml" +README_MD = "README.md" class GitHubActionFetcher(AbstractFetcher): diff --git a/scripts/docs-collator/GitHubActionRenderer.py b/scripts/docs-collator/GitHubActionRenderer.py index 1157e6c13..2a0a0e6ab 100644 --- a/scripts/docs-collator/GitHubActionRenderer.py +++ b/scripts/docs-collator/GitHubActionRenderer.py @@ -6,18 +6,18 @@ from utils import io from utils import rendering, templating -TARGETS_MD = 'targets.md' -README_YAML = 'README.yaml' -README_MD = 'README.md' -INDEX_CATEGORY_JSON = '_category_.json' -README_TEMPLATE = 'readme.md' -DOC_SUBFOLDER = 'actions' +TARGETS_MD = "targets.md" +README_YAML = "README.yaml" +README_MD = "README.md" +INDEX_CATEGORY_JSON = "_category_.json" +README_TEMPLATE = "readme.md" +DOC_SUBFOLDER = "actions" SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) -TEMPLATES_DIR = os.path.join(SCRIPT_DIR, 'templates/github-actions') +TEMPLATES_DIR = os.path.join(SCRIPT_DIR, "templates/github-actions") jenv = templating.init_templating(TEMPLATES_DIR) -INDEX_CATEGORY_TEMPLATE = jenv.get_template('index_category.json') +INDEX_CATEGORY_TEMPLATE = jenv.get_template("index_category.json") class GitHubActionRenderer(AbstractRenderer): @@ -51,12 +51,18 @@ def __render_readme(self, module_download_dir, module_docs_dir): io.create_dirs(module_docs_dir) - response = subprocess.run(["make", "readme", - f"README_TEMPLATE_FILE={readme_tmpl_file}", - f"README_FILE={readme_md_file}", - f"README_YAML={readme_yaml_file}", - f"README_TEMPLATE_YAML={readme_yaml_file}", - f"README_INCLUDES={module_download_dir}"], capture_output=True) + response = subprocess.run( + [ + "make", + "readme", + f"README_TEMPLATE_FILE={readme_tmpl_file}", + f"README_FILE={readme_md_file}", + f"README_YAML={readme_yaml_file}", + f"README_TEMPLATE_YAML={readme_yaml_file}", + f"README_INCLUDES={module_download_dir}", + ], + capture_output=True, + ) if response.returncode != 0: error_message = response.stderr.decode("utf-8") diff --git a/scripts/docs-collator/GitHubProvider.py b/scripts/docs-collator/GitHubProvider.py index 5a8f74a37..3b09f43f6 100644 --- a/scripts/docs-collator/GitHubProvider.py +++ b/scripts/docs-collator/GitHubProvider.py @@ -7,9 +7,13 @@ from utils import io -GITHUB_ORG = 'cloudposse' -TERRAFORM_MODULE_NAME_PATTERN = re.compile("^terraform-[a-zA-Z0-9]+-.*") # convention is terraform-- -GITHUB_ACTION_NAME_PATTERN = re.compile("^github-action-.*") # convention is github-action- +GITHUB_ORG = "cloudposse" +TERRAFORM_MODULE_NAME_PATTERN = re.compile( + "^terraform-[a-zA-Z0-9]+-.*" +) # convention is terraform-- +GITHUB_ACTION_NAME_PATTERN = re.compile( + "^github-action-.*" +) # convention is github-action- class GitHubProvider: @@ -17,7 +21,9 @@ def __init__(self, github_api_token): self.github = Github(github_api_token) def get_terraform_repos(self, includes_csv, excludes_csv): - return self.__get_repos(includes_csv, excludes_csv, TERRAFORM_MODULE_NAME_PATTERN) + return self.__get_repos( + includes_csv, excludes_csv, TERRAFORM_MODULE_NAME_PATTERN + ) def get_github_actions_repos(self, includes_csv, excludes_csv): return self.__get_repos(includes_csv, excludes_csv, GITHUB_ACTION_NAME_PATTERN) @@ -71,7 +77,9 @@ def list_repo_dir(self, repo, remote_dir, recursive=True): def fetch_file(self, repo, remote_file, output_dir): io.create_dirs(os.path.join(output_dir, os.path.dirname(remote_file))) - content_encoded = repo.get_contents(remote_file, ref=repo.default_branch).content + content_encoded = repo.get_contents( + remote_file, ref=repo.default_branch + ).content content = base64.b64decode(content_encoded) output_file = os.path.join(output_dir, remote_file) io.save_to_file(output_file, content) diff --git a/scripts/docs-collator/ModuleFetcher.py b/scripts/docs-collator/ModuleFetcher.py index 6f9f9fe71..06efeee75 100644 --- a/scripts/docs-collator/ModuleFetcher.py +++ b/scripts/docs-collator/ModuleFetcher.py @@ -2,11 +2,11 @@ from AbstractFetcher import AbstractFetcher, MissingReadmeYamlException -DOCS_DIR = 'docs' -IMAGES_DIR = 'images' -SUBMODULES_DIR = 'modules' -README_YAML = 'README.yaml' -README_MD = 'README.md' +DOCS_DIR = "docs" +IMAGES_DIR = "images" +SUBMODULES_DIR = "modules" +README_YAML = "README.yaml" +README_MD = "README.md" class ModuleFetcher(AbstractFetcher): @@ -54,4 +54,8 @@ def __fetch_submodules(self, repo, module_download_dir): for readme_file in readme_files.values(): self.github_provider.fetch_file(repo, readme_file, module_download_dir) if os.path.basename(readme_file) == README_YAML: - self._fetch_docs(repo, module_download_dir, submodule_dir=os.path.dirname(readme_file)) + self._fetch_docs( + repo, + module_download_dir, + submodule_dir=os.path.dirname(readme_file), + ) diff --git a/scripts/docs-collator/ModuleRenderer.py b/scripts/docs-collator/ModuleRenderer.py index c4f525585..3f6ebaa31 100644 --- a/scripts/docs-collator/ModuleRenderer.py +++ b/scripts/docs-collator/ModuleRenderer.py @@ -6,22 +6,22 @@ from utils import io from utils import rendering, templating -DOCS_DIR = 'docs' -IMAGES_DIR = 'images' -SUBMODULES_DIR = 'modules' -TARGETS_MD = 'targets.md' -README_YAML = 'README.yaml' -README_MD = 'README.md' -INDEX_CATEGORY_JSON = '_category_.json' -MODULES_README_TEMPLATE = 'readme.md' +DOCS_DIR = "docs" +IMAGES_DIR = "images" +SUBMODULES_DIR = "modules" +TARGETS_MD = "targets.md" +README_YAML = "README.yaml" +README_MD = "README.md" +INDEX_CATEGORY_JSON = "_category_.json" +MODULES_README_TEMPLATE = "readme.md" SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) -TEMPLATES_DIR = os.path.join(SCRIPT_DIR, 'templates/modules') +TEMPLATES_DIR = os.path.join(SCRIPT_DIR, "templates/modules") jenv = templating.init_templating(TEMPLATES_DIR) -PROVIDER_INDEX_CATEGORY_TEMPLATE = jenv.get_template('provider_index_category.json') -INDEX_CATEGORY_TEMPLATE = jenv.get_template('index_category.json') -SUBMODULE_TEMPLATE = jenv.get_template('submodule.readme.md') +PROVIDER_INDEX_CATEGORY_TEMPLATE = jenv.get_template("provider_index_category.json") +INDEX_CATEGORY_TEMPLATE = jenv.get_template("index_category.json") +SUBMODULE_TEMPLATE = jenv.get_template("submodule.readme.md") class ModuleRenderer(AbstractRenderer): @@ -48,7 +48,9 @@ def render(self, repo): self._copy_extra_resources_for_docs(module_download_dir, module_docs_dir) self.__copy_extra_resources_for_images(module_download_dir, module_docs_dir) - self.__copy_extra_resources_for_submodules(repo, module_download_dir, module_docs_dir) + self.__copy_extra_resources_for_submodules( + repo, module_download_dir, module_docs_dir + ) self.__create_index_for_provider(repo) self.__create_indexes_for_subfolders(repo) @@ -60,12 +62,18 @@ def __render_readme(self, module_download_dir, module_docs_dir): io.create_dirs(module_docs_dir) - response = subprocess.run(["make", "readme", - f"README_TEMPLATE_FILE={readme_tmpl_file}", - f"README_FILE={readme_md_file}", - f"README_YAML={readme_yaml_file}", - f"README_TEMPLATE_YAML={readme_yaml_file}", - f"README_INCLUDES={module_download_dir}"], capture_output=True) + response = subprocess.run( + [ + "make", + "readme", + f"README_TEMPLATE_FILE={readme_tmpl_file}", + f"README_FILE={readme_md_file}", + f"README_YAML={readme_yaml_file}", + f"README_TEMPLATE_YAML={readme_yaml_file}", + f"README_INCLUDES={module_download_dir}", + ], + capture_output=True, + ) if response.returncode != 0: error_message = response.stderr.decode("utf-8") @@ -75,19 +83,23 @@ def __render_readme(self, module_download_dir, module_docs_dir): def __copy_extra_resources_for_images(self, module_download_dir, module_docs_dir): extra_resources_dir = os.path.join(module_download_dir, IMAGES_DIR) - files = io.get_filenames_in_dir(extra_resources_dir, '*', True) + files = io.get_filenames_in_dir(extra_resources_dir, "*", True) for file in files: if os.path.isdir(file): continue - dest_file = os.path.join(module_docs_dir, IMAGES_DIR, os.path.relpath(file, extra_resources_dir)) + dest_file = os.path.join( + module_docs_dir, IMAGES_DIR, os.path.relpath(file, extra_resources_dir) + ) io.copy_file(file, dest_file) logging.info(f"Copied extra file: {dest_file}") - def __copy_extra_resources_for_submodules(self, repo, module_download_dir, module_docs_dir): + def __copy_extra_resources_for_submodules( + self, repo, module_download_dir, module_docs_dir + ): extra_resources_dir = os.path.join(module_download_dir, SUBMODULES_DIR) - files = io.get_filenames_in_dir(extra_resources_dir, '*', True) + files = io.get_filenames_in_dir(extra_resources_dir, "*", True) readme_files = {} for remote_file in files: @@ -120,13 +132,19 @@ def __copy_extra_resources_for_submodules(self, repo, module_download_dir, modul # Copy the README.md if we found one and no README.yaml submodule_name = os.path.basename(os.path.dirname(dest_file)) submodule_readme_content = io.read_file_to_string(readme) - submodule_readme_content = rendering.replace_relative_links_with_github_links(repo, submodule_readme_content, rel_dir) - - content = SUBMODULE_TEMPLATE.render(label=submodule_name, - title=submodule_name, - description=submodule_name, - github_edit_url=f"https://github.com/{repo.full_name}/blob/{repo.default_branch}/{rel_path}", - content=submodule_readme_content) + submodule_readme_content = ( + rendering.replace_relative_links_with_github_links( + repo, submodule_readme_content, rel_dir + ) + ) + + content = SUBMODULE_TEMPLATE.render( + label=submodule_name, + title=submodule_name, + description=submodule_name, + github_edit_url=f"https://github.com/{repo.full_name}/blob/{repo.default_branch}/{rel_path}", + content=submodule_readme_content, + ) io.create_dirs(os.path.dirname(dest_file)) io.save_string_to_file(dest_file, content) @@ -139,15 +157,17 @@ def __create_index_for_provider(self, repo): json_file = os.path.join(self.docs_dir, provider, INDEX_CATEGORY_JSON) if not os.path.exists(json_file): - content = PROVIDER_INDEX_CATEGORY_TEMPLATE.render(label=provider, - title=provider, - description=provider) + content = PROVIDER_INDEX_CATEGORY_TEMPLATE.render( + label=provider, title=provider, description=provider + ) io.save_string_to_file(json_file, content) def __create_indexes_for_subfolders(self, repo): # create category index files for dirs that doesn't have files because of docusaurus sidebar rendering issues provider, module_name = rendering.parse_terraform_repo_name(repo.name) - files = io.get_filenames_in_dir(os.path.join(self.docs_dir, provider, module_name), '*', True) + files = io.get_filenames_in_dir( + os.path.join(self.docs_dir, provider, module_name), "*", True + ) for file in files: if os.path.isfile(file) or io.has_files(file): continue @@ -157,8 +177,7 @@ def __create_indexes_for_subfolders(self, repo): def __render_category_index(self, dir): name = os.path.basename(dir) - content = INDEX_CATEGORY_TEMPLATE.render(label=name, - title=name) + content = INDEX_CATEGORY_TEMPLATE.render(label=name, title=name) io.save_string_to_file(os.path.join(dir, INDEX_CATEGORY_JSON), content) diff --git a/scripts/docs-collator/render_docs_for_components.py b/scripts/docs-collator/render_docs_for_components.py index 00c85c1ee..9dbd653df 100644 --- a/scripts/docs-collator/render_docs_for_components.py +++ b/scripts/docs-collator/render_docs_for_components.py @@ -6,12 +6,12 @@ from ComponentRenderer import ComponentRenderer from utils import io -OUTPUT_DOC_DIR = 'content/components/library/aws' -CLONED_REPO_DIR = 'tmp/components/terraform-aws-components' +OUTPUT_DOC_DIR = "content/components/library/aws" +CLONED_REPO_DIR = "tmp/components/terraform-aws-components" def main(input_dir, output_dir): - modules_dir = os.path.join(input_dir, 'modules') + modules_dir = os.path.join(input_dir, "modules") logging.info(f"Looking for modules in: {modules_dir}") @@ -27,13 +27,30 @@ def main(input_dir, output_dir): @click.command() -@click.option('--input-dir', default=CLONED_REPO_DIR, required=True, help="Path to cloned repository") -@click.option('--output-dir', default=OUTPUT_DOC_DIR, required=False, help="Rendered documentation output directory") -@click.option('--log-level', default=False, required=False, help="Recursive lookup in sub folders of README.md files") +@click.option( + "--input-dir", + default=CLONED_REPO_DIR, + required=True, + help="Path to cloned repository", +) +@click.option( + "--output-dir", + default=OUTPUT_DOC_DIR, + required=False, + help="Rendered documentation output directory", +) +@click.option( + "--log-level", + default=False, + required=False, + help="Recursive lookup in sub folders of README.md files", +) def cli_main(input_dir, output_dir, log_level): - logging.basicConfig(format='[%(asctime)s] %(levelname)s %(message)s', - datefmt='%d-%m-%Y %H:%M:%S', - level=logging.getLevelName(log_level)) + logging.basicConfig( + format="[%(asctime)s] %(levelname)s %(message)s", + datefmt="%d-%m-%Y %H:%M:%S", + level=logging.getLevelName(log_level), + ) main(input_dir, output_dir) diff --git a/scripts/docs-collator/render_docs_for_github_actions.py b/scripts/docs-collator/render_docs_for_github_actions.py index a9e767d19..6f9b36ed2 100644 --- a/scripts/docs-collator/render_docs_for_github_actions.py +++ b/scripts/docs-collator/render_docs_for_github_actions.py @@ -8,12 +8,19 @@ from GitHubActionRenderer import GitHubActionRenderer from GitHubProvider import GitHubProvider -DOWNLOAD_TMP_DIR = 'tmp/github-actions' -OUTPUT_DOC_DIR = 'content/github-actions/library' -REPOS_SKIP_LIST = {'terraform-aws-components'} - - -def main(github_api_token, output_dir, download_dir, includes_csv, excludes_csv, fail_on_rendering_error): +DOWNLOAD_TMP_DIR = "tmp/github-actions" +OUTPUT_DOC_DIR = "content/github-actions/library" +REPOS_SKIP_LIST = {"terraform-aws-components"} + + +def main( + github_api_token, + output_dir, + download_dir, + includes_csv, + excludes_csv, + fail_on_rendering_error, +): github_provider = GitHubProvider(github_api_token) fetcher = GitHubActionFetcher(github_provider, download_dir) renderer = GitHubActionRenderer(download_dir, output_dir) @@ -44,30 +51,72 @@ def main(github_api_token, output_dir, download_dir, includes_csv, excludes_csv, @click.command() -@click.option('--github-api-token', - envvar='PUBLIC_REPO_ACCESS_TOKEN', required=True, help="GitHub API token") -@click.option('--output-dir', - default=OUTPUT_DOC_DIR, required=True, help="Rendered documentation output directory") -@click.option('--download-dir', - default=DOWNLOAD_TMP_DIR, required=True, help="Temporary download directory") -@click.option('--includes', - required=False, - help="Comma separated list of repos to include. Conflicts with --excludes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'") -@click.option('--excludes', - required=False, - help="Comma separated list of repos to exclude. Conflicts with --includes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'") -@click.option('--fail-on-rendering-error', - is_flag=True, show_default=True, default=False, required=False, help="Fail on rendering error") -@click.option('--log-level', - default='INFO', required=False, help="Log level. Available options") -def cli_main(github_api_token, output_dir, download_dir, includes, excludes, fail_on_rendering_error, log_level): - logging.basicConfig(format='[%(asctime)s] %(levelname)s %(message)s', - datefmt='%d-%m-%Y %H:%M:%S', - level=logging.getLevelName(log_level)) - - logging.info(f"Download directory: {download_dir}, documentation output directory: {output_dir}") - - main(github_api_token, output_dir, download_dir, includes, excludes, fail_on_rendering_error) +@click.option( + "--github-api-token", + envvar="PUBLIC_REPO_ACCESS_TOKEN", + required=True, + help="GitHub API token", +) +@click.option( + "--output-dir", + default=OUTPUT_DOC_DIR, + required=True, + help="Rendered documentation output directory", +) +@click.option( + "--download-dir", + default=DOWNLOAD_TMP_DIR, + required=True, + help="Temporary download directory", +) +@click.option( + "--includes", + required=False, + help="Comma separated list of repos to include. Conflicts with --excludes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'", +) +@click.option( + "--excludes", + required=False, + help="Comma separated list of repos to exclude. Conflicts with --includes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'", +) +@click.option( + "--fail-on-rendering-error", + is_flag=True, + show_default=True, + default=False, + required=False, + help="Fail on rendering error", +) +@click.option( + "--log-level", default="INFO", required=False, help="Log level. Available options" +) +def cli_main( + github_api_token, + output_dir, + download_dir, + includes, + excludes, + fail_on_rendering_error, + log_level, +): + logging.basicConfig( + format="[%(asctime)s] %(levelname)s %(message)s", + datefmt="%d-%m-%Y %H:%M:%S", + level=logging.getLevelName(log_level), + ) + + logging.info( + f"Download directory: {download_dir}, documentation output directory: {output_dir}" + ) + + main( + github_api_token, + output_dir, + download_dir, + includes, + excludes, + fail_on_rendering_error, + ) if __name__ == "__main__": diff --git a/scripts/docs-collator/render_docs_for_modules.py b/scripts/docs-collator/render_docs_for_modules.py index 0682c57a9..5c3def28a 100644 --- a/scripts/docs-collator/render_docs_for_modules.py +++ b/scripts/docs-collator/render_docs_for_modules.py @@ -8,11 +8,18 @@ from ModuleFetcher import ModuleFetcher, MissingReadmeYamlException from ModuleRenderer import ModuleRenderer -DOWNLOAD_TMP_DIR = 'tmp/modules' -OUTPUT_DOC_DIR = 'content/modules/library' - - -def main(github_api_token, output_dir, download_dir, includes_csv, excludes_csv, fail_on_rendering_error): +DOWNLOAD_TMP_DIR = "tmp/modules" +OUTPUT_DOC_DIR = "content/modules/library" + + +def main( + github_api_token, + output_dir, + download_dir, + includes_csv, + excludes_csv, + fail_on_rendering_error, +): github_provider = GitHubProvider(github_api_token) fetcher = ModuleFetcher(github_provider, download_dir) renderer = ModuleRenderer(download_dir, output_dir) @@ -43,30 +50,72 @@ def main(github_api_token, output_dir, download_dir, includes_csv, excludes_csv, @click.command() -@click.option('--github-api-token', - envvar='PUBLIC_REPO_ACCESS_TOKEN', required=True, help="GitHub API token") -@click.option('--output-dir', - default=OUTPUT_DOC_DIR, required=True, help="Rendered documentation output directory") -@click.option('--download-dir', - default=DOWNLOAD_TMP_DIR, required=True, help="Temporary download directory") -@click.option('--includes', - required=False, - help="Comma separated list of repos to include. Conflicts with --excludes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'") -@click.option('--excludes', - required=False, - help="Comma separated list of repos to exclude. Conflicts with --includes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'") -@click.option('--fail-on-rendering-error', - is_flag=True, show_default=True, default=False, required=False, help="Fail on rendering error") -@click.option('--log-level', - default='INFO', required=False, help="Log level. Available options") -def cli_main(github_api_token, output_dir, download_dir, includes, excludes, fail_on_rendering_error, log_level): - logging.basicConfig(format='[%(asctime)s] %(levelname)s %(message)s', - datefmt='%d-%m-%Y %H:%M:%S', - level=logging.getLevelName(log_level)) - - logging.info(f"Download directory: {download_dir}, documentation output directory: {output_dir}") - - main(github_api_token, output_dir, download_dir, includes, excludes, fail_on_rendering_error) +@click.option( + "--github-api-token", + envvar="PUBLIC_REPO_ACCESS_TOKEN", + required=True, + help="GitHub API token", +) +@click.option( + "--output-dir", + default=OUTPUT_DOC_DIR, + required=True, + help="Rendered documentation output directory", +) +@click.option( + "--download-dir", + default=DOWNLOAD_TMP_DIR, + required=True, + help="Temporary download directory", +) +@click.option( + "--includes", + required=False, + help="Comma separated list of repos to include. Conflicts with --excludes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'", +) +@click.option( + "--excludes", + required=False, + help="Comma separated list of repos to exclude. Conflicts with --includes. Example: 'terraform-aws-s3-log-storage,terraform-aws-network-firewall'", +) +@click.option( + "--fail-on-rendering-error", + is_flag=True, + show_default=True, + default=False, + required=False, + help="Fail on rendering error", +) +@click.option( + "--log-level", default="INFO", required=False, help="Log level. Available options" +) +def cli_main( + github_api_token, + output_dir, + download_dir, + includes, + excludes, + fail_on_rendering_error, + log_level, +): + logging.basicConfig( + format="[%(asctime)s] %(levelname)s %(message)s", + datefmt="%d-%m-%Y %H:%M:%S", + level=logging.getLevelName(log_level), + ) + + logging.info( + f"Download directory: {download_dir}, documentation output directory: {output_dir}" + ) + + main( + github_api_token, + output_dir, + download_dir, + includes, + excludes, + fail_on_rendering_error, + ) if __name__ == "__main__": diff --git a/scripts/docs-collator/templates/components/readme.md b/scripts/docs-collator/templates/components/readme.md index 89cda4c88..838eb2555 100644 --- a/scripts/docs-collator/templates/components/readme.md +++ b/scripts/docs-collator/templates/components/readme.md @@ -9,7 +9,7 @@ tags: [{{ tags|join(', ') }}] {{ content }} {% if change_log_content|trim != "" %} -## CHANGELOG +## Changelog {{ change_log_content }} -{% endif %} \ No newline at end of file +{% endif %} diff --git a/scripts/docs-collator/templates/components/terraform-docs.yml b/scripts/docs-collator/templates/components/terraform-docs.yml new file mode 100644 index 000000000..08a076091 --- /dev/null +++ b/scripts/docs-collator/templates/components/terraform-docs.yml @@ -0,0 +1,225 @@ +# https://pkg.go.dev/github.com/terraform-docs/terraform-docs/terraform#Module +content: |- + {{- $context_variables := list "context" "enabled" "namespace" "tenant" "environment" "stage" "name" "delimiter" "attributes" "tags" "additional_tag_map" "label_order" "regex_replace_chars" "id_length_limit" "label_key_case" "label_value_case" "descriptor_formats" "labels_as_tags" -}} + + {{ .Header }} + + ## Contents + + {{ if ne (len .Module.Requirements) 0 -}} + ### Version Requirements + {{ range .Module.Providers }} + - `{{ .Name }}`{{ if .Version }}, version: `{{ .Version }}`{{ end }} + {{- end }} + {{- end }} + + {{ if ne (len .Module.Providers) 0 -}} + ### Providers + {{ range .Module.Providers }} + - `{{ .Name }}`{{ if .Version }}, version: `{{ .Version }}`{{ end }} + {{- end }} + {{- end }} + + {{ if ne (len .Module.ModuleCalls) 0 -}} + ### Modules + + Name | Version | Source | Description + --- | --- | --- | --- + {{ range .Module.ModuleCalls }} + {{- $baseUrl := "https://registry.terraform.io/modules" -}} + {{ if contains "//" .Source -}} + {{- $moduleParts := regexSplit "//" .Source 2 -}} + {{- $moduleName := first $moduleParts -}} + {{- $submodulePath := last $moduleParts -}} + `{{ .Name }}` | {{ .Version | default "latest" }} | {{ printf "[`%s`](%s/%s/%s/%s)" .Source $baseUrl $moduleName $submodulePath .Version }} | {{ tostring .Description | default "n/a" }} + {{ else -}} + `{{ .Name }}` | {{ .Version | default "latest" }} | {{ printf "[`%s`](%s/%s/%s)" .Source $baseUrl .Source .Version }} | {{ tostring .Description | default "n/a" }} + {{ end }} + {{- end -}} + {{- end -}} + + {{- if ne (len .Module.Resources) 0 }} + + ### Resources + + The following resources are used by this module: + {{ range .Module.Resources }} + {{- $isResource := and $.Config.Sections.Resources ( eq "resource" (printf "%s" .GetMode)) }} + {{- if $isResource }} + {{- $fullspec := ternary .URL (printf "[`%s`](%s)" .Spec .URL) .Spec }} + - {{ $fullspec }} {{ printf "(%s)" .GetMode -}} + {{- end }} + {{- end }} + {{- end }} + + {{ if ne (len .Module.Resources) 0 -}} + ### Data Sources + + The following data sources are used by this module: + {{ range .Module.Resources }} + {{- $isDataResource := and $.Config.Sections.DataSources ( eq "data source" (printf "%s" .GetMode)) }} + {{- if $isDataResource }} + {{- $fullspec := ternary .URL (printf "[`%s`](%s)" .Spec .URL) .Spec }} + - {{ $fullspec }} {{ printf "(%s)" .GetMode -}} + {{- end }} + {{- end }} + {{- end }} + + ### Required Inputs + +
+ {{- range .Module.Inputs }} + {{- if and (not (has .Name $context_variables)) .Required }} +
`{{ .Name }}`{{ if lt (len (split "\n" (tostring .Type))) 2 }} (`{{ tostring .Type }}`){{end}} required{{ if contains "OBSOLETE: " (tostring .Description) }} OBSOLETE{{ end }}
+
+ {{- $lines := regexSplit "\n" (tostring .Description | replace "OBSOLETE: " "") -1 -}} + {{- range $lines }} + {{ . }}
+ {{- end }} + + {{ if gt (len (split "\n" (tostring .Type))) 2 }} + **Type:** + ```hcl + {{ .Type.Raw }} + ``` +
+ {{ end }} + +
+ {{- end }} + {{- end }} +
+ + {{ $optional := false -}} + {{ range .Module.Inputs }}{{ if not .Required }}{{ $optional = true -}}{{ end -}}{{ end -}} + + {{ if $optional -}} + + ### Optional Inputs + +
+ {{- range .Module.Inputs }} + {{- if and (not (has .Name $context_variables)) (not .Required) }} +
`{{ .Name }}`{{ if lt (len (split "\n" (tostring .Type))) 2 }} (`{{ tostring .Type }}`){{end}} optional{{ if contains "OBSOLETE: " (tostring .Description) }} OBSOLETE{{ end }}
+
+ {{- $lines := regexSplit "\n" (tostring .Description | replace "OBSOLETE: " "") -1 -}} + {{- range $lines }} + {{ . }}
+ {{- end }} +
+ {{ if gt (len (split "\n" (tostring .Type))) 2 }} + **Type:** + ```hcl + {{ tostring .Type }} + ``` +
+ {{ end }} + **Default value:** {{ if lt (len (split "\n" .GetValue)) 2 }}`{{ .GetValue }}`{{ else }} + ```hcl + {{- $lines := regexSplit "\n" .GetValue -1 -}} + {{- range $lines }} + {{ . }} + {{- end }} + ``` + {{ end }} +
+ {{- end -}} + {{ end -}} +
+ {{ end }} + + ### Context Variables + +
+ + The following variables are defined in the `context.tf` file of this module and part of the [terraform-null-label](https://registry.terraform.io/modules/cloudposse/label/null) pattern. + + +
+ {{- range .Module.Inputs }} + {{- if and (has .Name $context_variables) }} +
`{{ .Name }}`{{ if lt (len (split "\n" (tostring .Type))) 2 }} (`{{ tostring .Type }}`){{end}} {{ ternary .Required "required" "optional" }}{{ if contains "OBSOLETE: " (tostring .Description) }} OBSOLETE{{ end }}
+
+ {{- $lines := regexSplit "\n" (tostring .Description | replace "OBSOLETE: " "") -1 -}} + {{- range $lines }} + {{ . }}
+ {{- end }} + **Required:** {{ ternary .Required "Yes" "No" }}
+ {{ if gt (len (split "\n" (tostring .Type))) 2 }} + **Type:** + ```hcl + {{ .Type.Raw }} + ``` +
+ {{ end }} + **Default value:** {{ if lt (len (split "\n" .GetValue)) 2 }}`{{ .GetValue }}`{{ else }} + ```hcl + {{- $lines := regexSplit "\n" .GetValue -1 -}} + {{- range $lines }} + {{ . }} + {{- end }} + ``` + {{ end }} +
+ {{- end }} + {{- end }} +
+
+ + {{ if ne (len .Module.Outputs) 0 -}} + ### Outputs + +
+ {{- range .Module.Outputs }} +
`{{ .Name }}`{{ if contains "OBSOLETE: " (tostring .Description) }} OBSOLETE{{ end }}
+
+ {{- $lines := regexSplit "\n" (tostring .Description | replace "OBSOLETE: " "" | default "n/a" ) -1 -}} + {{- range $lines }} + {{ . }}
+ {{- end }} +
+ {{- end }} +
+ {{- end }} + +formatter: "markdown document" # this is required +version: "" + +recursive: + enabled: false + path: modules + +sections: + hide: [] + show: [] + +sort: + enabled: true + by: required + +output-values: + enabled: false + from: '' + +output: + file: README.md + mode: inject + template: |- + + {{ .Content }} + + +settings: + anchor: true + color: true + default: true + description: true + escape: true + hide-empty: false + html: true + indent: 2 + lockfile: false + read-comments: true + required: true + sensitive: true + type: true diff --git a/scripts/docs-collator/utils/io.py b/scripts/docs-collator/utils/io.py index 407a98685..e9d033f39 100644 --- a/scripts/docs-collator/utils/io.py +++ b/scripts/docs-collator/utils/io.py @@ -14,14 +14,14 @@ def save_to_file(file, content): def read_file_to_string(file): - with open(file, 'r') as file: + with open(file, "r") as file: content = file.read() return content def read_file_to_list_of_strings(file): - with open(file, 'r') as file: + with open(file, "r") as file: result = file.read().splitlines() return result @@ -37,7 +37,7 @@ def remove_dir(dir): def get_filenames_in_dir(dir, pattern, recursive=False): - glob_pattern = f'**/{pattern}' if recursive else pattern + glob_pattern = f"**/{pattern}" if recursive else pattern return glob.glob(os.path.join(dir, glob_pattern), recursive=recursive) diff --git a/scripts/docs-collator/utils/rendering.py b/scripts/docs-collator/utils/rendering.py index 73abfbc5a..2feedb6c1 100644 --- a/scripts/docs-collator/utils/rendering.py +++ b/scripts/docs-collator/utils/rendering.py @@ -1,15 +1,16 @@ import re +import subprocess -TAG_REGEX = re.compile('(<)([0-9a-zA-Z._-]+)(>)', re.IGNORECASE) -BR_REGEX = re.compile(re.escape('
'), re.IGNORECASE) -SIDEBAR_LABEL_REGEX = re.compile('sidebar_label: .*', re.IGNORECASE) -CUSTOM_EDIT_URL_REGEX = re.compile('custom_edit_url: .*', re.IGNORECASE) -NAME_REGEX = re.compile('name: .*', re.IGNORECASE) +TAG_REGEX = re.compile("(<)([0-9a-zA-Z._-]+)(>)", re.IGNORECASE) +BR_REGEX = re.compile(re.escape("
"), re.IGNORECASE) +SIDEBAR_LABEL_REGEX = re.compile("sidebar_label: .*", re.IGNORECASE) +CUSTOM_EDIT_URL_REGEX = re.compile("custom_edit_url: .*", re.IGNORECASE) +NAME_REGEX = re.compile("name: .*", re.IGNORECASE) RELATIVE_LINK_PATTERN = r"\]\((?!http[s]?://)([^)\s]+)\)" def fix_self_non_closing_br_tags(content): - return BR_REGEX.sub('
', content) + return BR_REGEX.sub("
", content) def fix_custom_non_self_closing_tags_in_pre(content): @@ -17,20 +18,20 @@ def fix_custom_non_self_closing_tags_in_pre(content): fixed_lines = [] for line in lines: - if not line.startswith('|'): + if not line.startswith("|"): fixed_lines.append(line) continue - groups = re.findall(r'
(.*?)
', line) + groups = re.findall(r"
(.*?)
", line) for group in groups: before = group after = TAG_REGEX.sub(r"<\2>", group) - line = line.replace(f'
{before}
', f'
{after}
') + line = line.replace(f"
{before}
", f"
{after}
") fixed_lines.append(line) - return '\n'.join(fixed_lines) + return "\n".join(fixed_lines) def shift_headings(content): @@ -40,11 +41,11 @@ def shift_headings(content): # figuring out how much to shift headings to the right so the biggest heading is H3 for line in lines: - if re.match(r'^#\s+', line): # we have to shift all headings by 2 + if re.match(r"^#\s+", line): # we have to shift all headings by 2 shift_by = 2 break - if re.match(r'^##\s+', line): # we have to shift all headings by 1 + if re.match(r"^##\s+", line): # we have to shift all headings by 1 shift_by = 1 break @@ -52,7 +53,7 @@ def shift_headings(content): return content for line in lines: - if not line.startswith('#'): # not a heading + if not line.startswith("#"): # not a heading fixed_lines.append(line) continue @@ -61,26 +62,80 @@ def shift_headings(content): elif shift_by == 2: fixed_lines.append(f"##{line}") - return '\n'.join(fixed_lines) + return "\n".join(fixed_lines) def fix_sidebar_label(content, repo, submodule_name=""): module_name = submodule_name if not module_name: provider, module_name = parse_terraform_repo_name(repo.name) - return SIDEBAR_LABEL_REGEX.sub(f'sidebar_label: {module_name}', content) + return SIDEBAR_LABEL_REGEX.sub(f"sidebar_label: {module_name}", content) def fix_github_edit_url(content, repo, submodule_dir=""): - subdir = f"/{submodule_dir}" if submodule_dir else '' + subdir = f"/{submodule_dir}" if submodule_dir else "" github_edit_url = f"custom_edit_url: https://github.com/{repo.full_name}/blob/{repo.default_branch}{subdir}/README.yaml" return CUSTOM_EDIT_URL_REGEX.sub(github_edit_url, content) +def fix_mdx_format(content): + """ + Replace all special characters outside code blocks for MDX support + + Even after we re-render all terraform-docs, there are still some issues in our markdown files. + This function cleans up the remaining issues. + """ + replacements = { + r"{": r"\{", # Replace curly braces with literal + r"}": r"\}", + } + + in_code_block = False + lines = content.splitlines() + result = [] + for line in lines: + # Check if the line starts or ends a code block + if line.strip().startswith("```"): + in_code_block = not in_code_block + result.append(line) + continue + + # Perform replacements only if not in a code block + if not in_code_block: + for pattern, replacement in replacements.items(): + line = re.sub(pattern, replacement, line) + + result.append(line) + + return "\n".join(result) + + +def render_terraform_docs(terraform_module_path, terraform_docs_configuration): + """ + Renders Terraform module documentation using terraform-docs with a provided configuration. + """ + try: + # Command to run terraform-docs + command = [ + "terraform-docs", + "markdown", + terraform_module_path, + "--config", + terraform_docs_configuration, + ] + + # Run the command and capture the output + return subprocess.run(command, check=True, capture_output=True, text=True) + + except subprocess.CalledProcessError as e: + print(f"An error occurred while running terraform-docs: {e.stderr}") + + def remove_logo_from_the_bottom(content): return content.replace( '[](https://cpco.io/component)', - '') + "", + ) def remove_targets_md(content): @@ -89,24 +144,24 @@ def remove_targets_md(content): def remove_prefix(string, prefix): if string.startswith(prefix): - return string[len(prefix):] + return string[len(prefix) :] def rename_name(name, content): - return NAME_REGEX.sub(f'name: {name}', content) + return NAME_REGEX.sub(f"name: {name}", content) def parse_terraform_repo_name(name): - name_items = name.split('-') + name_items = name.split("-") provider = name_items[1] - module_name = '-'.join(name_items[2:]) + module_name = "-".join(name_items[2:]) return provider, module_name def parse_github_action_repo_name(name): - name_items = name.split('-') - action_name = '-'.join(name_items[2:]) + name_items = name.split("-") + action_name = "-".join(name_items[2:]) return action_name @@ -116,20 +171,27 @@ def replace_relative_links_with_github_links(repo, content, relative_path=None): for link in links: # ignore links to images, anchors and emails - if link.startswith('images/') or link.startswith('#') or link.startswith('mailto:'): + if ( + link.startswith("images/") + or link.startswith("#") + or link.startswith("mailto:") + ): continue updated_link = link # remove leading './' or '/' - if link.startswith('/'): - updated_link = updated_link.replace('/', '', 1) + if link.startswith("/"): + updated_link = updated_link.replace("/", "", 1) else: - if link.startswith('./'): - updated_link = updated_link.replace('./', '', 1) + if link.startswith("./"): + updated_link = updated_link.replace("./", "", 1) if relative_path: updated_link = f"{relative_path}/{updated_link}" - content = content.replace(f"]({link})", f"](https://github.com/{repo.full_name}/tree/{repo.default_branch}/{updated_link})") + content = content.replace( + f"]({link})", + f"](https://github.com/{repo.full_name}/tree/{repo.default_branch}/{updated_link})", + ) return content diff --git a/scripts/render-docs-for-components.sh b/scripts/render-docs-for-components.sh index f2c5beb09..a0eb2d7b5 100755 --- a/scripts/render-docs-for-components.sh +++ b/scripts/render-docs-for-components.sh @@ -6,7 +6,7 @@ GITHUB_ORG=${GITHUB_ORG:-"cloudposse"} GITHUB_REPO=${GITHUB_REPO:-"terraform-aws-components"} GIT_BRANCH=${GIT_BRANCH:-"main"} TMP_CLONE_DIR="${TMP_CLONE_DIR:-tmp/components/${GITHUB_REPO}}" -RENDERED_DOCS_DIR="${RENDERED_DOCS_DIR:-content/reference/components/library/aws/}" +RENDERED_DOCS_DIR="${RENDERED_DOCS_DIR:-docs/components/library/aws/}" echo "Cloning repo '${GITHUB_ORG}/${GITHUB_REPO}'" git clone --depth 1 --branch "${GIT_BRANCH}" https://github.com/${GITHUB_ORG}/${GITHUB_REPO}.git "${TMP_CLONE_DIR}" || true diff --git a/sidebars.js b/sidebars.js index defc7762c..5a545d367 100644 --- a/sidebars.js +++ b/sidebars.js @@ -339,12 +339,12 @@ module.exports = { collapsed: false, link: { type: 'doc', - id: 'generated/components/index' + id: 'components/index' }, items: [ { type: 'autogenerated', - dirName: 'generated/components/library', + dirName: 'components/library', } ] }, @@ -356,12 +356,12 @@ module.exports = { collapsed: false, link: { type: 'doc', - id: 'generated/modules/index' + id: 'modules/index' }, items: [ { type: 'autogenerated', - dirName: 'generated/modules/library', + dirName: 'modules/library', } ] }, @@ -373,12 +373,12 @@ module.exports = { collapsed: true, link: { type: 'doc', - id: 'generated/github-actions/index' + id: 'github-actions/index' }, items: [ { type: 'autogenerated', - dirName: 'generated/github-actions/library/actions', + dirName: 'github-actions/library/actions', } ] },