{"_id":"@avaragado/contentful-backup","_rev":"3-002aeb19a35b62002e41340404a88e14","name":"@avaragado/contentful-backup","dist-tags":{"latest":"0.6.1"},"versions":{"0.6.0":{"name":"@avaragado/contentful-backup","version":"0.6.0","description":"A console app and node module for backing up Contentful spaces","keywords":["contentful","backup","console","cli","terminal","library"],"main":"./dist/index.js","bin":{"contentful-backup":"./dist/bin/cli-shim.js"},"files":["dist/","doc/"],"homepage":"https://github.com/avaragado/contentful-backup#readme","repository":{"type":"git","url":"git+https://github.com/avaragado/contentful-backup.git"},"bugs":{"url":"https://github.com/avaragado/contentful-backup/issues"},"author":{"name":"David Smith","email":"contentful-backup@avaragado.org","url":"https://avaragado.org"},"license":"MIT","scripts":{"start":"nps"},"devDependencies":{"babel-cli":"^6.24.1","babel-eslint":"^8.2.2","babel-plugin-transform-es2015-modules-commonjs":"^6.24.1","babel-plugin-transform-flow-strip-types":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.23.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.3.3","concurrently":"^3.4.0","eslint":"^4.18.2","eslint-config-airbnb":"^16.1.0","eslint-plugin-flowtype":"^2.46.1","eslint-plugin-import":"^2.9.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-react":"^7.7.0","flow-bin":"^0.66.0","flow-copy-source":"^1.1.0","flow-coverage-report":"^0.5.0","flow-typed":"^2.0.0","nps":"^5.8.1","standard-version":"^4.0.0"},"dependencies":{"babel-core":"^6.24.1","babel-polyfill":"^6.23.0","babel-runtime":"^6.23.0","bardot":"^0.2.1","chalk":"^2.3.2","contentful":"^5.1.3","emittery":"^0.3.0","fallback-cli":"^2.0.2","glob":"^7.1.1","mkdirp":"^0.5.1","node-fetch":"^2.1.1","ora":"^2.0.0","outdent":"^0.4.1","rimraf":"^2.6.2","winston":"^2.4.0","yargs":"^11.0.0","yup":"^0.24.1"},"gitHead":"e7705939f89e059aea581105556ec5eb0fdaa861","_id":"@avaragado/contentful-backup@0.6.0","_npmVersion":"5.5.1","_nodeVersion":"8.9.1","_npmUser":{"name":"avaragado","email":"npm@avaragado.org"},"dist":{"integrity":"sha512-xanpGJooyKrLrQ4OP0xV0IWw9RMRnzDJPB8dQWc2EDyVLEMCXS+o2AC48VirNTAGLd9LYiUFOpIiqOkbDQCyOw==","shasum":"21bb971bb650721b904f14d258672ccfa454391e","tarball":"https://registry.npmjs.org/@avaragado/contentful-backup/-/contentful-backup-0.6.0.tgz","fileCount":41,"unpackedSize":88435,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5zl1ls9pJAsg9DpMI88GNOJowrvDAHOEJNERPdhK5RgIgb+zZ5CDulefzdCrXpG8bmHysVKV0yCzC9MAutWSznuk="}]},"maintainers":[{"name":"avaragado","email":"npm@avaragado.org"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/contentful-backup_0.6.0_1521200218318_0.6241721448246198"},"_hasShrinkwrap":false},"0.6.1":{"name":"@avaragado/contentful-backup","version":"0.6.1","description":"A console app and node module for backing up Contentful spaces","keywords":["contentful","backup","console","cli","terminal","library"],"main":"./dist/index.js","bin":{"contentful-backup":"./dist/bin/cli-shim.js"},"files":["dist/","doc/"],"homepage":"https://github.com/avaragado/contentful-backup#readme","repository":{"type":"git","url":"git+https://github.com/avaragado/contentful-backup.git"},"bugs":{"url":"https://github.com/avaragado/contentful-backup/issues"},"author":{"name":"David Smith","email":"contentful-backup@avaragado.org","url":"https://avaragado.org"},"license":"MIT","scripts":{"start":"nps"},"devDependencies":{"babel-cli":"^6.24.1","babel-eslint":"^8.2.2","babel-plugin-transform-es2015-modules-commonjs":"^6.24.1","babel-plugin-transform-flow-strip-types":"^6.22.0","babel-plugin-transform-object-rest-spread":"^6.23.0","babel-plugin-transform-runtime":"^6.23.0","babel-preset-env":"^1.3.3","concurrently":"^3.4.0","eslint":"^4.18.2","eslint-config-airbnb":"^16.1.0","eslint-plugin-flowtype":"^2.46.1","eslint-plugin-import":"^2.9.0","eslint-plugin-jsx-a11y":"^6.0.2","eslint-plugin-react":"^7.7.0","flow-bin":"^0.68.0","flow-copy-source":"^1.1.0","flow-coverage-report":"^0.5.0","flow-typed":"^2.0.0","nps":"^5.8.2","standard-version":"^4.0.0"},"dependencies":{"babel-core":"^6.24.1","babel-polyfill":"^6.23.0","babel-runtime":"^6.23.0","bardot":"^0.2.1","chalk":"^2.3.2","contentful":"^5.1.3","emittery":"^0.3.0","fallback-cli":"^2.0.2","glob":"^7.1.1","mkdirp":"^0.5.1","node-fetch":"^2.1.1","ora":"^2.0.0","outdent":"^0.4.1","rimraf":"^2.6.2","winston":"^2.4.1","yargs":"^11.0.0","yup":"^0.24.1"},"gitHead":"763f850e9be2f591d0275a86df845a5b318f0a46","_id":"@avaragado/contentful-backup@0.6.1","_npmVersion":"5.5.1","_nodeVersion":"8.9.1","_npmUser":{"name":"avaragado","email":"npm@avaragado.org"},"dist":{"integrity":"sha512-PVcCpvc2li0PubLdqItYvICxZ0cTO38zkQWG8AfvpKa4SXqxYpEnunCM0aoTvI0Ic1v3NaeB04fb+Fi3bC7K9g==","shasum":"f2d45790ddf360359cf28b50927d7a44123b9ff4","tarball":"https://registry.npmjs.org/@avaragado/contentful-backup/-/contentful-backup-0.6.1.tgz","fileCount":41,"unpackedSize":88702,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDMTm4cFGOQHTXJAL3+FWD+EFfvisHujLDHMO4uUWX+OAiBvrEl+4SBF6uELt/gI6do3E89VJlpSoH+v4Cucnk4WpA=="}]},"maintainers":[{"name":"avaragado","email":"npm@avaragado.org"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/contentful-backup_0.6.1_1521231637809_0.018166252684830297"},"_hasShrinkwrap":false}},"time":{"created":"2018-03-16T11:36:58.268Z","0.6.0":"2018-03-16T11:36:58.426Z","modified":"2022-04-04T16:44:48.971Z","0.6.1":"2018-03-16T20:20:37.849Z"},"maintainers":[{"name":"avaragado","email":"npm@avaragado.org"}],"description":"A console app and node module for backing up Contentful spaces","homepage":"https://github.com/avaragado/contentful-backup#readme","keywords":["contentful","backup","console","cli","terminal","library"],"repository":{"type":"git","url":"git+https://github.com/avaragado/contentful-backup.git"},"author":{"name":"David Smith","email":"contentful-backup@avaragado.org","url":"https://avaragado.org"},"bugs":{"url":"https://github.com/avaragado/contentful-backup/issues"},"license":"MIT","readme":"# contentful-backup\n\n> A console app and node module for backing up Contentful spaces.\n\n`contentful-backup` backs up:\n\n- Entries\n- Assets\n- Space metadata\n- Content type metadata\n\n## Features\n\n- **Incremental backup of entries and assets** Run the app at any time to keep a backup \"topped up\". (Space and content type metadata is downloaded in full each time.)\n- **Multiple spaces** Back up one or more spaces with a single run of the app.\n- **Once or forever** Back up once then exit – or run forever, backing up as frequently as you want.\n- **Configurable backoff** Back up more frequently when spaces are changing.\n- **Plugins** Built-in plugins for saving spaces to disk, logging to console and file, and performing post-backup git actions – with the ability to define your own plugins.\n\n\n### Caveats\n\n- This project may have bugs and makes no guarantees. Use it at your own risk.\n- This project uses the Contentful JavaScript API, and is subject to the whims of that API. In particular, the synchronisation API only retrieves published entries and assets, and not those in draft, changed/updated or archived states.\n- This project is not associated with Contentful.\n- This project supports Node 8 and above.\n\n\n## Why?\n\n- **Recover from accidental deletions** The main goal of the Contentful product is to store your content and let you fetch it easily for presentation. Contentful supports versioning and makes its own backups, but those backups aren't accessible to customers. If you accidentally delete an entry or an asset in the Contentful UI, you can't undo that. Given sufficient quantities of fingers, an accident is guaranteed to happen. `contentful-backup` aims to help you recover from these accidents.\n- **Keep a timeline of changes** It can be useful to track the history of content (\"when did we change that product name?\"). Contentful lets you examine past versions of an entry, but doesn't place that in context (what else changed then?), and Contentful doesn't keep past versions of assets. `contentful-backup` snapshots entire spaces. (This is imperfect – there are no details of who made the changes.)\n\n\n## Overview\n\nYou can use `contentful-backup` in several ways:\n\n- Put your settings in a configuration file in a directory, run `contentful-backup`, and leave it forever or run it whenever you wish. You can use command-line arguments to override certain settings in the configuration file.\n- As above, [adding your own plugins](./doc/plugins.md) to change or augment built-in behaviour.\n- [Write your own node-based interface](./doc/node-module.md) and call `contentful-backup` programmatically with full control over its behaviour.\n\nThe core of `contentful-backup` is concerned with managing the overall flow of data: for each space to back up, calling the Contentful API and storing necessary tokens for incremental backups. `contentful-backup` triggers events with the appropriate data, and then plugins – both built-in plugins and ones you write yourself – make all the interesting things happen (saving, logging, gitting).\n\nThis README covers usage as a console app with the built-in plugins.\n\n\n## Installation\n\n```bash\n$ yarn global add @avaragado/contentful-backup\n$ # or\n$ npm -g install @avaragado/contentful-backup\n```\n\n## Usage\n\n```bash\n$ contentful-backup\n    [--dir <target-dir>]\n    [--space <space-id> <cda-token>]...\n    [--every <minutes>...]\n    [--plugins [save-disk | log-console | log-file | git-commit | <module-name>]...]\n```\n\nBacks up one or more spaces.\n\n`--dir` is the path to the directory in which `contentful-backup` stores backup data, and where it looks for a configuration file. This directory must already exist. It defaults to the current directory.\n\n`--space` identifies a space to back up, and must be followed by a space ID and then a Content Delivery API token. Specify `--space` multiple times to back up multiple spaces in a single backup run. All spaces are backed up in sequence on every run.\n\n`--every` runs `contentful-backup` forever, periodically performing a backup run. In this mode, `contentful-backup` does a backup run, sleeps for a time, then repeats. If omitted, `contentful-backup` performs a single backup run and then exits.\n\n- With one number (example: `--every 60`): `contentful-backup` always sleeps that number of minutes between backup runs.\n- With multiple numbers (example: `--every 1 2 10 60`): `contentful-backup` uses a form of exponential backoff. When content changes in a backup run, the first number in the sequence is used for the sleep period. For each backup run where content doesn't change, the next number is used, eventually repeating the last number. In this way, backup runs occur more frequently when content seems to be changing.\n\n`--plugins` identifies the `contentful-backup` plugins to use for each backup run, in order. If omitted, defaults to `save-disk log-console`.\n\n- Use a built-in name (`save-disk`, `log-console`, `log-file`, `git-commit`) to use that plugin\n- Use a path (absolute, or relative to the target directory or current directory) to use your own node module as a custom plugin\n- Use a node module name to require that module as a custom plugin\n\nUnless you write your own plugin to replace `save-disk`, always include that in your list otherwise nothing gets saved to disk.\n\nSome plugins accept configuration options. To set these, specify plugins in a configuration file: you can't set plugin options on the command line.\n\n\n## Configuration file\n\nDefine default `contentful-backup` configuration in a file named `contentful-backup.config.js` or `contentful-backup.config.json` in the target directory. Command-line arguments override this configuration.\n\nThe configuration file must export or define an object of type `FileConfig`:\n\n```ts\ntype SpaceConfig = { id: string, token: string };\ntype PluginName = \"save-disk\" | \"log-console\" | \"log-file\" | \"git-commit\" | string;\ntype PluginOptions = Object;\ntype PluginConfigSimple = PluginName;\ntype PluginConfigStrict = [PluginName, PluginOptions];\ntype PluginConfig = PluginConfigSimple | PluginConfigStrict;\n\ntype FileConfig = {\n    spaces?: Array<SpaceConfig>,\n    every?: number | Array<number>,\n    plugins?: Array<PluginConfig>,\n}\n```\n\nExample configuration file `contentful-backup.config.json`:\n\n```json\n{\n    \"spaces\": [\n        { \"id\": \"abcdabcdabcd\", \"token\": \"abcdabcdabcdabcdabcdabcd\" },\n        { \"id\": \"zxzxzxzxzxzx\", \"token\": \"zxzxzxzxzxzxzxzxzxzxzxzx\" },\n    ],\n    \"every\": [1, 10, 100],\n    \"plugins\": [\n        \"save-disk\",\n        \"log-file\",\n        [\"git-commit\", { \"push\": true }]\n    ]\n}\n```\n\n\n## Built-in plugins\n\n`contentful-backup` ships with some built-in plugins to perform actions such as storing retrieved files, and logging. Without plugins, `contentful-backup` doesn't do anything useful.\n\n\n### save-disk\n\nSaves space and content type metadata, plus entries and assets. All data is stored in a subdirectory (whose name is the space ID) of the target directory.\n\nIn the configuration file, you can specify a plugin options object matching this type:\n\n```ts\ntype SaveDiskPluginOptions = {\n    onDeletedEntry?: 'delete' | 'move',\n    onDeletedAsset?: 'delete' | 'move',\n};\n```\n\n- `onDeletedEntry` and `onDeletedAsset` indicate the action the plugin should take for `DeletedEntry` and `DeletedAsset` records in Contentful's synchronisation response. Use `move` (the default) to move deleted entries and assets to a `deleted` subdirectory, with a timestamp of deletion. Use `delete` to simply delete the local files (best used in conjunction with the `git-commit` plugin, so each commit reflects the state of the space at that time).\n\nNote that in the Contentful synchronisation API, \"deleted\" entries or assets include those whose state changes from 'published' to 'draft' or 'archived'. If you archive an entry or asset, the next `contentful-backup` run will delete it or move it to the `deleted` subdirectory according to the appropriate plugin option. If you then republish the entry or asset, the next backup run will recreate it as if new (leaving the `deleted` directory unchanged: the record remains there).\n\n\n### log-console\n\nLogs backup events to the console. This output is intended for human consumption.\n\n_No plugin options_\n\n\n### log-file\n\nLogs backup events to the file `contentful-backup.log` in the target directory. These log files are rotated when they reach 1 MB.\n\nIn the configuration file, you can specify a plugin options object matching this type:\n\n```ts\ntype LogFilePluginOptions = {\n    level?: 'error' | 'warn' | 'info' | 'verbose' | 'debug' | 'silly',\n};\n```\n\n- `level` indicates how verbose the messages should be. Default: `info`.\n\n\n### git-commit\n\nAfter a backup run, checks changes into git, then optionally pushes the branch to a remote.\n\nIn the configuration file, you can specify a plugin options object matching this type:\n\n```ts\ntype GitCommitPluginOptions = {\n    push?: boolean | string,\n};\n```\n\n- `push` can be `false` (the default) to mean \"don't push\", `true` to mean \"push to the default remote\", or the name of a remote to mean \"push to this remote\".\n\nThe `git-commit` plugin assumes:\n\n- The target directory is a valid git repository\n- The user running the app has commit and push permissions on the current branch in that repository\n- `git` is in the `PATH`.\n\nThe plugin makes no effort to recover from errors.\n\n\n## Examples\n\n```bash\n$ contentful-backup --space ididididid1 tktktktktk1 --space ididididid2 tktktktktk2 --every 2 30\n```\n\nBacks up spaces `ididididid1` and `ididididid2` to the current directory. While content is changing, backups occur every two minutes. When content isn't changing, backups occur every 30 minutes. Other configuration (here, plugins) would be read from a configuration file in the current directory.\n\n```bash\n$ contentful-backup --dir ../my-backups --plugins save-disk log-file git-commit\n```\n\nBacks up spaces to `../my-backups`, logs to `contentful-backup.log` in that directory, then checks in all changes. Other configuration (here, the spaces and any `every` setting) would be read from a configuration file in `../my-backups`.\n\n\n## Errors\n\n- Any error that occurs during the backup of a space, such as a network glitch, skips the rest of the backup for that space but doesn't exit the app. For example, imagine you've configured `contentful-backup` to back up two spaces in each backup run, and perform a backup run `--every 60` minutes. If an error occurs while fetching the content type information of the first space, then the app won't try to back up entries and assets for that space. Instead, it'll skip to backing up the second space, then wait 60 minutes before starting another backup run for both spaces.\n- Use the `log-file` and/or `log-console` plugins to record details of any errors.\n- Use the `git-commit` plugin to include error details in the commit log.\n- You could write a plugin to notify a human or friendly droid when an error occurs.\n- If an error occurs before `contentful-backup` finishes synchronising entries and assets, the app doesn't save the next synchronisation token. This means the next backup run reruns the synchronisation that failed (in other words, you shouldn't lose anything).\n- If you don't trust a particular incremental backup, remove the `<space-id>` subdirectory of the target directory: the next backup run will trigger a full backup of that space.\n\n\n## Questions\n\n### What files are written?\n\n`contentful-backup` writes these files and directories in the target directory, if the plugin shown is in use:\n\n| File/directory | Plugin | Description |\n|---|---|---|\n| `contentful-backup.log` | `log-file` | Audit trail of backup runs |\n| `<space-id>/space.json` | `save-disk` | Space metadata |\n| `<space-id>/contentTypes.json` | `save-disk` | Content types metadata |\n| `<space-id>/asset/<id>/data.json` | `save-disk` | Data for a single asset |\n| `<space-id>/asset/<id>/<locale>/<filename>` | `save-disk` | An asset file |\n| `<space-id>/entry/<id>/data.json` | `save-disk` | Data for a single entry |\n| `<space-id>/deleted/<yyyy-mm-ddThh-mm-ss>/...` | `save-disk` | When moving deleted entries/assets, data deleted at the timestamp |\n| `<space-id>/nextSyncToken.txt` | _always_ | The Contentful token indicating the most recent successful synchronisation |\n\n\n### What value should I use for `--every`?\n\nIt's up to you. Something like `--every 1 1 2 5 10 30 60` covers many bases:\n\n- While content is changing constantly, backup runs take place every minute.\n- As soon as content stops changing, backup runs take place less frequently: after two minutes, then five, then ten, and so on.\n- When content starts changing again, backup runs will occur every minute once more.\n\nBear in mind that every backup downloads space and content type metadata in full: even if your CMS entries and assets change rarely, each backup may still result in a hefty chunk of download.\n\nAlso, every backup contributes to your API usage, as measured by Contentful.\n\n\n## Maintainer\n\nDavid Smith (@avaragado)\n\n\n## Contribute\n\nBug reports, feature requests and PRs are gratefully received. [Add an issue](https://github.com/avaragado/contentful-backup/issues/new) or submit a PR.\n\nPlease note that this project is released with a [Contributor Code of Conduct](code-of-conduct.md). By participating in this project you agree to abide by its terms.\n\n\n## Licence\n\n[MIT](LICENSE.txt) © David Smith\n","readmeFilename":"README.md"}