Skip to content

Copy docs workflow #404

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 68 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
b172f2d
Add enough for barebones test
RubenSandwich May 19, 2025
342b4a6
Add push for testing
RubenSandwich May 19, 2025
60eb367
Fix workflow name
RubenSandwich May 19, 2025
12b0dc6
Add test file at root to check that PR opens
RubenSandwich May 19, 2025
c2e3369
login with github token
RubenSandwich May 19, 2025
4b56c79
Update gh token
RubenSandwich May 19, 2025
ec30b5c
Try just a git push
RubenSandwich May 19, 2025
102fcd7
Add back push head
RubenSandwich May 19, 2025
a6d16ef
merge into develop for now
RubenSandwich May 19, 2025
bcdc2bc
Remove push action
RubenSandwich May 19, 2025
27d44ff
remove comment that was messing up code
RubenSandwich May 19, 2025
67aa011
Get copy-cloud-docs-for-tfe compiling
RubenSandwich May 19, 2025
b84e5a6
Merge branch 'develop' into rn/add-copy-docs-workflow
RubenSandwich May 19, 2025
813da4b
Only building only version metadata
RubenSandwich May 19, 2025
4328005
Refactor to remove targetRepoLastSyncFile
RubenSandwich May 20, 2025
c0dc4e3
Downgrade remark to fix mdx v2 errors
RubenSandwich May 20, 2025
8439ce2
Get Copy cloud-docs for TFE working
RubenSandwich May 20, 2025
ac124bf
Check if the branch name already exists
RubenSandwich May 20, 2025
b9991dd
Fix type errors
RubenSandwich May 20, 2025
503dea9
Update the README with the new name
RubenSandwich May 20, 2025
5abeeed
Test the whole workflow connected together
RubenSandwich May 21, 2025
4c1a475
checkout the proper workflow file before running
RubenSandwich May 21, 2025
d51ac06
path should be a string
RubenSandwich May 21, 2025
d6a4a87
forgot a period
RubenSandwich May 21, 2025
e4af74d
Use latest sha for GHA run
RubenSandwich May 21, 2025
25db2b1
Forgot to build :facepalm:
RubenSandwich May 21, 2025
4b65042
double check that the checkout is working
RubenSandwich May 21, 2025
4b93b64
Add the current loc to checkout
RubenSandwich May 21, 2025
6f4f716
try the path just "testing"
RubenSandwich May 21, 2025
f5fdcc3
include the github workspace
RubenSandwich May 21, 2025
1d0c631
It's github.workspace
RubenSandwich May 21, 2025
a64e2f2
checkout is deleting previous folder contents
RubenSandwich May 21, 2025
1eab0df
Double check that the files are being moved over
RubenSandwich May 21, 2025
7cda500
fix move command
RubenSandwich May 21, 2025
631f326
fix a space in path
RubenSandwich May 21, 2025
444c6c9
list files for debugging
RubenSandwich May 21, 2025
ef17d02
What is in our base dir
RubenSandwich May 21, 2025
94742a5
copy dot files
RubenSandwich May 21, 2025
30c6d67
correct file path
RubenSandwich May 21, 2025
045bac1
For demo only add changed files in /ptfe-releases
RubenSandwich May 21, 2025
f3df9dc
For the sake of the demo do a clean PR
RubenSandwich May 21, 2025
065e93f
Forgot to rebuild
RubenSandwich May 21, 2025
c55547f
base against develop
RubenSandwich May 21, 2025
7f856a1
new-pr checkout should be develop as well
RubenSandwich May 21, 2025
34d2145
clean up testing code
RubenSandwich May 21, 2025
084ddd6
Don't copy over /architectural-details
RubenSandwich May 22, 2025
ee0677a
Corrected new-pr path
RubenSandwich May 22, 2025
5388e7f
prebuild-only-version-metadata exists in branch
RubenSandwich May 22, 2025
86c1f39
Correctly fetch the origin branch
RubenSandwich May 22, 2025
0ea700d
use working-directory
RubenSandwich May 22, 2025
f2edb3e
Rewrite to support two different PRs
RubenSandwich May 22, 2025
f06f4ad
Resolve the versionMetadataPath
RubenSandwich May 22, 2025
5b36f28
Fix working dir and branch checking code
RubenSandwich May 22, 2025
3c5a97a
Remove branch checking code
RubenSandwich May 22, 2025
86431ab
Try a new way to check for a branch existing
RubenSandwich May 22, 2025
3914828
try again for branch checking
RubenSandwich May 22, 2025
0b242e6
should be a "do not equals"
RubenSandwich May 22, 2025
ad5a48e
Update bot user
RubenSandwich May 22, 2025
fc945cc
Add diff PR to workflow
RubenSandwich May 22, 2025
2563a92
Update PR names
RubenSandwich May 22, 2025
8368498
Update a bit of the wording
RubenSandwich May 22, 2025
32b959a
Remove testing code
RubenSandwich May 22, 2025
701ffc4
Trigger copy docs from release notes workflow
RubenSandwich May 22, 2025
5779835
delete old tests as they are no longer being used
RubenSandwich May 22, 2025
fee1ee1
Add copywrite headers
RubenSandwich May 23, 2025
d31ca4f
Merge branch 'develop' into rn/add-copy-docs-workflow
RubenSandwich May 23, 2025
066c22c
Add todos to workflow
RubenSandwich May 23, 2025
5bab866
Add parenthesis around closed message
RubenSandwich May 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions .github/actions/copy-cloud-docs-for-tfe/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# `copy-cloud-docs-for-tfe`

This composite action is consumed by the `copy-docs.yml` workflow, which is triggered
at the time of the Terraform Enterprise team's **APP_DEADLINE** event.

Roughly, it behaves as depicted in this diagram:

```mermaid
graph LR
A[terraform-docs-common] -->|copy `/cloud-docs/*` contents<br/>alongside `/enterprise/*` contents| B[ptfe-releases]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The /cloud-docs/* and /enterprise/* are showing as Unsupported markdown in the rich diff

```

## Overview

This action looks for a few things in authored `mdx`, to determine
if sections or entire pages should be ignored from this copy process.

### Frontmatter

Adding a `tfc_only: true` line in markdown frontmatter signals to
the action that the associated `.mdx` file should not be handled in the copy process.

#### Example

```markdown
---
page_title: Assessments - API Docs - Terraform Cloud
tfc_only: true
description: >-
Assessment results contain information about continuous validation in
Terraform Cloud, like drift detection.
---
```

### HTML Comments

Specially formatted HTML comments can be used in matching pairs
to omit multiple **lines** of text from the copy process.

> **Warning**: This only works with MDX v1.

#### Example

```markdown
Some content available in both TFC & TFE...

<!-- BEGIN: TFC:only -->
## Some section

This will only be visible in TFC

<!-- END: TFC:only -->

More content available in both TFC & TFE...
```

> **Note**: More details are available in this [TFC/TFE Content exclusion][rfc] RFC.

[rfc]: https://docs.google.com/document/d/1DPJU6_7AdGIJVlwJUWBlRqREmYon2IgYf_DrtKjhkcE/edit
18 changes: 18 additions & 0 deletions .github/actions/copy-cloud-docs-for-tfe/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

name: Copy cloud-docs for TFE
description: Copy docs/nav-data/assets from source dir to target dir
runs:
using: 'node20'
main: 'out/index.js'
inputs:
source_path:
required: true
description: 'The path to source HCP terraform files from'
target_path:
required: true
description: 'The path to copy over HCP terraform files to'
new_TFE_version:
required: false
description: 'The new TFE version to create and copy docs into.'
19 changes: 19 additions & 0 deletions .github/actions/copy-cloud-docs-for-tfe/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import * as core from '@actions/core'

import { main } from './main'

async function action() {
const sourcePath = core.getInput('source_path')
const targetPath = core.getInput('target_path')
const newTFEVersion = core.getInput('new_TFE_version')
// const newTFEVersion = 'v000011-1'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment can be removed


await main(sourcePath, targetPath, newTFEVersion)
}

action()
277 changes: 277 additions & 0 deletions .github/actions/copy-cloud-docs-for-tfe/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import * as fs from 'fs'
import * as path from 'path'

import * as core from '@actions/core'
import walk from 'klaw-sync'
import matter from 'gray-matter'
import remark from 'remark'
import remarkMdx from 'remark-mdx'

import { remarkGetImages } from './remark-get-images-plugin'
import { remarkTransformCloudDocsLinks } from './remark-transfrom-cloud-docs-links'

const PR_TYPE = {
NewVersion: 'NewVersion',
Diff: 'Diff',
} as const

const imageSrcSet = new Set<string>()

// List of MDX files to exclude from being copied
const IGNORE_LIST = ['cloud-docs/index.mdx']

export const IGNORE_PATTERNS: RegExp[] = [
/cloud-docs\/agents/i,
/cloud-docs\/architectural-details/i,
]
const SUB_PATH_MAPPINGS: {
source: string
target: string
}[] = [
{
source: 'cloud-docs',
target: 'enterprise',
},
]

/**
* This is a helper to be passed to `walk` dry up repeated logic
* for ignore certain files.
*/
const filterFunc = (item: walk.Item) => {
// if the item matches a IGNORE_PATTERNS expression, exclude it
if (
IGNORE_PATTERNS.some((pattern: RegExp) => {
return pattern.test(item.path)
})
) {
return false
}

// Check files for `tfc_only` frontmatter property; Ignore them if true
if (item.stats.isFile()) {
const fullContent = fs.readFileSync(item.path, 'utf8')
const { data } = matter(fullContent)
if (data.tfc_only == true) {
return false
}
}

return true
}

/**
* A helper that accepts a data object and an array of functions that
* receive the object as an arg and transform it.
*/
const transformObject = <T = Record<string, any>>(
data: T,
plugins: Array<(data: T) => T>,
): T => {
let result = data

plugins.forEach((fn: (data: T) => T) => {
result = fn(result)
})

return result
}

/**
* This function will copy 3 things
* - MDX files
* - these can be at varying levels of nesting
* - used images
* - these are expected to all be at the same level
* - nav-data JSON files
*
* This function will also prune the target directory
* of any files that are not in the source directory.
*
* @param newTFEVersion An absolute path to a GitHub repository on disk
*/
export async function main(
sourcePath: string,
targetPath: string,
newTFEVersion?: string,
): Promise<void> {
const prType = newTFEVersion ? PR_TYPE.NewVersion : PR_TYPE.Diff

//Read version metadata and get the latest version of ptfe-releases
const versionMetadataPath = path.resolve(
'./workflow/app/api/versionMetadata.json',
)
const versionMetadata = JSON.parse(
fs.readFileSync(versionMetadataPath, 'utf8'),
)

const ptfeReleasesMetadata = versionMetadata['ptfe-releases']
if (!ptfeReleasesMetadata || ptfeReleasesMetadata.length === 0) {
throw new Error('No ptfe-releases found in versionMetadata.json')
}

const currentPtfeRelease = ptfeReleasesMetadata.find((release: any) => {
return release.isLatest
})?.version

if (!currentPtfeRelease) {
throw new Error('No latest ptfe-releases found in versionMetadata.json')
}

core.info(
`Latest ptfe-releases version found in versionMetadata.json: ${currentPtfeRelease}`,
)

const HCPsourceDir = path.join(sourcePath, 'content/terraform-docs-common')
const HCPContentDir = path.join(HCPsourceDir, 'docs')

const newTFEVersionDir = path.join(
targetPath,
'content/ptfe-releases',
prType === PR_TYPE.NewVersion ? newTFEVersion : currentPtfeRelease,
)

const newTFEVersionContentDir = path.join(newTFEVersionDir, 'docs')
const newTFEVersionImageDir = path.join(newTFEVersionDir, 'img/docs')

// If this is a new version, we need to copy the current ptfe-release
// files to the new version's directory.
// This is to ensure that we have the all of the images and nav-data
if (prType === PR_TYPE.NewVersion) {
core.info(`Creating new version directory: ${newTFEVersionDir}`)
fs.mkdirSync(newTFEVersionDir, { recursive: true })

const prevTFEVersionDir = path.join(
targetPath,
'content/ptfe-releases',
currentPtfeRelease,
)

fs.cpSync(prevTFEVersionDir, newTFEVersionDir, { recursive: true })
}

// traverse source docs and accumulate mdx files for a given set of "subPaths"
let items: ReadonlyArray<walk.Item> = []

for (const { source: subPath } of SUB_PATH_MAPPINGS) {
const src = path.join(HCPContentDir, subPath)
const docItems = walk(src, {
nodir: true,
filter: filterFunc,
})
items = items.concat(docItems)
}

// process each mdx file
for (const item of items) {
// ignore some files
if (
IGNORE_LIST.some((ignore: string) => {
return item.path.endsWith(ignore)
})
) {
continue
}

// extract mdx content; ignore frontmatter
const fullContent = fs.readFileSync(item.path, 'utf8')

// eslint-disable-next-line prefer-const
let { content, data } = matter(fullContent)

data = transformObject(data, [
// inject `source` frontmatter property
function injectSource(d: { [key: string]: any }) {
d.source = path.basename(HCPsourceDir)
return d
},
// replace cloud instances with enterprise
function replaceCloudWithEnterprise(d: { [key: string]: any }) {
// Some docs do not have all frontmatter properties. Make sure
// we do not assign `undefined` (which is invalid) in YAML
if (d.page_title) {
d.page_title = d.page_title.replace(
'Terraform Cloud',
'Terraform Enterprise',
)
d.page_title = d.page_title.replace(
'HCP Terraform',
'Terraform Enterprise',
)
}

if (d.description) {
d.description = d.description.replace(
'Terraform Cloud',
'Terraform Enterprise',
)
d.description = d.description.replace(
'HCP Terraform',
'Terraform Enterprise',
)
}
return d
},
])

const vfile = await remark()
.use(remarkMdx)
// @ts-expect-error remark is being passed in through the pipeline
.use(remarkGetImages, HCPsourceDir, imageSrcSet)
// @ts-expect-error remark is being passed in through the pipeline
.use(remarkTransformCloudDocsLinks)
.process(content)

// replace \-> with ->
const stringOutput = vfile.toString().replaceAll('\\->', '->')

// overwrite original file with transformed content
const contents = matter.stringify('\n' + stringOutput, data)
fs.writeFileSync(item.path, contents)
}

// Copy an entire directory
// ---------------------------------------------
// /{source}/cloud-docs/dir/some-doc.mdx
// ↓ ↓ ↓ ↓
// /{target}/enterprise/dir/some-docs.mdx
// ---------------------------------------------
for (const { source, target } of SUB_PATH_MAPPINGS) {
const src = path.join(HCPContentDir, source)
const dest = path.join(newTFEVersionContentDir, target)

const items = walk(src, {
nodir: true,
filter: filterFunc,
})

for (const item of items) {
// ignore some files
if (
IGNORE_LIST.some((ignore: string) => {
return item.path.endsWith(ignore)
})
) {
continue
}

const destAbsolutePath = item.path.replace(src, dest)
fs.mkdirSync(path.dirname(destAbsolutePath), { recursive: true })
fs.copyFileSync(item.path, destAbsolutePath)
}
}

// Copy images
for (const src of Array.from(imageSrcSet)) {
const basename = path.basename(src)
const target = path.join(newTFEVersionImageDir, basename)

fs.mkdirSync(newTFEVersionImageDir, { recursive: true })
fs.copyFileSync(src, target)
}
}
Loading
Loading