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
2,072 changes: 18 additions & 2,054 deletions .circleci/config.yml

Large diffs are not rendered by default.

2,054 changes: 2,054 additions & 0 deletions .circleci/continue_config.yml

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions .circleci/continue_config_light.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 2.1

# Lightweight continuation for non-code PRs (CLI-1625).
orbs:
prodsec: snyk/prodsec-orb@1

workflows:
test_and_release:
jobs:
- prodsec/secrets-scan:
name: secrets-scan
context: snyk-bot-slack
channel: cli-alerts
trusted-branch: main
32 changes: 32 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,38 @@ Your PR checks will run every time you push changes to your branch.
The [test pipeline](https://app.circleci.com/pipelines/github/snyk/cli?filter=mine) is on CircleCI. This is where your
changes are built and tested.

A short **setup** job runs first. It compares your branch to `main` and continues one of two configs:

| Path | When | `test_and_release` runs |
| ---- | ---- | ----------------------- |
| **Full** | Any changed file matches a [code path](#what-triggers-the-full-pipeline) | Builds, unit tests, acceptance tests, and the rest of the existing pipeline |
| **Lightweight** | Only non-code files changed | `secrets-scan` only |

Mixed PRs (for example `.md` and `.ts`) always take the **full** path.

**Branches named `docs/*`** use the same full vs lightweight rules. Md-only `docs/*` PRs are lightweight
(`secrets-scan` only), not a separate pipeline.

`docs-only-check` (full config, `docs/*` only) allows `.md`, `.svg`, and `.jpg`—see
[`scripts/ci/is-docs-only-changes.sh`](scripts/ci/is-docs-only-changes.sh). It runs only when the full
config is selected, not on typical md-only PRs. Repo help assets: `help/ide.svg`, `help/monitor.svg`;
`help/snyk-cli-screenshot.png` is not in that allowlist.

#### What triggers the full pipeline

If **any** changed file (vs merge-base with `main`, excluding `node_modules/`) matches one of these, you get the full
pipeline:

- Source: `*.go`, `*.ts`, `*.js`, `*.sh`
- Build / deps: `Makefile`, `go.mod`, `go.sum`, `package.json`, `package-lock.json`
- Tooling: `.circleci/**` (except `continue_config_light.yml`), `.nvmrc`

Changes only to other files — for example `.md`, `.yaml`/`.yml` (outside `.circleci/`), `CODEOWNERS`, or
`.github/workflows/**` — use the lightweight path.

The authoritative list lives in [`scripts/ci/code-change-pathspecs.txt`](scripts/ci/code-change-pathspecs.txt) (used by
[`scripts/ci/has-code-changes.sh`](scripts/ci/has-code-changes.sh)).

If any checks fail, fix them and force push your changes again. Make sure to review and tidy up your branch so that it
remains easy to follow.

Expand Down
6 changes: 2 additions & 4 deletions cliv2/internal/persona/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,8 @@ func detectAgent(l lookup) (Agent, bool) {
// canonicalAgent normalises an explicitly declared AI_AGENT value onto the set
// of canonical agent names.
func canonicalAgent(name string) Agent {
switch Agent(name) {
case "github-copilot-cli":
if Agent(name) == "github-copilot-cli" {
return AgentGitHubCopilot
default:
return Agent(name)
}
return Agent(name)
}
Comment on lines 128 to 133

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some changes such as this one I believe is not necessary in this PR

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes this is necessary because linter was failing

12 changes: 9 additions & 3 deletions cliv2/internal/persona/interactive/interactive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ func Test_InteractiveMode_Has(t *testing.T) {
func Test_isTerminal_pipeIsNotATerminal(t *testing.T) {
r, w, err := os.Pipe()
require.NoError(t, err)
defer r.Close()
defer w.Close()
defer func(r *os.File) {
_ = r.Close()
}(r)
defer func(w *os.File) {
_ = w.Close()
}(w)
Comment on lines +69 to +74

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this change needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linter


assert.False(t, isTerminal(r), "read end of a pipe is not a terminal")
assert.False(t, isTerminal(w), "write end of a pipe is not a terminal")
Expand All @@ -76,7 +80,9 @@ func Test_isTerminal_pipeIsNotATerminal(t *testing.T) {
func Test_isTerminal_regularFileIsNotATerminal(t *testing.T) {
f, err := os.CreateTemp(t.TempDir(), "interactive-test")
require.NoError(t, err)
defer f.Close()
defer func(f *os.File) {
_ = f.Close()
}(f)

assert.False(t, isTerminal(f), "a regular file is not a terminal")
}
Expand Down
16 changes: 16 additions & 0 deletions scripts/ci/code-change-pathspecs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Git pathspecs that require the full CI pipeline (one per line).
# Used by: scripts/ci/has-code-changes.sh
# Slice 3 setup job should call has-code-changes.sh (not duplicate regex in path-filtering orb).
*.go
*.ts
*.js
*.sh
:(glob)**/Makefile
:(glob)**/go.mod
:(glob)**/go.sum
:(glob)**/package.json
:(glob)**/package-lock.json
.circleci/
:(exclude,glob).circleci/continue_config_light.yml
.nvmrc
:(exclude,glob)**/node_modules/**
34 changes: 34 additions & 0 deletions scripts/ci/continue-pipeline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# POST a continuation config to the CircleCI dynamic configuration API.
set -euo pipefail

CONFIG_PATH="${1:-}"
if [ -z "$CONFIG_PATH" ] && [ -r /tmp/continuation-config-path.txt ]; then
CONFIG_PATH="$(cat /tmp/continuation-config-path.txt)"
fi

if [ -z "$CONFIG_PATH" ] || [ ! -r "$CONFIG_PATH" ]; then
echo "continue-pipeline: missing or unreadable config path" >&2
exit 2
fi

if [ -z "${CIRCLE_CONTINUATION_KEY:-}" ]; then
echo "continue-pipeline: CIRCLE_CONTINUATION_KEY is required" >&2
exit 1
fi

mkdir -p /tmp/circleci
jq -Rs '.' "$CONFIG_PATH" > /tmp/circleci/config-string.json
jq -n \
--arg continuation "$CIRCLE_CONTINUATION_KEY" \
--slurpfile config /tmp/circleci/config-string.json \
'{"continuation-key": $continuation, "configuration": $config|join("\n"), "parameters": {}}' \
> /tmp/circleci/continue_post.json
code="$(curl -sS -o /tmp/circleci/continue_response.json -w '%{http_code}' \
-X POST \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
--data @/tmp/circleci/continue_post.json \
"https://${CIRCLECI_DOMAIN:-circleci.com}/api/v2/pipeline/continue")"
cat /tmp/circleci/continue_response.json
[ "$code" = "200" ]
32 changes: 32 additions & 0 deletions scripts/ci/has-code-changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Exit 0 if git diff vs merge-base includes any code-change pathspec; else exit 1.
set -euo pipefail

SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
PATHSPECS_FILE="${CODE_CHANGE_PATHSPECS_FILE:-$SCRIPT_DIR/code-change-pathspecs.txt}"
BASE_REF="${BASE_REF:-origin/main}"

if [ ! -r "$PATHSPECS_FILE" ]; then
echo "has-code-changes: missing pathspecs file: $PATHSPECS_FILE" >&2
exit 2
fi

PATHSPECS=()
while IFS= read -r line; do
PATHSPECS+=("$line")
done < <(grep -v '^[[:space:]]*#' "$PATHSPECS_FILE" | grep -v '^[[:space:]]*$' || true)
if [ "${#PATHSPECS[@]}" -eq 0 ]; then
echo "has-code-changes: no pathspecs in $PATHSPECS_FILE" >&2
exit 2
fi

if ! git rev-parse --verify "$BASE_REF" >/dev/null 2>&1; then
echo "has-code-changes: unknown base ref: $BASE_REF" >&2
exit 2
fi

MERGE_BASE="$(git merge-base HEAD "$BASE_REF")"
if [ -n "$(git diff --name-only "${MERGE_BASE}...HEAD" -- "${PATHSPECS[@]}")" ]; then
exit 0
fi
exit 1
24 changes: 24 additions & 0 deletions scripts/ci/is-docs-only-changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Exit 0 if all changed files are docs assets (.md, .svg, .jpg); else exit 1.
set -euo pipefail

SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
ALLOWED='(\.md|\.svg|\.jpg)$'

@danskmt danskmt Jul 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, instead of maintaining this separate list, can we just make an "opposite" of the patterns we have in the other file? (scripts/ci/code-change-pathspecs.txt)


CHANGED_FILES="$("$SCRIPT_DIR/list-changed-files.sh")"
if [ -z "$CHANGED_FILES" ]; then
exit 0
fi

while IFS= read -r file; do
[ -z "$file" ] && continue
if echo "$file" | grep -qE '(^|/)node_modules/'; then
continue
fi
if ! echo "$file" | grep -qE "$ALLOWED"; then
echo "Error: disallowed file type for docs-only branch: $file" >&2
exit 1
fi
done <<< "$CHANGED_FILES"

exit 0
13 changes: 13 additions & 0 deletions scripts/ci/list-changed-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# List files changed on HEAD since merge-base with a base ref (default origin/main).
set -euo pipefail

BASE_REF="${1:-${BASE_REF:-origin/main}}"

if ! git rev-parse --verify "$BASE_REF" >/dev/null 2>&1; then
echo "list-changed-files: unknown base ref: $BASE_REF" >&2
exit 2
fi

MERGE_BASE="$(git merge-base HEAD "$BASE_REF")"
git diff --name-only "${MERGE_BASE}...HEAD"
Comment on lines +5 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can probably reuse what is being used in has-code-changes.sh

77 changes: 77 additions & 0 deletions scripts/ci/test_has_code_changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
HAS_CODE="$SCRIPT_DIR/has-code-changes.sh"

# ponytail: temp repo exercises real git diff + pathspecs (no hand-rolled pattern matcher).
with_repo() {
local dir
dir=$(mktemp -d)
trap 'rm -rf "$dir"' RETURN

cp "$SCRIPT_DIR/has-code-changes.sh" "$SCRIPT_DIR/list-changed-files.sh" "$dir/"
chmod +x "$dir"/*.sh

cd "$dir"
git init -q
git config user.email 'ci-test@example.com'
git config user.name 'ci-test'
echo base >README.md
git add . && git commit -qm base

export BASE_REF=HEAD
export CODE_CHANGE_PATHSPECS_FILE="$SCRIPT_DIR/code-change-pathspecs.txt"
"$@"
}

commit_paths() {
for f in "$@"; do
mkdir -p "$(dirname -- "$f")"
touch "$f"
git add -- "$f"
done
git commit -qm "add: $*"
}

assert_no() {
commit_paths "$@"
if BASE_REF=HEAD~1 "$HAS_CODE"; then
echo "FAIL: expected no code match for: $*" >&2
exit 1
fi
echo "ok (no code): $*"
git reset --hard HEAD~1 >/dev/null
}

assert_yes() {
commit_paths "$@"
if ! BASE_REF=HEAD~1 "$HAS_CODE"; then
echo "FAIL: expected code match for: $*" >&2
exit 1
fi
echo "ok (code): $*"
git reset --hard HEAD~1 >/dev/null
}

with_repo assert_no CONTRIBUTING.md .github/CODEOWNERS
with_repo assert_no README.md docs/guide.md
with_repo assert_no test/fixtures/pnpm-app/node_modules/.pnpm/foo/index.mjs
with_repo assert_no .circleci/continue_config_light.yml

with_repo assert_yes src/cli/main.ts
with_repo assert_yes cliv2/pkg/foo/bar.go
with_repo assert_yes scripts/install-dev-dependencies.sh
with_repo assert_yes Makefile
with_repo assert_yes cliv2/Makefile
with_repo assert_yes cliv2/go.mod
with_repo assert_yes cliv2/go.sum
with_repo assert_yes package.json
with_repo assert_yes packages/foo/package-lock.json
with_repo assert_yes .circleci/config.yml
with_repo assert_yes .circleci/continue_config.yml
with_repo assert_yes .nvmrc
with_repo assert_yes dangerfile.js
with_repo assert_yes CONTRIBUTING.md src/foo.ts

echo "test_has_code_changes: all passed"
58 changes: 58 additions & 0 deletions scripts/ci/test_is_docs_only_changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
IS_DOCS="$SCRIPT_DIR/is-docs-only-changes.sh"

with_repo() {
local dir
dir=$(mktemp -d)
trap 'rm -rf "$dir"' RETURN

cp "$SCRIPT_DIR/is-docs-only-changes.sh" "$SCRIPT_DIR/list-changed-files.sh" "$dir/"
chmod +x "$dir"/*.sh

cd "$dir"
git init -q
git config user.email 'ci-test@example.com'
git config user.name 'ci-test'
echo base >README.md
git add . && git commit -qm base
"$@"
}

commit_paths() {
for f in "$@"; do
mkdir -p "$(dirname -- "$f")"
touch "$f"
git add -- "$f"
done
git commit -qm "add: $*"
}

assert_docs() {
commit_paths "$@"
if ! BASE_REF=HEAD~1 "$IS_DOCS"; then
echo "FAIL: expected docs-only for: $*" >&2
exit 1
fi
echo "ok (docs): $*"
git reset --hard HEAD~1 >/dev/null
}

assert_not_docs() {
commit_paths "$@"
if BASE_REF=HEAD~1 "$IS_DOCS"; then
echo "FAIL: expected non-docs failure for: $*" >&2
exit 1
fi
echo "ok (not docs): $*"
git reset --hard HEAD~1 >/dev/null
}

with_repo assert_docs README.md docs/guide.md
with_repo assert_docs assets/logo.svg
with_repo assert_not_docs README.md src/foo.ts
with_repo assert_not_docs CODEOWNERS

echo "test_is_docs_only_changes: all passed"