Skip to content

Improve helper object api (#2745) #3960

Improve helper object api (#2745)

Improve helper object api (#2745) #3960

name: Check for Broken Links and Publish Wiki
'on':
push:
branches: [ main ]
pull_request:
types: [ opened, synchronize, reopened ]
branches: [ main ]
workflow_dispatch:
workflow_run:
workflows: ["Release Action"]
branches: [ main ]
types:
- completed
schedule:
# every monday at night
- cron: '0 1 * * 1'
env:
PUBLISH: ${{ github.event_name == 'workflow_dispatch'
|| (github.event_name == 'push' && github.ref == 'refs/heads/main')
|| (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') }}
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 65
if: ${{ !(github.event_name == 'push' && startsWith(github.event.head_commit.message, '[release:'))
&& (github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success') }}
outputs:
publish: ${{ steps.gate.outputs.publish }}
steps:
- name: "🚦 Publish Gate"
id: gate
run: echo "publish=$PUBLISH" >> "$GITHUB_OUTPUT"
- name: "🛒 Checkout Repository"
uses: actions/checkout@v6
with:
submodules: true
lfs: ${{ env.PUBLISH == 'true' }}
token: ${{ secrets.RELEASE_TOKEN }}
fetch-tags: true
fetch-depth: 0
ref: ${{ github.event_name == 'workflow_run' && 'main' || github.sha }}
- name: "🌍 Load Versions to Use"
run: bash .github/workflows/scripts/global-configuration.sh
- name: "⬇️ Use Node.js"
uses: actions/setup-node@v6
with:
node-version: ${{ env.ACTION_NODE_VERSION }}
registry-url: "https://registry.npmjs.org/"
cache: npm
- name: "⬇️ Setup R"
uses: ./.github/actions/setup-r
timeout-minutes: 45
with:
r-version: ${{ env.ACTION_R_VERSION }}
- name: "📦 Install R Packages"
uses: r-lib/actions/setup-r-dependencies@v2
with:
cache: 'true'
cache-version: '1'
extra-packages: |
any::dplyr
any::readr
- name: "🛒 Checkout LFS"
if: ${{ success() && env.PUBLISH == 'true' }}
# just as a time-proven safety measure
run: git lfs checkout
- name: "⚗️ Test Suite (full, for data)"
# we make sure we have a clean run
run: |
rm -rf "coverage/"
rm -f "/tmp/flowr-label-summary.json"
bash .github/workflows/scripts/run-flowr-command.sh "test:full -- --allowOnly=false"
- name: "💾 Cache the Signature Database Bundles"
uses: actions/cache@v4
with:
# only the bundles, the shards unpacked next to them are re-derived on demand
path: ~/.cache/flowr/sigdb/bundles
key: sigdb-bundles-${{ hashFiles('src/data/sigdb/sigdb.remote.json') }}
- name: "⬇️ Sync the Signature Database"
# without it every sigdb-backed page says "No signature database is loaded" instead of showing a result
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run sync:sigdb
# the release workflow produces the data but does not commit it, so it lands in the same commit as the pages
- name: "⬇️ Get the Benchmark Data from the Release Run"
if: ${{ github.event_name == 'workflow_run' }}
uses: actions/download-artifact@v4
continue-on-error: true
with:
name: benchmark-data
path: wiki/stats/benchmark/
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.RELEASE_TOKEN }}
- name: "🛠️ Update the Generated Pages"
run: |
# npm ci is done by the full test run before
CHANGED_ANY=false
CHANGED_FILES=()
# Generate all wiki pages
npm run wiki
# the landing page is generated too, from real flowR output; the signature browser is megabytes,
# so the documentation job builds that one straight onto gh-pages instead
FLOWR_LANDING_ONLY=1 npm run gen:landing
# the signature browser is gitignored on purpose (megabytes); only the page and its samples are committed
for LANDING in index.html wiki/landing/index.html; do
if [ -n "$(git status --porcelain -- "$LANDING")" ]; then CHANGED_FILES+=("$LANDING"); fi
done
# read in everything from /tmp/flowr-wiki-changed-files.txt and add to CHANGED_FILES
if [ -f /tmp/flowr-wiki-changed-files.txt ]; then
while IFS= read -r line; do
CHANGED_FILES+=("$line")
done < /tmp/flowr-wiki-changed-files.txt
fi
if [ ${#CHANGED_FILES[@]} -gt 0 ]; then
echo "The following Wiki pages were changed:"
for file in "${CHANGED_FILES[@]}"; do
echo "- $file"
git add -f "$file"
done
CHANGED_ANY=true
echo "CHANGED=true" >> $GITHUB_ENV
else
echo "No Wiki pages were changed!"
fi
echo "====== Benchmark Data (if any) ======"
if [ -n "$(git status --porcelain -- wiki/stats/benchmark/)" ]; then
echo "New benchmark data!"
git add -f wiki/stats/benchmark/
CHANGED_ANY=true
echo "CHANGED=true" >> $GITHUB_ENV
CHANGED_FILES+=("benchmark-data")
else
echo "No new benchmark data!"
fi
echo "====== Producing Update (if necessary) ======"
# check for an update in the versions! (including the updated name information)
LIMIT_TO_LAST=8
ALL_TAGS=$( git tag --list --format="%(tag) (%(creatordate))" | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' | sed -E 's/^(v[0-9]+\.[0-9]+\.[0-9]+)\s*\(([a-zA-Z]+\s*)([a-zA-Z]+)\s*([0-9]+)\s*[0-9:]+\s+([0-9]+).*/\1 (\3 \4, \5)/' | sort -V | tail -n$LIMIT_TO_LAST)
NUM_OF_TAGS=$(echo "$ALL_TAGS" | wc -l)
LATEST_TAG=$(echo "$ALL_TAGS" | tail -n1)
# add (latest) to the latest tag
ALL_TAGS=$(sed -E "s/^$LATEST_TAG$/$LATEST_TAG (latest)/" <<< "$ALL_TAGS")
ALL_TAGS=$(echo -e "<Older> (please consider updating)\n$ALL_TAGS")
ALL_TAGS+="\nUnknown\nUnreleased/Dev"
ALL_TAGS=$(echo -e "$ALL_TAGS" | sed -E 's/^(.*)$/ - \1/')
cp .github/ISSUE_TEMPLATE/bug-report.yaml .github/ISSUE_TEMPLATE/bug-report.yaml.tmp
sed -i -E "/\s*# START::Versions/,/\s*# END::Versions/c\\# START::Versions\n$(echo "$ALL_TAGS" | sed 's/$/\\/' ) \n# END::Versions" .github/ISSUE_TEMPLATE/bug-report.yaml
sed -i -E "/\s*# START::DefaultVersion/,/\s*# END::DefaultVersion/c\\# START::DefaultVersion\n default: $((NUM_OF_TAGS))\n# END::DefaultVersion" .github/ISSUE_TEMPLATE/bug-report.yaml
if ! diff -q .github/ISSUE_TEMPLATE/bug-report.yaml .github/ISSUE_TEMPLATE/bug-report.yaml.tmp; then
echo "Versions in the bug-report changed!"
echo "CHANGED=true" >> $GITHUB_ENV
CHANGED_ANY=true
git add -f .github/ISSUE_TEMPLATE/bug-report.yaml
CHANGED_FILES+=("bug-report.yaml")
else
echo "Versions in bug-report did not change!"
fi
echo "====== Make Commit (if necessary) ======"
if [ $CHANGED_ANY == "true" ]; then
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git config lfs.allowincompletepush true
CHANGED_FILES_STRING=$(IFS=,; echo "${CHANGED_FILES[*]}")
git commit -m "[skip ci] doc: update generated wiki pages ($CHANGED_FILES_STRING)"
fi
- name: "⬆️ Push changed Wiki pages"
if: ${{ success() && env.CHANGED == 'true' && env.PUBLISH == 'true' }}
run: |
git fetch origin main
git rebase --autostash origin/main
git push origin HEAD:main
- name: "⬆️ Publish the Wiki"
uses: Andrew-Chen-Wang/github-wiki-action@v4.4.0
# We do not need to republish if nothing changes. Furthermore, do not publish on PR as this should be done by the push on main!
if: ${{ success() && env.PUBLISH == 'true' }}
with:
path: "wiki/"
strategy: 'init'
token: ${{ secrets.GH_DEPLOY_WIKI }}
ignore: |
**/*.md.tmp
**/*-Old.*
- name: "🔎 Check the Wiki pages for broken external links"
uses: becheran/mlc@v1.2.0
# thousands of links against a rate-limiting host can stall for hours; the step is allowed to
# fail, so hitting the limit only ends the check, it never ends the run
timeout-minutes: 20
continue-on-error: true
if: ${{ success() && env.PUBLISH == 'true' }}
with:
args: >-
--throttle 25 --ignore-path "wiki/stats"
--do-not-warn-for-redirect-to "http*://github.com/flowr-analysis/*,http*://flowr-analysis.github.io/*"
--ignore-links "http*://hub.docker.com/r/*,http*://*npmjs.com/*,http*://doi.org/*,http*://*linkedin.com/*,http*://*r-bloggers.com/*,http*://*gnu.org/*,http*://linux.die.net/*,http*://mermaid.js.org/*"
wiki/
- name: "🔎 Check the README for broken external links"
uses: becheran/mlc@v1.2.0
timeout-minutes: 5
continue-on-error: true
if: ${{ success() && env.PUBLISH == 'true' }}
with:
args: >-
--throttle 25 --files "./README.md"
--do-not-warn-for-redirect-to "http*://github.com/flowr-analysis/*,http*://flowr-analysis.github.io/*"
--ignore-links "http*://hub.docker.com/r/*,http*://*npmjs.com/*,http*://doi.org/*,http*://*linkedin.com/*,http*://*r-bloggers.com/*,http*://*gnu.org/*,http*://linux.die.net/*,http*://mermaid.js.org/*"
deploy-doc:
name: "🚀 Build and Deploy Documentation"
needs: [ build ]
if: ${{ needs.build.outputs.publish == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 65
steps:
- name: "🛒 Checkout Repository"
uses: actions/checkout@v6
with:
token: ${{ secrets.DOCUMENTATION_TOKEN }}
submodules: true
lfs: true
ref: main
# the version marker compares HEAD against the release tag, which a shallow clone without tags cannot do
fetch-tags: true
fetch-depth: 0
- name: "🌍 Load Versions to Use"
run: bash .github/workflows/scripts/global-configuration.sh
- name: "⬇️ Use Node.js"
uses: actions/setup-node@v6
with:
node-version: ${{ env.ACTION_NODE_VERSION }}
registry-url: "https://registry.npmjs.org/"
cache: npm
- name: "⬇️ Setup R"
uses: ./.github/actions/setup-r
timeout-minutes: 45
with:
r-version: ${{ env.ACTION_R_VERSION }}
- name: "📦 Install R Packages"
uses: r-lib/actions/setup-r-dependencies@v2
timeout-minutes: 20
with:
cache: 'true'
cache-version: '1'
extra-packages: |
any::dplyr
any::readr
- name: "📦 Install Node Dependencies"
# the sync below runs through ts-node, so the dependencies have to be there before it
run: npm ci
- name: "💾 Cache the Signature Database Bundles"
uses: actions/cache@v4
with:
# only the bundles, the shards unpacked next to them are re-derived on demand
path: ~/.cache/flowr/sigdb/bundles
key: sigdb-bundles-${{ hashFiles('src/data/sigdb/sigdb.remote.json') }}
- name: "⬇️ Sync the Signature Database"
# the signature browser is built from it; without the shards it is skipped and the page 404s
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run sync:sigdb
- name: "🛠️ Build the documentation"
run: bash .github/workflows/scripts/run-flowr-command.sh doc
- name: "⬆️ Push New Documentation"
run: git push --force origin HEAD:gh-pages