Skip to content

Commit 1bf8f89

Browse files
Merge pull request #106 from contentstack/feat/dx-5396-cursor-skills-rules
snyk fix and cursor rules
1 parent 4eb4eba commit 1bf8f89

File tree

14 files changed

+494
-62
lines changed

14 files changed

+494
-62
lines changed

.cursor/rules/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Cursor (optional)
2+
3+
*Cursor* users: start at *[AGENTS.md](../../AGENTS.md)*. All conventions live in **skills/*/SKILL.md**.
4+
5+
This folder only points contributors to *AGENTS.md* so editor-specific config does not duplicate the canonical docs.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Catches when developers forget to bump package.json for release-affecting changes.
2+
# App code changes (app.js, bin/, config/, routes/, views/, etc.) require a version bump vs latest tag.
3+
# Skips for: test-only, docs, .github (workflows/config), dependency-only bumps without app edits.
4+
name: Check Version Bump
5+
6+
on:
7+
pull_request:
8+
9+
jobs:
10+
version-bump:
11+
name: Version bump
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
19+
- name: Detect changed files and version bump
20+
id: detect
21+
run: |
22+
if git rev-parse HEAD^2 >/dev/null 2>&1; then
23+
FILES=$(git diff --name-only HEAD^1 HEAD^2)
24+
else
25+
FILES=$(git diff --name-only HEAD~1 HEAD)
26+
fi
27+
VERSION_FILES_CHANGED=false
28+
echo "$FILES" | grep -qx 'package.json' && VERSION_FILES_CHANGED=true
29+
echo "version_files_changed=$VERSION_FILES_CHANGED" >> $GITHUB_OUTPUT
30+
# App source paths for this boilerplate (no lib/webpack/dist); .github/ and test/ do not count
31+
CODE_CHANGED=false
32+
echo "$FILES" | grep -qE '^app\.js$|^bin/|^config/|^middlewares/|^models/|^public/|^routes/|^views/|^schemaNentries/' && CODE_CHANGED=true
33+
echo "$FILES" | grep -qx 'package.json' && CODE_CHANGED=true
34+
echo "code_changed=$CODE_CHANGED" >> $GITHUB_OUTPUT
35+
36+
- name: Skip when only test/docs/.github changed
37+
if: steps.detect.outputs.code_changed != 'true'
38+
run: |
39+
echo "No release-affecting files changed (e.g. only test/docs/.github). Skipping version-bump check."
40+
exit 0
41+
42+
- name: Fail when version bump was missed
43+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed != 'true'
44+
run: |
45+
echo "::error::This PR has code changes but no version bump. Please bump the version in package.json."
46+
exit 1
47+
48+
- name: Setup Node
49+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
50+
uses: actions/setup-node@v4
51+
with:
52+
node-version: '22.x'
53+
54+
- name: Check version bump
55+
if: steps.detect.outputs.code_changed == 'true' && steps.detect.outputs.version_files_changed == 'true'
56+
run: |
57+
set -e
58+
PKG_VERSION=$(node -p "require('./package.json').version.replace(/^v/, '')")
59+
if [ -z "$PKG_VERSION" ]; then
60+
echo "::error::Could not read version from package.json"
61+
exit 1
62+
fi
63+
git fetch --tags --force 2>/dev/null || true
64+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || true)
65+
if [ -z "$LATEST_TAG" ]; then
66+
echo "No existing tags found. Skipping version-bump check (first release)."
67+
exit 0
68+
fi
69+
LATEST_VERSION="${LATEST_TAG#v}"
70+
LATEST_VERSION="${LATEST_VERSION%%-*}"
71+
if [ "$(printf '%s\n' "$LATEST_VERSION" "$PKG_VERSION" | sort -V | tail -1)" != "$PKG_VERSION" ]; then
72+
echo "::error::Version bump required: package.json version ($PKG_VERSION) is not greater than latest tag ($LATEST_TAG). Please bump the version in package.json."
73+
exit 1
74+
fi
75+
if [ "$PKG_VERSION" = "$LATEST_VERSION" ]; then
76+
echo "::error::Version bump required: package.json version ($PKG_VERSION) equals latest tag ($LATEST_TAG). Please bump the version in package.json."
77+
exit 1
78+
fi
79+
echo "Version bump check passed: package.json is at $PKG_VERSION (latest tag: $LATEST_TAG)."

.github/workflows/release.yml

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,57 @@
1-
name: Release datasync manager
1+
name: Release
2+
23
on:
3-
push:
4-
branches: [master]
4+
release:
5+
types: [created]
6+
57
jobs:
68
build:
79
runs-on: ubuntu-latest
10+
permissions:
11+
contents: write
812
steps:
9-
- uses: actions/checkout@v4
10-
- uses: actions/setup-node@v4
13+
# Checkout the repository
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
# Setup Node.js environment
18+
- name: Setup Node.js
19+
uses: actions/setup-node@v4
1120
with:
1221
node-version: "22.x"
13-
# The below action will see the existing tags and will bump the current ones and this is only used to check whether the given tag already exists or not
14-
# We will be using the previous tag to compare with the current tag in the package.json
15-
# If both match then no new release would be triggered
16-
# Else New release will be created
17-
- name: Bump version and push tag
18-
id: tag_version
19-
uses: mathieudutour/github-tag-action@v6.1
20-
with:
21-
github_token: ${{ secrets.GITHUB_TOKEN }}
22-
default_bump: false
23-
# Getting the version info from package.json
24-
- name: get-npm-version
25-
id: package-version
26-
uses: martinbeentjes/npm-get-version-action@v1.3.1
27-
# Here we are checking whether this is the first release or not and then checking if it is release or not
28-
- name: check-first-release
29-
env:
30-
First_Release: ${{steps.tag_version.outputs.previous_tag=='v0.0.0'}}
31-
run: |
32-
if ${First_Release} == true; then
33-
echo "fr=true" >> $GITHUB_ENV
34-
echo "flag set to true"
35-
else
36-
echo "fr=false" >> $GITHUB_ENV
37-
echo "flag set to false"
38-
fi
39-
- name: check-release-version
40-
if: ${{env.fr=='false'}}
41-
env:
42-
old_version: ${{steps.tag_version.outputs.previous_tag}}
43-
new_version: v${{steps.package-version.outputs.current-version}}
44-
run: |
45-
echo ${old_version}
46-
echo ${new_version}
47-
echo ${{env.old_version==env.new_version}}
48-
if ${{env.old_version!=env.new_version}}; then
49-
echo "fr=true" >> $GITHUB_ENV
50-
echo "flag set to true"
51-
else
52-
echo "fr=false" >> $GITHUB_ENV
53-
echo "flag set to false"
54-
fi
55-
- name: Installing dependencies
22+
23+
# Install dependencies
24+
- name: Install dependencies
5625
run: npm install
26+
27+
# Build (dist/ is published per package.json "files")
5728
- name: Build
5829
run: npm run build-ts
59-
- name: Publishing datasync manager
60-
id: publish-core
30+
31+
# Fetch package details (name and version)
32+
- name: Get package details
33+
id: package
34+
uses: codex-team/action-nodejs-package-info@v1.1
35+
36+
# Pack the package into a .tgz archive (@contentstack/datasync-manager → contentstack-datasync-manager-<version>.tgz)
37+
- name: Pack the npm package
38+
run: npm pack
39+
40+
# Publish the package to npm
41+
- name: Publish to npm
42+
id: publish_npm
6143
uses: JS-DevTools/npm-publish@v3
62-
if: ${{env.fr=='true'}}
6344
with:
6445
token: ${{ secrets.NPM_TOKEN }}
65-
- name: Create Release
66-
id: create_release
67-
if: ${{env.fr=='true'}}
46+
# access: public # Uncomment if you need to publish a scoped package as public for the first time
47+
48+
# Upload the packaged .tgz to the release that triggered this workflow
49+
- name: Upload Release Asset
50+
uses: actions/upload-release-asset@v1
6851
env:
6952
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70-
run: gh release create v${{ steps.publish-core.outputs.version }} --title "Release ${{ steps.publish-core.outputs.version }}" --generate-notes
53+
with:
54+
upload_url: ${{ github.event.release.upload_url }}
55+
asset_path: "./contentstack-datasync-manager-${{ steps.package.outputs.version }}.tgz"
56+
asset_name: "contentstack-datasync-manager-${{ steps.package.outputs.version }}.tgz"
57+
asset_content_type: application/tgz

.husky/post-checkout

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env sh
2+
# When switching to a branch that doesn't exist on remote (e.g. newly created),
3+
# pull and merge origin/main or origin/master into current branch. Does not push.
4+
5+
# Only run on branch checkout (not file checkout)
6+
if [ "$3" != "1" ]; then
7+
exit 0
8+
fi
9+
10+
# Skip if we don't have a remote
11+
if ! git rev-parse --verify origin 2>/dev/null; then
12+
exit 0
13+
fi
14+
15+
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
16+
17+
# Skip main/master - no need to merge base into these
18+
case "$CURRENT_BRANCH" in
19+
main|master) exit 0 ;;
20+
esac
21+
22+
# Only run when current branch does not exist on origin (treat as new local branch)
23+
if git ls-remote --heads origin "$CURRENT_BRANCH" 2>/dev/null | grep -q .; then
24+
echo "post-checkout: $CURRENT_BRANCH exists on origin, skipping merge."
25+
exit 0
26+
fi
27+
28+
# Prefer main, fallback to master
29+
if git rev-parse --verify origin/main 2>/dev/null; then
30+
BASE=origin/main
31+
elif git rev-parse --verify origin/master 2>/dev/null; then
32+
BASE=origin/master
33+
else
34+
exit 0
35+
fi
36+
37+
echo "New branch detected: merging latest $BASE into $CURRENT_BRANCH (local only, not pushing)..."
38+
git fetch origin
39+
git merge "$BASE" --no-edit --no-ff
40+
echo "Done. Merge is local only; push when ready."

.talismanrc

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ fileignoreconfig:
22
- filename: .github/workflows/secrets-scan.yml
33
ignore_detectors:
44
- filecontent
5-
- filename: package-lock.json
6-
checksum: 9cafb0c285094b2aa863df712fce85e5701a2d508d0d6ecf52ea49b40dcb0c29
7-
- filename: .husky/pre-commit
8-
checksum: 1b9367d219802de2e3a8af9c5c698e0c255c00af89339d73bdbb8acf5275079f
9-
version: ""
5+
- filename: .github/workflows/check-version-bump.yml
6+
ignore_detectors:
7+
- filename: skills/dev-workflow/SKILL.md
8+
checksum: c9fa42c57463f3d00aa34ab6c8b4e58b984cd6a3a5878d25596e6e66abb4a129
9+
- filename: skills/contentstack-datasync/SKILL.md
10+
checksum: 62cf62a46c4dcdd9603b1e6ef910e0d77dfa39d2350db992a6a45d3a2791eaee
11+
- filename: skills/testing/SKILL.md
12+
checksum: ab1f82b284189e2779570d4fd5edcde93494c1ee01110da9f0be6f1f5cf5177b
13+
version: "1.0"

AGENTS.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# @contentstack/datasync-manager – Agent guide
2+
3+
*Universal entry point* for contributors and AI agents. Detailed conventions live in **skills/*/SKILL.md**.
4+
5+
## What this repo is
6+
7+
| Field | Detail |
8+
|-------|--------|
9+
| *Name:* | [`@contentstack/datasync-manager`](https://github.com/contentstack/datasync-manager) |
10+
| *Purpose:* | Primary Node.js module for [Contentstack DataSync](https://www.contentstack.com/docs/guide/synchronization/contentstack-datasync): coordinates content stores, asset stores, and a listener (webhooks), and pulls stack changes via the **Contentstack Sync API** with a **delivery token**. |
11+
| *Out of scope (if any):* | Not the standalone CDA or CMA client SDKs; not a generic HTTP client library—sync orchestration and Node **`https`** to the Sync API only. Say **DataSync** / **Sync API**, not “CMA”, for this package’s main behavior. |
12+
13+
## Tech stack (at a glance)
14+
15+
| Area | Details |
16+
|------|---------|
17+
| Language | TypeScript **4.9.x** → ES6, CommonJS (`tsconfig.json`); Node **>= 8** (`package.json` `engines`) |
18+
| Build | `tsc``dist/`; `types``typings` (`package.json`) |
19+
| Tests | **Jest** **29** + **ts-jest**; patterns in `jest.config.js`; tests under `test/**/*.ts` |
20+
| Lint / coverage | **ESLint** (`npm run lint`); legacy **TSLint** (`tslint.json`, `npm run tslint`); Jest coverage to `coverage/` |
21+
| Other | HTTP via Node **`https`**; logging via **`debug`** + `src/util/logger`; pre-commit **Talisman** + **Snyk** (`.husky/pre-commit`) |
22+
23+
## Commands (quick reference)
24+
25+
| Command type | Command |
26+
|--------------|---------|
27+
| Build | `npm run compile` or `npm run build-ts` (`clean` + `tsc`) |
28+
| Test | `npm test` (`PLUGIN_PATH=./test/dummy` + Jest, coverage, verbose) |
29+
| Lint | `npm run lint` / `npm run tslint` |
30+
31+
CI: [.github/workflows/check-version-bump.yml](.github/workflows/check-version-bump.yml) (version bump checks on PRs when applicable).
32+
33+
## Where the documentation lives: skills
34+
35+
| Skill | Path | What it covers |
36+
|-------|------|----------------|
37+
| Development workflow | [skills/dev-workflow/SKILL.md](skills/dev-workflow/SKILL.md) | Branches (`development` / `master`), release flow, build/test/lint, PR expectations, versioning |
38+
| TypeScript conventions | [skills/typescript/SKILL.md](skills/typescript/SKILL.md) | `src/` layout, compiler settings, logging, lint |
39+
| DataSync & Sync API | [skills/contentstack-datasync/SKILL.md](skills/contentstack-datasync/SKILL.md) | Public API, config, HTTP client, retries, tokens, core sync—**not** CMA |
40+
| Testing | [skills/testing/SKILL.md](skills/testing/SKILL.md) | Jest, nock, `PLUGIN_PATH`, fixtures, credentials policy |
41+
| Code review | [skills/code-review/SKILL.md](skills/code-review/SKILL.md) | PR checklist, severity (Blocker / Major / Minor) |
42+
43+
An index with “when to use” hints is in [skills/README.md](skills/README.md).
44+
45+
## Using Cursor (optional)
46+
47+
If you use *Cursor*, [.cursor/rules/README.md](.cursor/rules/README.md) only points to *AGENTS.md*—same docs as everyone else.

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"@braintree/sanitize-url": "^7.1.2",
99
"debug": "^4.4.3",
1010
"dns-socket": "^4.2.2",
11-
"lodash": "^4.17.23",
11+
"lodash": "^4.18.1",
1212
"marked": "^17.0.5",
1313
"write-file-atomic": "7.0.1"
1414
},

skills/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Skills – @contentstack/datasync-manager
2+
3+
Source of truth for detailed guidance. Read [AGENTS.md](../AGENTS.md) first, then open the skill that matches your task.
4+
5+
## When to use which skill
6+
7+
| Skill folder | Use when |
8+
|--------------|----------|
9+
| [dev-workflow](dev-workflow/SKILL.md) | Branching, releases, local build/test/lint, PR expectations, semantic-release |
10+
| [typescript](typescript/SKILL.md) | Editing `src/**/*.ts`—layout, `tsconfig`, `debug`/logger, ESLint/TSLint |
11+
| [contentstack-datasync](contentstack-datasync/SKILL.md) | Sync API, delivery token, `src/api.ts` / `src/config.ts` / `src/core/`, public API |
12+
| [testing](testing/SKILL.md) | Jest, nock, `test/dummy`, adding or debugging tests |
13+
| [code-review](code-review/SKILL.md) | Reviewing or preparing a PR—terminology, security, tests, severity |
14+
15+
Each folder contains **SKILL.md** with YAML frontmatter (`name`, `description`).

skills/code-review/SKILL.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
name: code-review
3+
description: PR review checklist for DataSync Manager—Sync API terminology, security, tests, optional severity labels
4+
---
5+
6+
# Code review – @contentstack/datasync-manager
7+
8+
## When to use
9+
10+
- Reviewing a pull request or preparing one for review.
11+
- You need a consistent checklist for **DataSync** / **Sync API** changes (not CMA).
12+
13+
## Instructions
14+
15+
### Scope and naming
16+
17+
- **DataSync Manager** uses the **Contentstack Sync API** and a **delivery token**. It is **not** the CMA SDK; do not describe it as “management” or “CMA” unless the code path actually uses Management APIs (rare here).
18+
19+
### Public surface
20+
21+
- **`src/index.ts`**: JSDoc for new or changed exports, matching existing style.
22+
- **Config**: Document new `contentstack` / `syncManager` options when behavior is user-visible.
23+
24+
### Correctness
25+
26+
- **Queues and notifications**: `push` / `unshift` / `pop` and `notifications` events (`publish`, `unpublish`, `delete`, `error`) must stay consistent for integrators.
27+
- **Token lifecycle**: `.tokens`, `.ledger`, `.checkpoint` and **Error 141** recovery in `src/api.ts`—verify edge cases and no infinite loops.
28+
- **Webhook + fallback polling** (`src/index.ts`): no timer leaks or duplicate `poke()` calls.
29+
30+
### Security
31+
32+
- HTTPS path/query handling must remain safe from SSRF (`src/api.ts`).
33+
- No secrets in logs; **Talisman** / **Snyk** expectations stay satisfied.
34+
35+
### Dependencies
36+
37+
- Prefer minimal, audited dependencies per team policy.
38+
39+
### Tests
40+
41+
- Add or update **Jest** tests with **nock** for HTTP; follow `test/dummy` patterns.
42+
- Run **`npm test`** before approving risky changes.
43+
44+
### Severity (optional labels)
45+
46+
| Label | Examples |
47+
|-------|----------|
48+
| **Blocker** | Security regression, data loss risk, infinite retries, broken public API contract |
49+
| **Major** | Wrong Sync API semantics, missing tests for critical paths, breaking change without version strategy |
50+
| **Minor** | Style, logging, non-user-facing refactors, doc-only gaps |
51+
52+
## References
53+
54+
- [contentstack-datasync/SKILL.md](../contentstack-datasync/SKILL.md) — intended semantics.
55+
- [testing/SKILL.md](../testing/SKILL.md) — test expectations.
56+
- [AGENTS.md](../../AGENTS.md) — project entry point.

0 commit comments

Comments
 (0)