Skip to content
Merged
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
62 changes: 54 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
name: CI

on:
push:
branches:
# Runs on merge to main only
- main
pull_request:

# Cancel in-progress PR runs; let main builds finish so the Nx Cloud cache is populated.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# Needed for nx-set-shas when run on the main branch
permissions:
actions: read
Expand All @@ -31,29 +37,69 @@ jobs:
# the PR is supposed to remove. nrwl/nx-set-shas still computes the correct
# affected range from git history. Falls back to github.sha for push events.
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check for code changes
id: changes
shell: bash
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
else
BASE="${{ github.event.before }}"
HEAD="${{ github.sha }}"
fi
Comment thread
ThePlenkov marked this conversation as resolved.

if [ "$BASE" = "0000000000000000000000000000000000000000" ]; then
echo "has-code-changes=true" >> "$GITHUB_OUTPUT"
exit 0
fi

if git diff --name-only "$BASE" "$HEAD" | grep -qvE '^website/|^docs/|\.md$|^netlify\.toml$'; then
Comment thread
ThePlenkov marked this conversation as resolved.
echo "has-code-changes=true" >> "$GITHUB_OUTPUT"
else
echo "has-code-changes=false" >> "$GITHUB_OUTPUT"
echo "Skipping heavy CI steps: only docs/website/markdown files changed."
fi
Comment thread
ThePlenkov marked this conversation as resolved.
- uses: actions/setup-node@v7
if: steps.changes.outputs.has-code-changes == 'true'
with:
node-version: lts/*
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
if: steps.changes.outputs.has-code-changes == 'true'
with:
bun-version: 1.3.14
- name: Cache bun dependencies
if: steps.changes.outputs.has-code-changes == 'true'
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
# This line enables distribution
# The "--stop-agents-after" is optional, but allows idle agents to shut down once the "e2e-ci" targets have been requested
# - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="e2e-ci"
- run: bun install
if: steps.changes.outputs.has-code-changes == 'true'

- uses: nrwl/nx-set-shas@afb73a62d26e41464e9254689e1fd6122ee683c1 # v5
if: steps.changes.outputs.has-code-changes == 'true'

- name: Test tool-call hook scripts
if: steps.changes.outputs.has-code-changes == 'true'
run: bash .github/hooks/scripts/test-hooks.sh
shell: bash
- name: Print Environment Info
run: npx nx report
if: steps.changes.outputs.has-code-changes == 'true'
run: ./node_modules/.bin/nx report
shell: bash
- run: npx nx format:check
- run: npx nx affected -t lint test build e2e-ci --verbose=false --parallel=3
if: ${{ env.NX_CLOUD_ACCESS_TOKEN != '' }}
- run: npx nx affected -t lint test build e2e-ci --verbose=false --parallel=3 --no-cloud
if: ${{ env.NX_CLOUD_ACCESS_TOKEN == '' }}
- run: npx nx-cloud fix-ci
if: always() && env.NX_CLOUD_ACCESS_TOKEN != ''
- name: Check formatting for changed files
Comment thread
ThePlenkov marked this conversation as resolved.
if: steps.changes.outputs.has-code-changes == 'true'
run: ./node_modules/.bin/nx format:check --base=$NX_BASE --head=$NX_HEAD
- name: Run affected lint, test, build and e2e-ci
if: ${{ env.NX_CLOUD_ACCESS_TOKEN != '' && steps.changes.outputs.has-code-changes == 'true' }}
run: ./node_modules/.bin/nx affected -t lint test build e2e-ci --verbose=false --parallel=3
- run: ./node_modules/.bin/nx affected -t lint test build e2e-ci --verbose=false --parallel=3 --no-cloud
if: ${{ env.NX_CLOUD_ACCESS_TOKEN == '' && steps.changes.outputs.has-code-changes == 'true' }}
- run: ./node_modules/.bin/nx-cloud fix-ci
if: always() && env.NX_CLOUD_ACCESS_TOKEN != '' && steps.changes.outputs.has-code-changes == 'true'
12 changes: 12 additions & 0 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ on:
paths:
- .github/workflows/copilot-setup-steps.yml

# Cancel any in-progress runs for the same workflow/ref combination.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
# Must be named exactly `copilot-setup-steps` for Copilot to pick it up.
copilot-setup-steps:
Expand All @@ -25,6 +30,13 @@ jobs:
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.14
- name: Cache bun dependencies
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-

- name: Install dependencies
run: bun install
Expand Down
8 changes: 7 additions & 1 deletion netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
# which gets reported as a red `error` state + failing GitHub commit
# status. Exit 0 from this command tells Netlify to skip the deploy
# (reported as `skipped`, not `error`). Exit 1 means "proceed".
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- . ../netlify.toml"
#
# The script runs from the `website/` base directory. It checks the
# Netlify cached diff first, then falls back to the GitHub API for
# pull requests so deploy previews are skipped unless the PR actually
# touches the docs site or `netlify.toml`. For private repos, set the
# `GITHUB_TOKEN` environment variable in the Netlify UI.
ignore = "node ./scripts/netlify-ignore.cjs"

[build.environment]
NODE_VERSION = "20"
Expand Down
142 changes: 142 additions & 0 deletions website/scripts/netlify-ignore.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env node
/**
* Netlify ignore script for the Docusaurus site under website/.
*
* Exit 0 -> skip the build (no deploy preview/production build needed).
* Exit 1 -> run the build (website/ or netlify.toml changed).
*
* The script is executed by Netlify from the `website/` base directory.
*/

const { spawnSync } = require('node:child_process');
const https = require('node:https');

const CONTEXT = process.env.CONTEXT || '';
const IS_PULL_REQUEST = process.env.PULL_REQUEST === 'true';
const REVIEW_ID = process.env.REVIEW_ID || '';
const COMMIT_REF = process.env.COMMIT_REF || '';
const CACHED_COMMIT_REF = process.env.CACHED_COMMIT_REF || '';
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';

const REPO = 'abapify/adt-cli';

function hasWebsiteRelevantPath(filePath) {
if (!filePath) return false;
return filePath === 'netlify.toml' || filePath.startsWith('website/');
}

function gitDiffChangedFiles(refA, refB) {
const result = spawnSync(
'/usr/bin/git',
['diff', '--name-only', refA, refB, '--', '.', '../netlify.toml'],
{
cwd: process.cwd(),
encoding: 'utf8',
env: { ...process.env, PATH: '/usr/local/bin:/usr/bin:/bin' },
shell: false,
},
);
if (result.error || result.status !== 0) {
return null;
}
return result.stdout.trim().split('\n').filter(Boolean);
}

function fetchJson(url) {
return new Promise((resolve, reject) => {
const options = { headers: { 'User-Agent': 'netlify-ignore-script' } };
if (GITHUB_TOKEN) {
options.headers.Authorization = `token ${GITHUB_TOKEN}`;
}
https
.get(url, options, (res) => {
if (res.statusCode !== 200) {
res.resume();
reject(new Error(`GitHub API returned ${res.statusCode}`));
return;
}
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (err) {
reject(err);
}
});
})
.on('error', reject);
});
}

async function prTouchesWebsite() {
const reviewId = Number(REVIEW_ID);
if (!Number.isInteger(reviewId) || reviewId <= 0) {
throw new Error(`Invalid REVIEW_ID: ${REVIEW_ID}`);
}
const pageSize = 100;
let page = 1;
while (true) {
const files = await fetchJson(
`https://api.github.com/repos/${REPO}/pulls/${reviewId}/files?per_page=${pageSize}&page=${page}`,
);
if (!Array.isArray(files) || files.length === 0) return false;
for (const file of files) {
if (hasWebsiteRelevantPath(file.filename)) return true;
}
if (files.length < pageSize) return false;
page += 1;
}
}

async function main() {
// Fast path: we have two distinct cached refs. If nothing in website/ or
// netlify.toml changed, skip the build.
if (COMMIT_REF && CACHED_COMMIT_REF && CACHED_COMMIT_REF !== COMMIT_REF) {
const files = gitDiffChangedFiles(CACHED_COMMIT_REF, COMMIT_REF);
if (files?.length > 0) {
console.log(
`Building: ${files.length} change(s) in website/ or netlify.toml`,
);
process.exit(1);
}
if (files?.length === 0) {
console.log(
'Skipping: no website/ or netlify.toml changes since the last build',
);
process.exit(0);
}
// git diff failed; fall through to PR API or default build.
}
Comment thread
ThePlenkov marked this conversation as resolved.

// For pull/merge request previews, ask GitHub which files changed.
if ((IS_PULL_REQUEST || CONTEXT === 'deploy-preview') && REVIEW_ID) {
try {
const touches = await prTouchesWebsite();
if (!touches) {
console.log(
'Skipping deploy preview: PR does not touch website/ or netlify.toml',
);
process.exit(0);
}
console.log(
'Building deploy preview: PR touches website/ or netlify.toml',
);
process.exit(1);
} catch (err) {
// If we cannot determine the changed files, build to be safe.
console.warn(
`Could not determine PR changed files (${err.message}); building to be safe`,
);
process.exit(1);
}
}

// Production / branch-deploy without useful cached refs or PR info: build.
console.log('Building: no cached diff or PR information available');
process.exit(1);
}
Comment thread
ThePlenkov marked this conversation as resolved.

main();
Loading