Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,6 @@ tfe-releases-repos.json
scripts/prebuild/prebuild-arm-mac-binary
scripts/prebuild/prebuild-x64-linux-binary
scripts/prebuild/prebuild-arm-linux-binary

# GA to RC tool output files
scripts/sync-ga-to-rc/output/*.txt
177 changes: 177 additions & 0 deletions scripts/sync-ga-to-rc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# Sync GA change to RC docset

The GA -> RC sync script helps with maintenance of long-lived release branches
for versioned docs by comparing updates since a provided cutoff in the current
(GA) docset against updates in an unreleased (RC) docset.

The default cutoff date is the last run date for the provided product slug, if
it exists. Otherwise, the script defaults to the creation date of the RC release
branch. The script standardizes all timestamps to ISO for simplicity but takes
the optional override date as a local time.


## Assumptions

- Your RC release branch use the following naming convention: `<product_slug>/<rc_version> <docTag>`.
- You have the GitHub CLI (`gh`) installed. The CLI is required if you want the
script to create a PR on your behalf. ==> DISABLED (THE PROCESS IS STILL BUGGY)


## Flags

Flag | Type | Default | Description
--------- | -------- | ------------ | -----------
`-slug` | `string` | No, required | Product slug used for the root content folder
`-ga` | `string` | No, required | Version of the current docset
`-rc` | `string` | No, required | Version of the unreleased docset
`-tag` | `string` | "" | String used to tag non-GA docsets (e.g., "(rc)")
`-branch` | `string` | `main` | Name of the GA branch
`-date` | `string` | null | Local override date in "YYYY-MM-DD HH:MM:SS" format for the commit date cutoff
`-update` | `bool` | false | Indicates whether to apply any safe changes locally
`-pr` | `bool` | false | Indicates whether to apply any safe changes locally and generate a PR if possible
`-merged` | `bool` | false | Indicates that RC docs are merged to `-branch`
`-help` | `bool` | false | Print usage help text and exit



## Usage

```text
node sync-ga-to-rc.mjs -slug <product> -ga <ga_version> -rc <rc_version> [-tag <folder_tag>] [-branch <ga_branch>] [-date <override_date] [-update] [-pr]
```

## Examples

### Basic call

```shell-session
$ node sync-ga-to-rc.mjs -slug vault -ga 1.20.x -rc 1.21.x -tag rc
```

### Provide an override date

Use `-tag` to provide a specific doc tag and set a custom override date with
`-date`:

```shell-session
$ node sync-ga-to-rc.mjs \
-slug sentinel \
-ga 1.40.x \
-rc 1.41.x \
-tag beta \
-date '2025-07-31 17:10:27'
```

### Sync GA to RC in main

Use `-merged` to sync GA and a published RC docset in `main`:

```shell-session
$ node sync-ga-to-rc.mjs \
-slug vault \
-ga 1.20.x \
-rc 1.21.x \
-merged
```

### Sync two published versions

Use `-merged` and set `-tag` to "none" so the script compares folders for past
versions in `main`:

```shell-session
$ node sync-ga-to-rc.mjs \
-slug vault \
-ga 1.19.x \
-rc 1.20.x \
-merged \
-tag none
```

## Adding exceptions

If you have files you **know** the script should always ignore, you can add the
relative path from your product root to the exclusion file `data/exclude.json`
using your product slug.

Expected schema:

```json
[
{
"<produc_slug>": [
"<relative_path_1>",
"<relative_path_1>",
...
"<relative_path_N>",
]
}
]
```

For example:

```json
[
{
"vault": [
"/content/docs/updates/important-changes.mdx",
"/content/docs/updates/release-notes.mdx",
"/content/docs/updates/change-tracker.mdx"
]
}
]
```

## General workflow

The script syncs the local GA and RC branches and creates a new branch off of
the RC branch to work from.

Next, the script builds the following file sets:

- exclusions - a list of files the script should ignore during the sync
- GAΔ - files in the GA (current) docset with a last commit date later
than the provided cutoff date.
- RCΔ - files in the RC (unreleased) docset with a last commit date
later than the provided cutoff date.
- GA-only - files in the GA (current) docset that do not exist in the RC
docset.

The script determines what to do with the files based on the following rubric
where GAu and RCu are the set of files unchanged since the cutoff in the GA and
RC docsets:

Set definition | Implication | Action
-------------------- | ------------------ | -------------------------
file ∈ { RCu ∧ GAu } | file unchanged | ignore
file ∈ { RCu ∧ GAΔ } | updated in GA only | safe to update in RC
file ∈ { RCΔ ∧ GAu } | updated in RC only | ignore
file ∈ { RCΔ ∧ GAΔ } | updated in both | possible conflict; needs manual review
file ∈ { RC ∧ !GA } | new file for RC | ignore
file ∈ { !RC ∧ GA } | new file for GA | safe to update in RC

If `-update` is `true`, the script slams files in the RC folder with files from
the GA folder with any file deemed "safe", prints a note to review the
information in the conflict file, and updates the last run date.

If `-update` is `false`, the script generates log files and exits.


## Script output

The script is designed to be chatty and print details of the run to `stdout`,
but it also creates the following artifacts:

- A new branch off of the RC branch called `bot/{product}-ga-to-rc-sync`
- A product record file (`data/product-records/last-run-{product}.txt`) with
the most recent run time.
- Local output files with the following file sets for human review if needed:

File set | Output file
------------------- | --------------------
GAΔ | output/ga-delta.txt
RCΔ | output/rc-delta.txt
GA-only | output/ga-only.txt
updated files | output/safe-list.txt
potential conflicts | output/manual-review.txt
52 changes: 52 additions & 0 deletions scripts/sync-ga-to-rc/bash-helpers/create-pr.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
#
# ------------------------------------------------------------------------------
#
# Create GitHub PR -- Currently disabled
#
# Create a PR with the local changes
#
# Expected usage: create-pr.sh <produc> <rcFolder> <rcBranch> <prBranch>
# Example: create-pr.sh 'vault' 'v1.21.x (rc)' 'vault/1.21.x' 'bot/vault-ga-to-rc-sync-20251002'

exit # PR creation currently buggy

# Pull in the common variable definitions
currDir="$(dirname "$0")"
. "${currDir}/definitions.sh"

# Set variables from command line argument
productKey="${1}" # product slug
rcFolder="${2}" # RC doc folder
rcBranch="${3}" # git branch name for RC docs
prSource="${4}" # git branch name for the local changes
ghcli=$(which gh) # Check for the GitHub CLI

# Bail if any of the command line parameters were omitted
if [[ -z ${productKey} ]] ; then echo "!!! Missing product key" ; exit ; fi
if [[ -z ${rcFolder} ]] ; then echo "!!! Missing RC folder name" ; exit ; fi
if [[ -z ${rcBranch} ]] ; then echo "!!! Missing RC branch name" ; exit ; fi
if [[ -z ${prSource} ]] ; then echo "!!! Missing PR branch name" ; exit ; fi

# Bail if the GitHub CLI needs to be installed
if [[ -z ${rcBranch} ]] ; then
echo "Could not create PR. Please install the GitHub CLI (gh)"
exit
fi

cd "${repoRoot}"

# Add any files updated under the RC directory
git add "content/${productKey}/${rcFolder}/*" > /dev/null 2>&1
git commit -m "Auto update GA to RC sync" > /dev/null 2>&1
git push -u origin ${prSource} > /dev/null 2>&1

# Create the draft PR
gh pr create \
--title "${prTitle/"<PRODUCT>"/${productKey}}" \
--body "${prBody/"<PRODUCT>"/${productKey}}" \
--head "${prSource}" \
--base "${rcBranch}" \
--draft
26 changes: 26 additions & 0 deletions scripts/sync-ga-to-rc/bash-helpers/definitions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
#
# ------------------------------------------------------------------------------
#
# Common values used by the bash helper files

#myDir=$(pwd)
myDir="/home/goblin/repos/web-unified-docs/" # temp line for testing
localReposDir=${myDir%"/web-unified-docs"*}

repoRoot="${localReposDir}/web-unified-docs" # Local root directory of the repo
docRoot="${repoRoot}/content/<PRODUCT>" # Root directory of product docs
rcTag=" (rc)"
betaTag=" (beta)"

gaBranch="" # Set in helper from command line arguments; expected to be "main"
rcBranch="" # Set in helper from command line arguments; for example, "vault/1.21.x"
rcDocs="" # Set in helper from command line arguments; for example, "${docRoot}/v1.21.x"
gaDocs="" # Set in helper from command line arguments; for example, "${docRoot}/v1.20.x"

jsonTemplate='{"file": "<FILENAME>", "shortname": "<SHORTNAME>", "commit": "<COMMIT>"}'
prBranch="bot/<PRODUCT>-ga-to-rc-sync-$(date +%Y%m%d)"
prTitle="<PRODUCT> GA to RC auto-sync"
prBody="Draft PR created by \`sync-ga-to-rc.mjs\` to push recent GA updates to the RC release branch for <PRODUCT>"
49 changes: 49 additions & 0 deletions scripts/sync-ga-to-rc/bash-helpers/get-cutoff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
#
# ------------------------------------------------------------------------------
#
# Get branch creation date
#
# Query the git logs to find the creation date (or oldest commit) for the target
# branch.
#
# Expected usage: get-cutoff.sh <targetBranch>
# Example: get-cutoff.sh vault/1.21.x

# Pull in the common variable definitions
currDir="$(dirname "$0")"
. "${currDir}/definitions.sh"

# Set variables from command line argument
targetBranch="${1}" # git branch name for RC docs

# Bail if any of the command line parameters were omitted
if [[ -z ${targetBranch} ]] ; then exit ; fi

cd "${repoRoot}"

if [[ "${targetBranch}" == "main" ]] ; then
# Find the earliest commit we can as the "creation" date; since git log
# entries expire based on the setting for reflogexpire on the repo/branch
branchDate=$(
git log \
--pretty=format:%ad \
--date=iso \
--date=format:'%Y-%m-%d %H:%M:%S' \
"${targetBranch}" \
| tail -1
)
else
branchDate=$(
git reflog \
--grep-reflog="Created from" \
--pretty=format:%ad \
--date=iso \
--date=format:'%Y-%m-%d %H:%M:%S' \
"${targetBranch}"
)
fi

echo "${branchDate}"
51 changes: 51 additions & 0 deletions scripts/sync-ga-to-rc/bash-helpers/get-file-delta.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
#
# ------------------------------------------------------------------------------
#
# Get file delta
#
# Look through every file in the target folder and check if the latest commit is
# after the cutoff. If so, echo the details so the script can add it to the
# result array
#
# Expected usage: get-file-delta.sh <product> <targetFolder> <cutoff>
# Example: get-file-delta.sh vault vault/1.20.x '2025-10-01 12:34:21'

# Pull in the common variable definitions
currDir="$(dirname "$0")"
. "${currDir}/definitions.sh"

# Set variables from command line argument
productKey="${1}" # product slug
verFolder="${2}" # folder for GA docs
cutoff="${3}" # cutoff date for commit comparison

# Bail if any of the command line parameters were omitted
if [[ -z "${productKey}" ]] ; then exit ; fi
if [[ -z "${verFolder}" ]] ; then exit ; fi
if [[ -z "${cutoff}" ]] ; then exit ; fi

# Set the absolute path to the local folder
docFolder="${docRoot/'<PRODUCT>'/"${productKey}"}/"${verFolder}""

cd "${repoRoot}"

# Loop through each file in the version folder
IFS=$'\n'
for file in $(find "${docFolder}" -type f); do

lastCommit=$(
git log -1 --format=%ai "${file}" |
cut -d " " -f1,2
)
# If the last commit happened after the cutoff, add it to the results
if [[ "${cutoff}" < "${lastCommit}" ]]; then
shortName=${file/"${docFolder}"/""}
jsonString=${jsonTemplate/'<FILENAME>'/"${file}"}
jsonString=${jsonString/'<SHORTNAME>'/"${shortName}"}
jsonString=${jsonString/'<COMMIT>'/"${lastCommit}"}
echo ${jsonString}
fi
done
Loading
Loading