From 43a36ceed1806b1c951a233c2b048ab9d089f69f Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Wed, 29 Jul 2026 17:07:27 +0100 Subject: [PATCH 1/2] Run the link check against the local build, not a Vercel preview The broken-links workflow asked the Vercel API for a PR preview URL and crawled that. With Vercel deprecated in favour of GitHub Pages there is no preview deployment, so the job pointed linkinator at a hostname that no longer resolves. Fold the check into the existing PR build job instead: linkinator serves ./build on an ephemeral localhost port, so it needs no deployed environment and reuses the build we already do rather than paying for a second one. Also fix the links:check script, which could never have worked against a local build - it skipped ".*localhost.*", and serving ./build locally makes every internal link a localhost URL, so the whole check was a no-op. Dropping that skip surfaces internal links properly. Externally, linkinator probes with HEAD and falls back to GET, but keeps the HEAD status if that GET exceeds --timeout; at the default concurrency of 100 this reported healthy links as 400/404. Lower the concurrency, raise the timeout, and treat 5xx as noise alongside 403 and 0 so the check does not go red on someone else's outage. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/check-links.yml | 123 ------------------------------ .github/workflows/pr.yml | 77 +++++++++++++++++++ package.json | 2 +- 3 files changed, 78 insertions(+), 124 deletions(-) delete mode 100644 .github/workflows/check-links.yml diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml deleted file mode 100644 index b563e538a..000000000 --- a/.github/workflows/check-links.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Check - broken links - -on: - pull_request: - types: [opened, reopened, synchronize] - workflow_dispatch: # Allows manual triggering from the GitHub UI - -permissions: - contents: read - pull-requests: write - issues: write - -jobs: - link-check: - runs-on: ubuntu-latest - timeout-minutes: 30 - if: github.event_name != 'pull_request' || github.actor != 'codatbot' - steps: - - name: Checkout code - uses: actions/checkout@v6 - - - name: Set up Node.js - uses: actions/setup-node@v6 - with: - node-version: '20' - - - name: Install linkinator - run: npm install -g linkinator - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Get Vercel Preview URL - id: get-preview-url - if: github.event_name == 'pull_request' - run: | - # Wait a bit for Vercel deployment to be ready - sleep 30 - - # Try to get the preview URL from Vercel API - if [ -n "${{ secrets.VERCEL_TOKEN }}" ]; then - # Get the latest deployment for this PR - DEPLOYMENT=$(curl -s -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \ - "https://api.vercel.com/v6/deployments?teamId=${{ secrets.VERCEL_TEAM_ID }}&projectId=${{ secrets.VERCEL_PROJECT_ID }}&gitSource.pullRequestId=${{ github.event.pull_request.number }}" \ - | jq -r '.deployments[0].url // empty') - - if [ -n "$DEPLOYMENT" ]; then - echo "preview_url=https://$DEPLOYMENT" >> $GITHUB_OUTPUT - else - # Fallback: construct the preview URL using the typical Vercel pattern - BRANCH_NAME=$(echo "${{ github.head_ref }}" | sed 's/[^a-zA-Z0-9]/-/g') - echo "preview_url=https://codat-docs-git-${BRANCH_NAME}-codat.vercel.app" >> $GITHUB_OUTPUT - fi - else - # Fallback: construct the preview URL using the typical Vercel pattern - BRANCH_NAME=$(echo "${{ github.head_ref }}" | sed 's/[^a-zA-Z0-9]/-/g') - echo "preview_url=https://codat-docs-git-${BRANCH_NAME}-codat.vercel.app" >> $GITHUB_OUTPUT - fi - - - name: Run linkinator - run: | - # Function to run linkinator with a given URL - run_linkinator() { - local url=$1 - npx linkinator "$url" --recurse --verbosity error --skip ".*github.*|.*localhost.*" --format json --timeout 10000 --delay 1000 > link-results.json - } - - if [ "${{ github.event_name }}" == "pull_request" ]; then - # Use preview URL for PRs - run_linkinator "${{ steps.get-preview-url.outputs.preview_url }}" - else - # Use main docs site for manual runs - run_linkinator "https://docs.codat.io" - fi - continue-on-error: true # Ensure the workflow continues even if linkinator finds broken links - - - name: Delete Previous Comments - uses: actions/github-script@v8 - if: github.event_name == 'pull_request' - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const issue_number = context.issue.number; - const owner = context.repo.owner; - const repo = context.repo.repo; - const comments = await github.rest.issues.listComments({ - issue_number, - owner, - repo, - }); - const actionComments = comments.data.filter(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('Link check results')); - for (const comment of actionComments) { - await github.rest.issues.deleteComment({ - owner, - repo, - comment_id: comment.id, - }); - } - - - name: Filter and Post Results - uses: actions/github-script@v8 - if: github.event_name == 'pull_request' - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const fs = require('fs'); - const results = JSON.parse(fs.readFileSync('link-results.json', 'utf8')); - const filtered = results.links - .filter(link => link.status !== 403 && link.status !== 500 && link.status !== 0) - - const printList = filtered - .map(link => `[${link.status}] ${link.url}`); - const previewUrl = '${{ steps.get-preview-url.outputs.preview_url }}'; - const output = `Link check results for preview deployment (${previewUrl}):\n\`\`\`\n${JSON.stringify(printList, null, 2)}\n\`\`\``; - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: output, - }); - - if (filtered.length > 0) { - core.setFailed("There are broken links in the documentation."); - } diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index df28d3e48..45aaf931f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -2,10 +2,17 @@ name: PR build on: pull_request: + workflow_dispatch: # Allows manual triggering from the GitHub UI + +permissions: + contents: read + pull-requests: write + issues: write jobs: build: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - uses: actions/checkout@v7 with: @@ -32,3 +39,73 @@ jobs: GTM_ID: ${{ vars.GTM_ID }} FEATURE_DEV_FLAG: ${{ vars.FEATURE_DEV_FLAG }} FEATURE_NEW_PRODUCTS_FLAG: ${{ vars.FEATURE_NEW_PRODUCTS_FLAG }} + + # Link checking reuses the build above: linkinator serves ./build on an + # ephemeral localhost port, so no deployed preview environment is needed. + # --silent keeps npm's banner out of the JSON on stdout. + - name: Check for broken links + if: github.actor != 'codatbot' + run: npm run --silent links:check -- --format json > link-results.json + continue-on-error: true # Broken links are reported below, not here + + - name: Delete previous link check comments + uses: actions/github-script@v8 + if: github.event_name == 'pull_request' && github.actor != 'codatbot' + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const issue_number = context.issue.number; + const owner = context.repo.owner; + const repo = context.repo.repo; + const comments = await github.rest.issues.listComments({ + issue_number, + owner, + repo, + }); + const actionComments = comments.data.filter(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('Link check results')); + for (const comment of actionComments) { + await github.rest.issues.deleteComment({ + owner, + repo, + comment_id: comment.id, + }); + } + + - name: Post link check results + uses: actions/github-script@v8 + if: github.actor != 'codatbot' + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const fs = require('fs'); + if (!fs.existsSync('link-results.json') || fs.statSync('link-results.json').size === 0) { + core.setFailed("The link check did not produce any results — see the 'Check for broken links' step."); + return; + } + // --verbosity error means the JSON only contains broken links, so + // filter out the statuses that bot protection (403) and flaky or + // briefly-down external hosts (0, 5xx) return rather than genuine + // link rot. Without this the check goes red on other people's + // outages. + const results = JSON.parse(fs.readFileSync('link-results.json', 'utf8')); + const isNoise = status => status === 0 || status === 403 || status >= 500; + const filtered = results.links.filter(link => !isNoise(link.status)) + + const printList = filtered + .map(link => `[${link.status}] ${link.url}`); + + if (context.eventName === 'pull_request') { + const output = `Link check results:\n\`\`\`\n${JSON.stringify(printList, null, 2)}\n\`\`\``; + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output, + }); + } else { + core.info(printList.join('\n')); + } + + if (filtered.length > 0) { + core.setFailed("There are broken links in the documentation."); + } diff --git a/package.json b/package.json index f30199def..c192e4f21 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "format:js:check": "prettier --check \"**/*.{js,jsx,ts,tsx}\"", "format:mdx": "prettier --write \"**/*.{md,mdx}\"", "format:mdx:check": "prettier --check \"**/*.{md,mdx}\"", - "links:check": "linkinator ./build --recurse --verbosity error --skip \".*github.*|.*localhost.*\"", + "links:check": "linkinator ./build --recurse --verbosity error --timeout 20000 --concurrency 25 --retry --skip \".*github.*\"", "vale:sync": "vale sync", "vale": "vale docs/", "vale:check": "vale --minAlertLevel=warning docs/", From 3f30a0ad3bc828804055fa45704cd5a3e96a69ef Mon Sep 17 00:00:00 2001 From: pmckinney-codat Date: Wed, 29 Jul 2026 17:07:39 +0100 Subject: [PATCH 2/2] Fix broken outbound links surfaced by the link check The first working run of the link check against a real build found six broken links that had accumulated while the Vercel-based job was pointing at a dead hostname. - Intuit developer portal and QuickBooks FR notice: dead URLs, replaced or unlinked - Data Coverage Explorer: knowledge.codat.io 404s, so drop the changelog entry and the post that existed only to link to it - Consent journey Figma prototype: no longer accessible - codat-docs.vercel.app self-link in a changelog post: now relative - Zoho: point at the current sign-in URL Co-Authored-By: Claude Opus 5 (1M context) --- blog/220715-data-coverage-explorer.md | 12 ------------ blog/220817-product-update.md | 1 - blog/230115-dashboard.md | 2 +- .../tech-implementation/consent-journey.md | 8 -------- .../quickbooksonline/accounting-quickbooksonline.md | 2 +- .../quickbooksonline/faq-quickbooks-online.md | 2 +- .../zoho-books/accounting-zohobooks-setup.md | 2 +- 7 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 blog/220715-data-coverage-explorer.md diff --git a/blog/220715-data-coverage-explorer.md b/blog/220715-data-coverage-explorer.md deleted file mode 100644 index b695634f1..000000000 --- a/blog/220715-data-coverage-explorer.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Use our new Data Coverage Explorer to view and compare our data coverage across all supported accounting, commerce, and banking platforms. -date: "2022-07-15" -tags: ["API", "Resource"] -draft: false -authors: mcclowes -link: "https://knowledge.codat.io/supported-features/accounting?integrationKey=gbol" ---- - -[Read more...](https://knowledge.codat.io/supported-features/accounting?integrationKey=gbol) - - diff --git a/blog/220817-product-update.md b/blog/220817-product-update.md index 390f4a6e7..9ef732aeb 100644 --- a/blog/220817-product-update.md +++ b/blog/220817-product-update.md @@ -16,7 +16,6 @@ We've got a host of Portal and DX improvements for you this week. - **Update:** We've [updated the navigation bar](https://docs.codat.io/updates/220806-navigation-bar) in the Codat Portal to make your experience more intuitive. - **New:** We've added a Support button in the navigation bar for quick access to Codat's [support resources](https://codat.zendesk.com/hc/en-gb) -- **New:** Use our new Data Coverage Explorer to view and compare our data coverage across all supported accounting, commerce, and banking platforms. https://knowledge.codat.io/supported-features/accounting?integrationKey=gbol #### Assess diff --git a/blog/230115-dashboard.md b/blog/230115-dashboard.md index dec14eef3..4a56b5ed1 100644 --- a/blog/230115-dashboard.md +++ b/blog/230115-dashboard.md @@ -12,7 +12,7 @@ The Portal homepage dashboard now shows how many data connections in each connec This is to better highlight your those connections that need your attention, like those that have de-authorized. -[Read more about connection statuses](https://codat-docs.vercel.app/core-concepts/connections). +[Read more about connection statuses](/core-concepts/connections). The Portal dashboard used to combined multiple connection statuses into single data points, making it hard to get a detailed picture of the health of your companies and their data connections. diff --git a/docs/enterprise/tech-implementation/consent-journey.md b/docs/enterprise/tech-implementation/consent-journey.md index 06b358317..3f78fae5a 100644 --- a/docs/enterprise/tech-implementation/consent-journey.md +++ b/docs/enterprise/tech-implementation/consent-journey.md @@ -16,14 +16,6 @@ To address this, Codat provides you with Link, our consent and authorization jou ![](/img/enterprise/implementation/consent/authjourney.png) -:::info Sample consent journey - -We prepared a consent journey prototype using an example business insights dashboard use case. - -[View the prototype in full screen →](https://www.figma.com/proto/YWkKvsYgeHJskPsfuIpy7w/Codat---Generic-bank---Consent-Journey?page-id=601%3A4488&type=design&node-id=641-11421&viewport=1275%2C-2886%2C0.1&t=rrDznIIhmQ8EayyY-1&scaling=contain&starting-point-node-id=641%3A11421&mode=design) - -::: - ### Implementation options Codat offers two options to implement the Link solution in your application: diff --git a/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md b/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md index 373cf1514..4f57821d6 100644 --- a/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md +++ b/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md @@ -40,7 +40,7 @@ Each tier is allocated a monthly limit of CorePlus API calls, and exceeding this To avoid unexpected costs, track your app's usage of API calls following these steps: -1. Log in to the [Intuit Developer Portal](https://developer.intuit.com/dashboard?tab=apps). +1. Log in to the [Intuit Developer Portal](https://accounts.intuit.com/app/sign-in). 2. Navigate to the _App dashboard_ and select the app you want to review. 3. In the left-hand menu, click **Analytics**. 4. In _Analytics_, toggle the environment to **Production**. You will see a chart detailing your Core and CorePlus usage. diff --git a/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md b/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md index a556e5f33..b57837b9e 100644 --- a/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md +++ b/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md @@ -35,7 +35,7 @@ When the Options endpoint is called for one of these data types and an AST-enabl ## Is QuickBooks Online (FR) supported? :::caution QBO no longer available in France -Note that QuickBooks Online (FR) will no longer be available after December 31st, 2023, as communicated by [Intuit](https://quickbooks.intuit.com/learn-support/fr-fr/help-article/account-management/faq/L5GgPEpLf_FR_fr_FR). +Note that QuickBooks Online (FR) will no longer be available after December 31st, 2023, as communicated by Intuit. ::: Yes. Codat supports the same functionality for QuickBooks Online France (FR) as for QuickBooks Online UK and US. diff --git a/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md b/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md index f491d451f..c4a5a15ef 100644 --- a/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md +++ b/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md @@ -35,7 +35,7 @@ If you already have a Zoho Books account, have your account details to hand. If To register your Zoho Books application. -1. Go to [https://api-console.zoho.com/](https://api-console.zoho.com/) and sign in to Zoho Books. If you are using your account for the first time, select **GET STARTED**. +1. Go to [https://accounts.zoho.com/signin](https://accounts.zoho.com/signin) and sign in to Zoho Books. If you are using your account for the first time, select **GET STARTED**. The **Developer Console** is displayed. If the **Applications** screen is displayed instead, from the top-left corner, select **ADD CLIENT**. 2. Select **Server-based Applications**. The **Create New Client** page is displayed.