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
23 changes: 23 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
node_modules
.todo
.env
.dccache
logs
contents
lerna-debug.log
.DS_Store
contentTest
build
_backup*
oclif.manifest.json
.vscode
.nyc_output
contentstack-cli-logs
packages/**/package-lock.json
.dccache
yarn.lock
contents-*
*.http
*.todo
talisman_output.log
snyk_output.log
55 changes: 55 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"env": {
"node": true
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"extends": [
// "oclif",
"oclif-typescript",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"@typescript-eslint/no-unused-vars": [
"error",
{
"args": "none"
}
],
"@typescript-eslint/prefer-namespace-keyword": "error",
"@typescript-eslint/quotes": [
"error",
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"semi": "off",
"@typescript-eslint/type-annotation-spacing": "error",
"@typescript-eslint/no-redeclare": "off",
"eqeqeq": [
"error",
"smart"
],
"id-match": "error",
"no-eval": "error",
"no-var": "error",
"quotes": "off",
"indent": "off",
"camelcase": "off",
"comma-dangle": "off",
"arrow-parens": "off",
"operator-linebreak": "off",
"object-curly-spacing": "off",
"node/no-missing-import": "off",
"padding-line-between-statements": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"unicorn/no-abusive-eslint-disable": "off",
"unicorn/consistent-function-scoping": "off",
"@typescript-eslint/no-use-before-define": "off"
}
}
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
*-debug.log
*-error.log
/.nyc_output
/dist
/lib
/tmp
/yarn.lock
node_modules
.DS_Store
coverage
./contents
.vscode/
/lib
.env
_backup_*
contents/
logs/
oclif.manifest.json
69 changes: 69 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env sh
# Pre-commit hook to run Talisman and Snyk scans, completing both before deciding to commit

# Function to check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}

# Check if Talisman is installed
if ! command_exists talisman; then
echo "Error: Talisman is not installed. Please install it and try again."
exit 1
fi

# Check if Snyk is installed
if ! command_exists snyk; then
echo "Error: Snyk is not installed. Please install it and try again."
exit 1
fi

# Allow bypassing the hook with an environment variable
if [ "$SKIP_HOOK" = "1" ]; then
echo "Skipping Talisman and Snyk scans (SKIP_HOOK=1)."
exit 0
fi

# Initialize variables to track scan results
talisman_failed=false
snyk_failed=false

# Run Talisman secret scan
echo "Running Talisman secret scan..."
talisman --githook pre-commit > talisman_output.log 2>&1
talisman_exit_code=$?

if [ $talisman_exit_code -eq 0 ]; then
echo "Talisman scan passed: No secrets found."
else
echo "Talisman scan failed (exit code $talisman_exit_code). See talisman_output.log for details."
talisman_failed=true
fi

# Run Snyk vulnerability scan (continues even if Talisman failed)
echo "Running Snyk vulnerability scan..."
snyk test --all-projects --fail-on=all > snyk_output.log 2>&1
snyk_exit_code=$?

if [ $snyk_exit_code -eq 0 ]; then
echo "Snyk scan passed: No vulnerabilities found."
elif [ $snyk_exit_code -eq 1 ]; then
echo "Snyk found vulnerabilities. See snyk_output.log for details."
snyk_failed=true
else
echo "Snyk scan failed with error (exit code $snyk_exit_code). See snyk_output.log for details."
snyk_failed=true
fi

# Evaluate results after both scans
if [ "$talisman_failed" = true ] || [ "$snyk_failed" = true ]; then
echo "Commit aborted due to issues found in one or both scans."
[ "$talisman_failed" = true ] && echo "- Talisman issues: Check talisman_output.log"
[ "$snyk_failed" = true ] && echo "- Snyk issues: Check snyk_output.log"
exit 1
fi

# If both scans pass, allow the commit
echo "All scans passed. Proceeding with commit."
rm -f talisman_output.log snyk_output.log
exit 0
12 changes: 12 additions & 0 deletions .mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"require": [
"test/helpers/init.js",
"ts-node/register",
"source-map-support/register"
],
"watch-extensions": [
"ts"
],
"recursive": true,
"timeout": 5000
}
5 changes: 5 additions & 0 deletions .nycrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"inlcude": [
"lib/**/*.js"
]
}
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/README.md
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2
}
9 changes: 5 additions & 4 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
fileignoreconfig:
- filename: .github/workflows/secrets-scan.yml
ignore_detectors:
- filecontent
version: "1.0"
- filename: snyk_output.log
checksum: dfa9dc093345f006cc3fb107b8495fcbe79c524db51f44c08d959c656bedf2f7
- filename: talisman_output.log
checksum: 50a8928e551f9092dafcf50f531c926ac00e2846c564aafaaab70c1ecaa19490
version: '1.0'
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Contentstack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
109 changes: 108 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,108 @@
# cli-query-export
# Contentstack CLI Query Export Plugin

A powerful CLI plugin for Contentstack that enables query-based content export with intelligent dependency resolution and asset reference detection.

## Overview

This plugin extends the Contentstack CLI to export content based on custom queries, automatically resolving dependencies between content types, global fields, extensions, and taxonomies. It intelligently detects and exports referenced assets to ensure complete content portability.

## Features

- 🔍 **Query-based Export**: Export content using custom queries instead of entire content types
- 🔗 **Dependency Resolution**: Automatically resolve and export dependencies (global fields, extensions, taxonomies)
- 🖼️ **Asset Reference Detection**: Intelligent detection of asset references in various formats
- 📁 **Organized Output**: Well-structured export with separate folders for each module
- ⚙️ **Configurable**: Support for external config files and flexible options
- 🌐 **Multi-locale Support**: Export content across different locales
- 📊 **Export Metadata**: Comprehensive metadata tracking for export operations

## Installation

```bash
# Install as a Contentstack CLI plugin
npm install -g @contentstack/cli-cm-export-query

# Or install locally
npm install @contentstack/cli-cm-export-query
```

## Usage

### Basic Export

```bash
# Export using management token alias
csdx cm:stacks:export-query -a <alias> -q "{'title': {'$exists': true}}"

# Export using API key and management token
csdx cm:stacks:export-query --stack-api-key <api-key> -A <management-token> -q "{'title': {'$exists': true}}"
```

### Command Options

| Flag | Description | Required |
|------|-------------|----------|
| `-a, --alias` | Management token alias | Yes (or use -A) |
| `-A, --management-token` | Management token | Yes (or use -a) |
| `--stack-api-key` | Stack API key | Yes |
| `-q, --query` | Query for content export | Yes |
| `-d, --data-dir` | Export directory path | No |
| `--branch` | Branch name | No |
| `--skip-references` | Skip reference resolution | No |
| `--skip-dependencies` | Skip dependency export | No |
| `--secured-assets` | Include secured assets | No |
| `--config` | External config file path | No |

### Query Examples

**Basic Content Query:**
```bash
csdx cm:stacks:export-query -a prod -q "{'title': {'$regex': 'blog'}}"
```

**Date Range Query:**
```bash
csdx cm:stacks:export-query -a prod -q "{'updated_at': {'$gte': '2024-01-01'}}"
```

**Complex Query:**
```bash
csdx cm:stacks:export-query -a prod -q "{'$and': [{'title': {'$exists': true}}, {'tags': {'$in': ['featured']}}]}"
```

## Configuration

### Default Configuration

The plugin includes a default configuration file at `src/config/export-defaults.json`:

```json
{
"skipReferences": false,
"skipDependencies": false,
"securedAssets": false,
"includeGlobalFieldSchema": true,
"includePublishDetails": true,
"includeDimension": false,
"fetchConcurrency": 5,
"writeConcurrency": 5,
"batchSize": 100
}
```

### External Configuration

Create a custom config file and pass it using the `--config` flag:

```json
{
"skipReferences": true,
"batchSize": 50,
"fetchConcurrency": 3,
"securedAssets": true
}
```

```bash
csdx cm:stacks:export-query -a prod -q "{'title': {'$exists': true}}" --config ./my-config.json
```
3 changes: 3 additions & 0 deletions bin/dev.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\dev" %*
6 changes: 6 additions & 0 deletions bin/dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env node_modules/.bin/ts-node
// eslint-disable-next-line node/shebang, unicorn/prefer-top-level-await
(async () => {
const oclif = await import('@oclif/core');
await oclif.execute({ development: true, dir: __dirname });
})();
3 changes: 3 additions & 0 deletions bin/run.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@echo off

node "%~dp0\run" %*
7 changes: 7 additions & 0 deletions bin/run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env node

// eslint-disable-next-line unicorn/prefer-top-level-await
(async () => {
const oclif = await import('@oclif/core');
await oclif.execute({ development: false, dir: __dirname });
})();
1 change: 1 addition & 0 deletions messages/index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading