diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5b3d59838f4..c41c480705b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -186,4 +186,4 @@ .github @aws-amplify/documentation-team #Protected Content -/src/protected @reesscot @srquinn21 @Milan-Shah @swaminator +/src/protected @reesscot @srquinn21 @swaminator diff --git a/.github/workflows/accessibility_scan.yml b/.github/workflows/accessibility_scan.yml new file mode 100644 index 00000000000..933fd2f136f --- /dev/null +++ b/.github/workflows/accessibility_scan.yml @@ -0,0 +1,46 @@ +name: Accessibility Scan +on: + pull_request: + branches: [main] + types: [opened, synchronize] +env: + BUILD_DIR: 'client/www/next-build' +jobs: + accessibility: + name: Runs accessibility scan on changed pages + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Setup Node.js 20 + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: 20.x + - name: Install dependencies + run: yarn + - name: Build + run: yarn build + env: + NODE_OPTIONS: --max_old_space_size=4096 + - name: Get changed/new pages to run accessibility tests on + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + id: pages-to-a11y-test + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { getChangedPages } = require('./.github/workflows/scripts/check_for_changed_pages.js'); + const buildDir = process.env.BUILD_DIR; + return getChangedPages({github, context, buildDir}); + - name: Run site + run: | + python -m http.server 3000 -d ${{ env.BUILD_DIR }} & + sleep 5 + - name: Run accessibility tests on changed/new MDX pages + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + id: axeResults + with: + result-encoding: string + script: | + const { runAccessibilityScan } = require('./.github/workflows/scripts/run_accessibility_scan.js'); + const pages = ${{ steps.pages-to-a11y-test.outputs.result }} + return await runAccessibilityScan(pages) diff --git a/.github/workflows/add_deleted_assets_label.yml b/.github/workflows/add_deleted_assets_label.yml index 30298646ea0..b636360befb 100644 --- a/.github/workflows/add_deleted_assets_label.yml +++ b/.github/workflows/add_deleted_assets_label.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write # used to add label steps: - name: Checkout repository to get the workflow scripts - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Download artifact uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: diff --git a/.github/workflows/add_redirects_label.yml b/.github/workflows/add_redirects_label.yml index 898634cdf48..3a369b8cfdd 100644 --- a/.github/workflows/add_redirects_label.yml +++ b/.github/workflows/add_redirects_label.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write # used to add label steps: - name: Checkout repository to get the workflow scripts - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Download artifact uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 env: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eca805b26e7..93863b37da8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Setup Node.js 20.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/check_bundle_size.yml b/.github/workflows/check_bundle_size.yml index aa2cdd7d29d..745ee002efa 100644 --- a/.github/workflows/check_bundle_size.yml +++ b/.github/workflows/check_bundle_size.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout main branch - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: main - name: Setup Node.js 20 @@ -34,7 +34,7 @@ jobs: const { checkBundleSize } = require('./.github/workflows/scripts/check_bundle_size.js'); return checkBundleSize(); - name: Checkout PR branch - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 with: ref: ${{ github.head_ref }} - name: Setup Node.js 20 diff --git a/.github/workflows/check_for_broken_links.yml b/.github/workflows/check_for_broken_links.yml index 162f3f9cf53..f03377ccafc 100644 --- a/.github/workflows/check_for_broken_links.yml +++ b/.github/workflows/check_for_broken_links.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Setup Node.js 20 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -32,7 +32,7 @@ jobs: role-to-assume: arn:aws:iam::464149486631:role/github_action_read_slack_webhook_url aws-region: us-west-2 - name: Read secrets from AWS Secrets Manager into environment variables - uses: aws-actions/aws-secretsmanager-get-secrets@f91b2a3e784edce744f972af1685eca7e24d2302 # v2.0.2 + uses: aws-actions/aws-secretsmanager-get-secrets@ff26a0aa6bd4dd5e51326b5afb3f5f6874c958c7 # v2.0.3 with: secret-ids: | SLACK_WEBHOOK_URL diff --git a/.github/workflows/check_for_console_errors.yml b/.github/workflows/check_for_console_errors.yml index d87d91f958d..3615ac34632 100644 --- a/.github/workflows/check_for_console_errors.yml +++ b/.github/workflows/check_for_console_errors.yml @@ -3,6 +3,8 @@ on: pull_request: branches: [main] types: [opened, synchronize] +env: + BUILD_DIR: 'client/www/next-build' permissions: contents: read jobs: @@ -10,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Setup Node.js 20.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: @@ -23,7 +25,7 @@ jobs: NODE_OPTIONS: --max_old_space_size=4096 - name: Run Server run: | - python -m http.server 3000 -d ${{ vars.BUILD_DIR }} & + python -m http.server 3000 -d ${{ env.BUILD_DIR }} & sleep 5 - name: Run Console Errors id: consoleErrors diff --git a/.github/workflows/check_for_deleted_assets.yml b/.github/workflows/check_for_deleted_assets.yml index b7ddc58b913..860a48bcd15 100644 --- a/.github/workflows/check_for_deleted_assets.yml +++ b/.github/workflows/check_for_deleted_assets.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository to get the workflow scripts - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Get count of deleted files uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 id: set-deleted-files-count diff --git a/.github/workflows/check_for_new_files.yml b/.github/workflows/check_for_new_files.yml index e021ba6c199..66c55cde326 100644 --- a/.github/workflows/check_for_new_files.yml +++ b/.github/workflows/check_for_new_files.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Get count of added files uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 id: set-added-files-count diff --git a/.github/workflows/check_for_new_fragments.yml b/.github/workflows/check_for_new_fragments.yml index 461b544093e..8b413dd4548 100644 --- a/.github/workflows/check_for_new_fragments.yml +++ b/.github/workflows/check_for_new_fragments.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Check if there are new fragments uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 id: new-fragments-boolean diff --git a/.github/workflows/check_for_redirects.yml b/.github/workflows/check_for_redirects.yml index a5e5f28c9dd..5f157560e99 100644 --- a/.github/workflows/check_for_redirects.yml +++ b/.github/workflows/check_for_redirects.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository to get the workflow scripts - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Get count of deleted files uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 id: set-deleted-files-count diff --git a/.github/workflows/check_pr_for_broken_links.yml b/.github/workflows/check_pr_for_broken_links.yml index b3dbdbe1ac9..24a9f7e6f38 100644 --- a/.github/workflows/check_pr_for_broken_links.yml +++ b/.github/workflows/check_pr_for_broken_links.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Setup Node.js 20 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1e6b77f7fd2..addc62f0570 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,37 +1,93 @@ -name: 'CodeQL' +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" on: + push: + branches: [ "main" ] pull_request: - types: [opened, synchronize] + branches: [ "main" ] + schedule: + - cron: '25 15 * * 2' + jobs: - CodeQL-Build: - name: CodeQL Build - runs-on: ubuntu-latest + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories actions: read contents: read - security-events: write strategy: fail-fast: false matrix: - language: ['javascript'] - + include: + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - with: - # Minimal depth 2 so we can checkout the commit before possible merge commit. - fetch-depth: 2 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - config-file: ./.github/codeql/codeql-config.yml - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: '/language:${{matrix.language}}' + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scripts/check_for_changed_pages.js b/.github/workflows/scripts/check_for_changed_pages.js new file mode 100644 index 00000000000..bb2d41fb534 --- /dev/null +++ b/.github/workflows/scripts/check_for_changed_pages.js @@ -0,0 +1,74 @@ +module.exports = { + getChangedPages: async ({ github, context, buildDir }) => { + const fs = require('fs'); + const cheerio = require('cheerio'); + + const urlList = []; + + const { + issue: { number: issue_number }, + repo: { owner, repo } + } = context; + + const possiblePages = []; + const platforms = [ + 'android', + 'angular', + 'flutter', + 'javascript', + 'nextjs', + 'react', + 'react-native', + 'swift', + 'vue' + ]; + + const changedFiles = await github.paginate( + 'GET /repos/{owner}/{repo}/pulls/{pull_number}/files', + { owner, repo, pull_number: issue_number }, + (response) => + response.data.filter( + (file) => file.status === 'modified' || file.status === 'added' + ) + ); + + // Get only the changed files that are pages and build out the + // possiblePages array + changedFiles.forEach(({ filename }) => { + const isPage = + filename.startsWith('src/pages') && + (filename.endsWith('index.mdx') || filename.endsWith('index.tsx')); + if (isPage) { + const path = filename + .replace('src/pages', '') + .replace('/index.mdx', '') + .replace('/index.tsx', ''); + if (path.includes('[platform]')) { + platforms.forEach((platform) => { + possiblePages.push(path.replace('[platform]', platform)); + }); + } else { + possiblePages.push(path); + } + } + }); + + // Get the sitemap and parse for an array of site URLs + const siteMap = fs.readFileSync(`${buildDir}/sitemap.xml`); + + const siteMapParse = cheerio.load(siteMap, { + xml: true + }); + + siteMapParse('url').each(function () { + urlList.push(siteMapParse(this).find('loc').text()); + }); + + // Filter the possiblePages for only those that are part of the sitemap + const pages = possiblePages.filter((page) => + urlList.includes(`https://docs.amplify.aws${page}/`) + ); + + return pages; + } +}; diff --git a/.github/workflows/scripts/run_accessibility_scan.js b/.github/workflows/scripts/run_accessibility_scan.js new file mode 100644 index 00000000000..ce3692e2792 --- /dev/null +++ b/.github/workflows/scripts/run_accessibility_scan.js @@ -0,0 +1,81 @@ +module.exports = { + runAccessibilityScan: (pages) => { + const core = require('@actions/core'); + const { AxePuppeteer } = require('@axe-core/puppeteer'); + const puppeteer = require('puppeteer'); + + const violations = []; + + // When flipping from dark mode to light mode, we need to add a small timeout + // to account for css transitions otherwise there can be false contrast issues found. + // Usage: await sleep(300); + const sleep = ms => new Promise(res => setTimeout(res, ms)); + + const logViolation = (violation) => { + violation.nodes.forEach(node => { + console.log(node.failureSummary); + console.log(node.html); + node.target.forEach( target => { + console.log('CSS target: ', target) + }) + console.log('\n'); + }) + + } + + async function runAxeAnalyze(pages) { + for (const page of pages) { + const browser = await puppeteer.launch(); + const pageToVisit = await browser.newPage(); + await pageToVisit.goto(`http://localhost:3000${page}/`, {waitUntil: 'domcontentloaded'}); + await pageToVisit.click('button[title="Light mode"]'); + await pageToVisit.waitForSelector('[data-amplify-color-mode="light"]'); + await sleep(300); + + + try { + console.log(`\nTesting light mode: http://localhost:3000${page}/`) + const results = await new AxePuppeteer(pageToVisit).analyze(); + if(results.violations.length > 0) { + results.violations.forEach(violation => { + logViolation(violation); + violations.push(violation); + }) + } else { + console.log('No violations found. \n'); + } + + } catch (error) { + core.setFailed(`There was an error testing the page: ${error}`); + } + + await pageToVisit.click('button[title="Dark mode"]'); + await pageToVisit.waitForSelector('[data-amplify-color-mode="dark"]'); + await sleep(300); + + try { + console.log(`\nTesting dark mode: http://localhost:3000${page}/`) + const results = await new AxePuppeteer(pageToVisit).analyze(); + if(results.violations.length > 0) { + results.violations.forEach(violation => { + logViolation(violation); + violations.push(violation); + }) + } else { + console.log('No violations found. \n'); + } + + } catch (error) { + core.setFailed(`There was an error testing the page: ${error}`); + } + + await browser.close(); + } + if(violations.length > 0) { + core.setFailed(`Please fix the above accessibility violations.`); + } + } + + runAxeAnalyze(pages); + } +}; diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index 4724f09fd3d..088fdeb0045 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6 - name: Setup Node.js 20 uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/Readme.md b/Readme.md index 623de18b290..264b9fad14b 100644 --- a/Readme.md +++ b/Readme.md @@ -6,7 +6,7 @@ ### Prerequisites -- [Node.js 16.14.0 or later](https://nodejs.org/en/) +- [Node.js 18.17.0 or later, but below 22.0.0](https://nodejs.org/en/) - [Yarn classic](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) ## Set up the docs repo @@ -176,6 +176,21 @@ Videos can be added using the `