Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
19 changes: 14 additions & 5 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,17 @@ jobs:
mkdir -p "$folder"
echo "📁 Moving contents from $temp_dir to $folder..."

# Copy all files except .git directory
find "$temp_dir" -mindepth 1 -maxdepth 1 ! -name '.git' -exec cp -r {} "$folder/" \;
# Copy documentation content only, excluding development/build files
# Excluded: .git, scripts/, branches-config.json, .github/, README.md, .gitignore, .devcontainer/
find "$temp_dir" -mindepth 1 -maxdepth 1 \
! -name '.git' \
! -name 'scripts' \
! -name 'branches-config.json' \
! -name '.github' \
! -name 'README.md' \
! -name '.gitignore' \
! -name '.devcontainer' \
-exec cp -r {} "$folder/" \;

# Clean up temp directory
rm -rf "$temp_dir"
Expand All @@ -186,13 +195,13 @@ jobs:

# Run the merge script with branches config
if [ -n "$SUBFOLDER" ]; then
python3 merge_docs_configs.py \
python3 scripts/merge_docs_configs.py \
--branches-config branches-config.json \
--base-dir . \
--subfolder "$SUBFOLDER" \
--output docs.json
else
python3 merge_docs_configs.py \
python3 scripts/merge_docs_configs.py \
--branches-config branches-config.json \
--base-dir . \
--output docs.json
Expand Down Expand Up @@ -294,4 +303,4 @@ jobs:
fi

echo "" >> $GITHUB_STEP_SUMMARY
echo "**Timestamp:** $(date)" >> $GITHUB_STEP_SUMMARY
echo "**Timestamp:** $(date)" >> $GITHUB_STEP_SUMMARY
166 changes: 166 additions & 0 deletions .github/workflows/release-bot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
name: Cherry-pick to Release Branch

on:
issue_comment:
types: [created]
workflow_call:

jobs:
cherry_pick:
runs-on: ubuntu-latest
steps:
- name: Check for release command
id: check_command
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const { issue, comment } = context.payload;
if (!issue || !issue.pull_request || !comment || !comment.body.startsWith('/release to ')) {
core.setOutput('release_valid', 'false');
return;
}
const releaseBranch = comment.body.split('/release to ')[1].trim();
core.setOutput('release_valid', 'true');
core.setOutput('release_branch', releaseBranch);
core.setOutput('pr_number', issue.number);

- name: Checkout repository
if: steps.check_command.outputs.release_valid == 'true'
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set default branch variable
if: steps.check_command.outputs.release_valid == 'true'
run: |
echo "DEFAULT_BRANCH=${{ github.event.repository.default_branch }}" >> $GITHUB_ENV

- name: Skip jobs if not a valid release command
if: steps.check_command.outputs.release_valid == 'false'
run: |
echo "Skipping cherry-pick as the release command is not valid."
continue-on-error: true

- name: Setup Git
if: steps.check_command.outputs.release_valid == 'true'
run: |
git config --global user.email "[email protected]"
git config --global user.name "Tyk Bot"

- name: Get PR details
id: pr_details
if: steps.check_command.outputs.release_valid == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_DATA=$(gh pr view ${{ steps.check_command.outputs.pr_number }} --json headRefOid)
COMMIT_SHA=$(echo $PR_DATA | jq -r .headRefOid)
echo "COMMIT_SHA=${COMMIT_SHA}" >> $GITHUB_OUTPUT

- name: Clone repository and cherry-pick commit
if: steps.check_command.outputs.release_valid == 'true'
id: cherry_pick
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPO: ${{ github.repository }}
GITHUB_BRANCH: ${{ steps.check_command.outputs.release_branch }}
GITHUB_CHERRY_PICK_COMMIT: ${{ steps.pr_details.outputs.COMMIT_SHA }}
run: |
# Clone the repository
export FOLDER=$(echo $GITHUB_REPO | cut -d '/' -f2)
rm -rf $FOLDER
git clone https://x-access-token:[email protected]/$GITHUB_REPO || true
cd $FOLDER

# Reset and checkout default branch (master or main)
git reset --hard
git checkout $DEFAULT_BRANCH
git pull
git checkout $GITHUB_BRANCH
git reset --hard
git pull

# Delete old local branch if it exists
git branch -D merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT 2>/dev/null || true

# Delete old remote branch if it exists
REMOTE_EXISTS=$(git ls-remote --heads origin merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT | wc -l)
if [ "$REMOTE_EXISTS" -gt 0 ]; then
git push origin --delete merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT || true
else
echo "No remote branch to delete"
fi

# Create and checkout the new branch
git checkout -b merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT

# Cherry-pick the commit
MERGE_FAILED=0
git cherry-pick -x $GITHUB_CHERRY_PICK_COMMIT || MERGE_FAILED=$?

# If the cherry-pick failed, resolve conflicts
if ! [ $MERGE_FAILED -eq 0 ]; then
git add -A && git -c core.editor=true cherry-pick --continue --no-edit || true
fi

echo "Push the new branch"
git push origin merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT --force || true

echo "Prepare the message and title for the PR"
MESSAGE=$(git log --format=%B -n 1 $GITHUB_CHERRY_PICK_COMMIT)
TITLE=$(git log --format=%B -n 1 $GITHUB_CHERRY_PICK_COMMIT | head -n 1)

echo "Check GH token"
echo $GITHUB_TOKEN || gh auth login --with-token

echo "Create the PR"
PR_URL=
if ! [ $MERGE_FAILED -eq 0 ]; then
PR_URL=$(gh pr create --draft --title "Merging to $GITHUB_BRANCH: $TITLE" --body "$MESSAGE" --repo $GITHUB_REPO --base $GITHUB_BRANCH --head merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT)
else
PR_URL=$(gh pr create --title "Merging to $GITHUB_BRANCH: $TITLE" --body "$MESSAGE" --repo $GITHUB_REPO --base $GITHUB_BRANCH --head merge/$GITHUB_BRANCH/$GITHUB_CHERRY_PICK_COMMIT)
fi

echo "$PR_URL"
PR_ID="${PR_URL##*/}"
echo "$PR_ID"

echo "If no merge failure, auto-merge the PR"
if [ $MERGE_FAILED -eq 0 ]; then
gh pr merge --squash "$PR_ID" --auto --subject "Merging to $GITHUB_BRANCH: $TITLE" --body "$MESSAGE" || echo "Auto-merge failed or not allowed, continuing..."
fi

echo "Set outputs for use in the next step"
echo "PR_URL=${PR_URL}" >> $GITHUB_OUTPUT
echo "MERGE_FAILED=${MERGE_FAILED}" >> $GITHUB_OUTPUT

- name: Comment on PR
if: steps.check_command.outputs.release_valid == 'true' && always()
uses: actions/github-script@v6
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const prUrl = '${{ steps.cherry_pick.outputs.PR_URL }}';
const mergeFailed = '${{ steps.cherry_pick.outputs.MERGE_FAILED }}' === '1';
let body;

if ('${{ job.status }}' === 'success') {
if (mergeFailed) {
body = `⚠️ Cherry-pick operation completed with conflicts. A draft pull request has been created: ${prUrl}\n\nPlease resolve the conflicts manually.`;
} else {
body = `✅ Cherry-pick operation completed successfully. New pull request created: ${prUrl}`;
}
} else {
body = '❌ Cherry-pick operation failed. Please check the action logs for more information.';
}

const owner = context.repo.owner || '${{ github.repository_owner }}';
const repo = context.repo.repo || '${{ github.event.repository.name }}';

github.rest.issues.createComment({
issue_number: ${{ steps.check_command.outputs.pr_number }},
owner: owner,
repo: repo,
body: body
});
104 changes: 104 additions & 0 deletions .github/workflows/release-to-branches-with-label.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: On Pull Request Merged to Master

on:
pull_request:
# Only trigger on pull requests targeting main/master
branches:
- master
- main
types:
- closed

jobs:
run-on-pr-merged:
runs-on: ubuntu-latest

# Only run if the PR was actually merged
if: ${{ github.event.pull_request.merged == true }}

steps:
- name: Add a comment to the merged PR (only if labeler is in the org)
uses: actions/github-script@v7
with:
github-token: ${{ secrets.ORG_GH_TOKEN }}
script: |
// 1. The label format: e.g., "release-1", "release-1.0"
const labelRegex = /^release-[0-9]+(\.[0-9]+)?$/;

// 2. Get PR info
const pullRequestNumber = context.payload.pull_request.number;
const labelsOnPR = context.payload.pull_request.labels || [];
console.log("Labels on the Pull Request:", labelsOnPR.map(label => label.name));
// 3. Get all timeline events to see who labeled the PR
const { data: prEvents } = await github.rest.issues.listEvents({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequestNumber
});

// 4. Filter down to "labeled" events
const labeledEvents = prEvents.filter(ev => ev.event === 'labeled');
console.log("Labeled Events:",labeledEvents.map(event => ({
label: event.label?.name,
user: event.actor?.login,
timestamp: event.created_at
})));
// 5. Build a map: labelName -> last user who added it
// (We reverse to get the *most recent* labeler, if a label was added multiple times)
const labelToLastLabeler = {};
for (const event of labeledEvents.reverse()) {
const labelName = event.label?.name;
const userName = event.actor?.login;
if (labelName && userName && !labelToLastLabeler[labelName]) {
labelToLastLabeler[labelName] = userName;
}
}

// 6. For each label on the PR, check if it matches "release-.."
// If yes, we see who labeled it last and check their membership
for (const label of labelsOnPR) {
if (labelRegex.test(label.name)) {
const userWhoAddedLabel = labelToLastLabeler[label.name];

// If there's no recorded user (edge case), skip
if (!userWhoAddedLabel) {
console.log(`User not found for label: ${label.name}`);
continue;
}

// 7. Check if the user is in the org
let isMember = false;
try {
await github.rest.orgs.checkMembershipForUser({
org: 'TykTechnologies',
username: userWhoAddedLabel
});
// If this call succeeds, they're a member
isMember = true;
console.log(`User '${userWhoAddedLabel}' is a member of the organization '${'TykTechnologies'}'.`);
} catch (error) {
// If 404, user is not a member. Anything else is an unexpected error.
if (error.status === 404) {
console.log(`User '${userWhoAddedLabel}' is NOT a member of the organization '${'TykTechnologies'}'.`);
}else {
console.error(`An error occurred while checking membership for user '${userWhoAddedLabel}':`, error);
throw error;
}
}

// 8. Comment only if user is in the org
if (isMember) {
console.log(`Creating comment for label '${label.name}' on PR #${pullRequestNumber} by user '${userWhoAddedLabel}'.`);
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequestNumber,
body: `/release to ${label.name}`
});
}else{
console.log(`No comment created for label '${label.name}' on PR #${pullRequestNumber} because the user '${userWhoAddedLabel}' is not a member of the organization 'TykTechnologies'.`);
}
}else{
console.log(`Label '${label.name}' does not match the expected format.`);
}
}
33 changes: 33 additions & 0 deletions .github/workflows/validate-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Validate Documentation

on:
pull_request:

jobs:
validate:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
# No external dependencies needed for our validation script

- name: Validate documentation
run: |
echo "🔍 Running documentation validation..."
python scripts/check_broken_links.py . --validate-redirects --verbose

- name: Validation complete
if: success()
run: |
echo "✅ Documentation validation passed!"
echo "All links, images, navigation, and redirects are valid."
Loading