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
325 changes: 325 additions & 0 deletions .github/workflows/docs-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
# yamllint disable rule:line-length
---
name: Docs preview

# Renders the generated-docs diff (config docs, OAS/x-tyk-gateway, swagger)
# for a PR in a source repo, posts it as a sticky PR comment, and requests
# an ADVISORY review from a PM team. Never a required check.
#
# Generation steps mirror the tyk-docs sync:
# https://github.com/TykTechnologies/exp/blob/main/.github/workflows/tyk-docs.yml
# Keep them in sync until they are factored into shared composite actions.

on:
workflow_call:
inputs:
component:
description: 'Generator set to run: gateway, dashboard, pump, mdcb or portal'
required: true
type: string
docs_paths:
description: 'Newline-separated globs of doc-bearing paths; the workflow no-ops unless the PR touches one'
required: true
type: string
pm_team:
description: 'GitHub team slug to request as advisory reviewer (empty = comment only)'
required: false
default: ''
type: string
comment_marker:
description: 'Hidden marker identifying the sticky PR comment'
required: false
default: '<!-- docs-preview -->'
type: string
secrets:
PROBE_APP_ID:
required: true
PROBE_APP_PRIVATE_KEY:
required: true

permissions:
contents: read
pull-requests: write

jobs:
detect:
name: Detect docs-bearing changes
# Fork PRs: secrets are unavailable and the config generator needs remote branch names.
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
outputs:
docs_affected: ${{ steps.match.outputs.docs_affected }}
steps:
# No checkout: the PR files API is much cheaper than a full-history
# clone on large repos, and returns exactly the PR's own changes.
- name: Match changed files against docs paths
id: match
env:
GH_TOKEN: ${{ github.token }}
DOCS_PATHS: ${{ inputs.docs_paths }}
run: |
affected=false
changed=$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files" --paginate --jq '.[].filename')
while IFS= read -r file; do
[ -z "$file" ] && continue
while IFS= read -r pat; do
[ -z "$pat" ] && continue
# bash [[ == ]] pattern matching: * matches across '/', so ** and * behave the same here
if [[ "$file" == $pat ]]; then
echo "docs-bearing change: $file (pattern: $pat)"
affected=true
break 2
fi
done <<< "$DOCS_PATHS"
done <<< "$changed"
echo "docs_affected=$affected" >> "$GITHUB_OUTPUT"

preview:
name: Generate docs preview
needs: [detect]
if: needs.detect.outputs.docs_affected == 'true'
runs-on: ubuntu-latest
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
steps:
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
with:
app-id: ${{ secrets.PROBE_APP_ID }}
private-key: ${{ secrets.PROBE_APP_PRIVATE_KEY }}
owner: TykTechnologies

- name: Checkout head
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
path: head
fetch-depth: 1

- name: Checkout base
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.base.ref }}
path: base
fetch-depth: 1

- name: Prepare output directories
run: mkdir -p out-base out-head

- name: Set up Go
if: inputs.component == 'gateway'
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
with:
go-version: '1.21'

- name: Checkout exp (schema-gen + cleanup script)
if: inputs.component == 'gateway'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: TykTechnologies/exp
path: exp
fetch-depth: 1

- name: Install schema-gen
if: inputs.component == 'gateway'
run: cd exp/cmd/schema-gen && GOBIN=/usr/local/bin go install .

- name: Generate x-tyk-gateway docs (base and head)
if: inputs.component == 'gateway'
run: |
for side in base head; do
(
cd "$side/apidef/oas"
schema-gen extract -o - | schema-gen markdown -i - \
--root XTykAPIGateway \

Check warning on line 136 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: security

security Issue

The workflow checks out code from an external repository (`TykTechnologies/exp`) using a floating reference (the default branch). This creates a supply chain risk, as any new commit to this branch—including a malicious one—will be automatically executed. This issue also exists for the checkout of `TykTechnologies/tyk-config-info-generator` on line 255, which uses `ref: main`. All external actions and repository checkouts should be pinned to a specific, audited commit SHA.
Raw output
Pin the repository checkouts to a specific commit SHA. For the `TykTechnologies/exp` checkout, add a `ref` property with a full commit SHA. For the `TykTechnologies/tyk-config-info-generator` checkout, replace `ref: main` with a full commit SHA.
--skip "OldOAS,APIDef,OAS,TykExtensionConfigParams,ServerRegenerationConfig" \
--title "## Tyk vendor extension reference" \
--replace "tyktime.ReadableDuration=string,model.ObjectID=string,apidef.MiddlewareDriver=string,apidef.IdExtractorSource=string,apidef.IdExtractorType=string,apidef.RequestInputType=string,apidef.AuthTypeEnum=string,[]osin.AuthorizeRequestType=[]string" \
--heading-format "### **%s**" \
--trim "Tyk classic API definition" \
-o "$GITHUB_WORKSPACE/out-$side/x-tyk-gateway.mdx"
)
(
cd exp
python3 scripts/remove-oas-dublicates/index.py --input-file "$GITHUB_WORKSPACE/out-$side/x-tyk-gateway.mdx"
)
done

- name: Generate gateway swagger (base and head)
if: inputs.component == 'gateway'
run: |
massage() {
cp "$1" ./swagger-modified.yml
sed -i "s|'https://raw.githubusercontent.com/TykTechnologies/tyk/refs/heads/master/apidef/oas/schema/3.0.json'|'#/components/schemas/OpenAPI3Schema'|g" ./swagger-modified.yml
sed -i "s|https://raw.githubusercontent.com/TykTechnologies/tyk/refs/heads/master/apidef/oas/schema/3.0.json|'#/components/schemas/OpenAPI3Schema'|g" ./swagger-modified.yml
awk '
{print}
/schemas:/ && !done {
print " OpenAPI3Schema:"
print " type: object"
print " additionalProperties: true"
done=1
}
' ./swagger-modified.yml > "$2"
}
for side in base head; do
massage "$side/swagger.yml" "out-$side/gateway-swagger.yml"
done

- name: Generate dashboard docs (base and head)
if: inputs.component == 'dashboard'
run: |
massage() {
cp "$1" ./swagger-modified.yml
sed -i "s|'https://raw.githubusercontent.com/TykTechnologies/tyk/refs/heads/master/apidef/oas/schema/3.0.json'|'#/components/schemas/OpenAPI3Schema'|g" ./swagger-modified.yml
sed -i "s|https://raw.githubusercontent.com/TykTechnologies/tyk/refs/heads/master/apidef/oas/schema/3.0.json|'#/components/schemas/OpenAPI3Schema'|g" ./swagger-modified.yml
awk '
{print}
/schemas:/ && !done {
print " OpenAPI3Schema:"
print " type: object"
print " additionalProperties: true"
done=1
}
' ./swagger-modified.yml > "$2"
}
for side in base head; do
massage "$side/swagger.yml" "out-$side/dashboard-swagger.yml"
cp "$side/swagger-admin.yml" "out-$side/dashboard-admin-swagger.yml"
cp "$side/docs/opa/opa-rules.md" "out-$side/opa-rules.mdx"
done

- name: Generate MDCB swagger (base and head)
if: inputs.component == 'mdcb'
run: |
for side in base head; do
cp "$side/swagger.yml" "out-$side/mdcb-swagger.yml"
done

- name: Generate portal swagger (base and head)
if: inputs.component == 'portal'
run: |
for side in base head; do
cp "$side/docs/api/enterprise-developer-portal-swagger.yaml" "out-$side/enterprise-developer-portal-swagger.yaml"
done

- name: Checkout config info generator

Check warning on line 208 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: architecture

architecture Issue

The shell function `massage()` is defined identically in two separate `run` steps: "Generate gateway swagger" (lines 173-188) and "Generate dashboard docs" (lines 193-208). This duplication within the same file violates the DRY (Don't Repeat Yourself) principle and makes the script harder to maintain, as any changes must be applied in both places.
Raw output
Refactor the script to define the `massage()` function only once. You could combine the two swagger generation steps into one, or define the function in a way that it's available to both steps (e.g., by exporting it in a previous step).
if: contains(fromJSON('["gateway","dashboard","pump","mdcb"]'), inputs.component)
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

Check warning on line 210 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: quality

style Issue

The `massage()` shell function is defined identically in two separate steps: 'Generate gateway swagger' (line 173) and 'Generate dashboard docs' (line 194). This code duplication makes the workflow harder to maintain, as any changes to the function must be applied in both places.
Raw output
Define the `massage()` function once and reuse it. You could define it as a multi-line string in the job's `env` context and source it, or extract it to a separate script file that is called by both steps.
with:
repository: TykTechnologies/tyk-config-info-generator
token: ${{ steps.app-token.outputs.token }}
path: tyk-config-info-generator
ref: main

- name: Generate config docs (base and head)
if: contains(fromJSON('["gateway","dashboard","pump","mdcb"]'), inputs.component)
working-directory: ./tyk-config-info-generator/src
run: |
repo="${{ inputs.component }}"
for side in base head; do
if [ "$side" = "base" ]; then branch="$BASE_REF"; else branch="$HEAD_REF"; fi
sudo TOKEN=${{ steps.app-token.outputs.token }} node app.js "$repo:$branch"
cp "/node/home/tyk-config-info-generator/info/$branch/$repo.md" "$GITHUB_WORKSPACE/out-$side/$repo-config.mdx"
done

- name: Diff rendered docs
id: diff
run: |
set +e
diff -ruN out-base out-head > docs.diff
rc=$?
set -e
if [ "$rc" -gt 1 ]; then
echo "diff failed with exit code $rc" >&2
exit "$rc"
fi

Check warning on line 238 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: performance

performance Issue

The checkout of the `TykTechnologies/tyk-config-info-generator` repository does not use `fetch-depth: 1`. This causes the runner to download the full git history for the `main` branch, which is unnecessary for this workflow and increases execution time and network usage.
Raw output
Add `fetch-depth: 1` to the `with` block to perform a shallow clone, which only downloads the latest commit.
if [ "$rc" -eq 1 ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
wc -c docs.diff

- name: Upload full diff as artifact
if: steps.diff.outputs.changed == 'true'

Check warning on line 247 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: performance

performance Issue

The documentation generation for the `base` and `head` commits is performed sequentially in a series of `for side in base head; do ... done` loops. This makes the total generation time approximately the sum of the times for both commits, which can be slow and delay feedback on the pull request.
Raw output
To significantly reduce the wall-clock time of this workflow, the documentation generation for the `base` and `head` commits should be parallelized. This can be achieved by refactoring the `preview` job into three separate jobs:
1. A `generate-base` job that checks out the base ref, generates its docs, and uploads the result as an artifact.
2. A `generate-head` job that runs in parallel with `generate-base`, checking out the head ref, generating its docs, and uploading its result as an artifact.
3. A final `diff-and-comment` job that depends on the completion of both generation jobs, downloads their artifacts, computes the diff, and posts the comment.
This change would reduce the generation time from `T(base) + T(head)` to `max(T(base), T(head))`.
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: docs-preview-diff
path: docs.diff

Check failure on line 251 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: quality

security Issue

The `node app.js` command is executed with `sudo`, granting it root privileges. Running applications, especially those with their own dependencies like a Node.js app, with elevated privileges is a security risk and violates the principle of least privilege.
Raw output
Investigate why `sudo` is necessary and remove it. If it's for writing to a directory with restrictive permissions, adjust the directory's ownership or permissions beforehand (e.g., with `chown`) so the script can run as the default, non-privileged user.

- name: Build comment body

Check failure on line 253 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: quality

architecture Issue

The documentation generation logic is duplicated from the `TykTechnologies/exp` repository's `tyk-docs.yml` workflow. This creates a significant maintenance burden, as any changes to the generation process must be manually synchronized between both locations, risking divergence between the preview and the final output.
Raw output
As noted in the PR description, this duplicated logic should be extracted into shared composite actions that can be consumed by both this preview workflow and the main `tyk-docs.yml` sync workflow. This should be treated as a high-priority follow-up to ensure a single source of truth for documentation generation.
env:
MARKER: ${{ inputs.comment_marker }}
PM_TEAM: ${{ inputs.pm_team }}
COMPONENT: ${{ inputs.component }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
CHANGED: ${{ steps.diff.outputs.changed }}
run: |

Check failure on line 260 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: security

security Issue

The workflow executes a Node.js script using `sudo`, granting it root privileges on the runner. The script is sourced from an external repository (`TykTechnologies/tyk-config-info-generator`) which is not pinned to a specific commit. If that repository's `main` branch is compromised, an attacker could achieve remote code execution with root privileges, compromising the runner and potentially exfiltrating secrets.
Raw output
Remove the `sudo` command. The Node.js script should be modified to write its output to a directory within the standard `${{ github.workspace }}` path, which is writable by the default runner user. Avoid writing to system-level or home directories that require elevated permissions.
if [ "$CHANGED" != "true" ]; then
{
echo "$MARKER"
echo "## 📚 Documentation preview"
echo
echo "No documentation impact — the latest changes do not alter generated docs."
} > comment-body.md
exit 0
fi
limit=60000
size=$(wc -c < docs.diff)
{
echo "$MARKER"
echo "## 📚 Documentation preview"
echo
echo "This PR changes generated documentation (component: \`$COMPONENT\`)."
if [ -n "$PM_TEAM" ]; then
echo
echo "cc @TykTechnologies/$PM_TEAM — advisory review requested; this does **not** block merging."
fi
echo
echo '<details><summary>Rendered docs diff</summary>'
echo
echo '````diff'
head -c "$limit" docs.diff
echo
echo '````'
echo
echo '</details>'
if [ "$size" -gt "$limit" ]; then
echo
echo "_Diff truncated (${size} bytes total). Full diff: \`docs-preview-diff\` artifact on the [workflow run]($RUN_URL)._"
fi
} > comment-body.md

- name: Find existing preview comment
id: fc
uses: peter-evans/find-comment@a54c31d7fa095754bfef525c0c8e5e5674c4b4b1 # v2
with:
issue-number: ${{ github.event.pull_request.number }}
body-includes: ${{ inputs.comment_marker }}

# Post when there is a docs diff; when there is none, only update an
# existing comment (a PR that never had docs impact stays silent).
- name: Create or update preview comment
if: steps.diff.outputs.changed == 'true' || steps.fc.outputs.comment-id != ''
uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: comment-body.md
edit-mode: replace

Check warning on line 312 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: quality

style Issue

The workflow contains several complex, multi-line shell scripts inline within the YAML file (e.g., in the 'Match changed files', 'Generate gateway swagger', and 'Build comment body' steps). This reduces the readability of the workflow file and makes the scripts difficult to test and maintain independently.
Raw output
Extract these complex scripts into separate, executable files (e.g., in a `.github/scripts/` directory). The workflow steps can then simply call these scripts. This improves readability, enables independent testing and linting of the scripts, and separates logic from orchestration.

# Advisory only, requested once per PR: on the first push that produces
# a docs diff (no pre-existing sticky comment). Uses the App token —
# GITHUB_TOKEN cannot request org team reviews.
- name: Request PM team review (advisory)
if: steps.diff.outputs.changed == 'true' && steps.fc.outputs.comment-id == '' && inputs.pm_team != ''
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
PM_TEAM: ${{ inputs.pm_team }}
run: |
jq -n --arg team "$PM_TEAM" '{team_reviewers: [$team]}' \
| gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/requested_reviewers" --input - \
|| echo "::warning::Could not request review from team '$PM_TEAM' — check the slug and that the PROBE App can write PRs in this repo"

Check failure on line 325 in .github/workflows/docs-preview.yml

View check run for this annotation

probelabs / Visor: architecture

architecture Issue

The `preview` job implements a monolithic design, embedding component-specific logic for five different products (`gateway`, `dashboard`, etc.) directly within the workflow, controlled by numerous `if` conditions. Furthermore, the PR description confirms this complex logic is a verbatim copy from another repository's workflow (`exp/tyk-docs.yml`). This approach introduces significant architectural issues: it violates the Single Responsibility Principle, making the workflow difficult to maintain and extend, and the cross-repository duplication creates major technical debt, requiring manual synchronization of any future changes.
Raw output
To mitigate the technical debt and create a more scalable design, the component-specific generation logic should be extracted into shared, reusable composite actions, as mentioned in the PR description. This workflow should be refactored to be a pure orchestrator that calls these actions. This architectural refactoring should be prioritized to avoid solidifying a fragile, duplicated implementation.
Loading
Loading