From 405a24f94d9a265e122649918cdfb67efa7a9893 Mon Sep 17 00:00:00 2001 From: Erik Schultink Date: Tue, 24 Feb 2026 21:50:06 -0800 Subject: [PATCH] modernize docs, tests, with a ci flow --- .github/workflows/test.yml | 17 +++++++++++------ AGENTS.md | 23 +++++++++++++++++++++++ README.md | 4 +++- lib/cmd.js | 4 +++- test/cmd.js | 23 ++++++++++++++++++----- 5 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 AGENTS.md diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 55bdf0a..1766b31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,16 +1,21 @@ # run automated tests name: CI Tests -on: [push] +on: + push: + pull_request: + workflow_dispatch: jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20.x, 22.x, 24.x, 25.x] steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - # VMs have 23.x installed - node-version: '23.x' - - run: npm install + node-version: ${{ matrix.node-version }} + - run: npm ci || npm install - run: npm test diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f2f67bf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,23 @@ +# AGENTS + +This document explains the repository structure and context for AI agents working with the `datastore-backup` tool. + +## Purpose + +The primary purpose of this tool is to back up a **subset of entities** from Google Cloud Datastore (or Firestore in Datastore mode) rather than an entire database. It allows specifying specific entities and groups them into "backup schedules" or schedules representing logical groupings of data to export together. + +If the goal is to back up an entire database en masse, this simple Node.js script is generally not the recommended approach. Instead, administrators should prefer [Firestore scheduled backups](https://cloud.google.com/firestore/docs/manage-data/scheduled-backups) or native Datastore export jobs. + +## Repository Structure + +- `index.js`: The main entry point and CLI wrapper. Uses `commander` to parse arguments and define commands for backing up, restoring, auditing (`list`), and testing restores. +- `lib/cmd.js`: Helper functions for parsing command line arguments and extracting things like Google Cloud project IDs and bucket names. +- `lib/datastore-backup.js`: Core programmatic logic that interfaces with `@google-cloud/datastore` to execute export operations and execute validation commands via `gcloud`. +- `test/`: Contains the test suite files (currently using Mocha + Should). Tests validate configuration parsing and command generation. +- `examples/`: Example configuration files like `backup-schedule.json`. + +## Technical Details + +- **Dependencies**: Uses `@google-cloud/datastore` for interacting with the Google Cloud Datastore API and executes some `gcloud` CLI commands via `child_process.execSync` for testing and restoration outputs. +- **Testing**: Test suite verifies the core configuration logic but does not automatically hit live GCP endpoints unless explicitly configured. +- **CI**: There is a GitHub Actions workflow that runs standard `npm test` checks. diff --git a/README.md b/README.md index fe18b88..b623e81 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # datastore-backup -npm script to backup/restore Google Cloud Datastore, across multiple projects +npm script to backup/restore Google Cloud Datastore, across multiple projects. + +> **Note**: This tool is designed primarily to backup a **subset of entities** by defining a schedule. If you are backing up an entire database, using [Firestore scheduled backups](https://cloud.google.com/firestore/docs/manage-data/scheduled-backups) or native export features is probably a better solution. ## Installation diff --git a/lib/cmd.js b/lib/cmd.js index d5ab7bd..34ec2a4 100644 --- a/lib/cmd.js +++ b/lib/cmd.js @@ -13,7 +13,9 @@ const child_process = require('child_process'); */ const validateFrequency = (backupSchedule, frequency) => { if (_.isUndefined(backupSchedule[frequency])) { - console.log(('Frequency value (' + frequency + ') unknown!').red); + const msg = 'Frequency value (' + frequency + ') unknown!'; + console.log(msg.red); + throw new Error(msg); } } diff --git a/test/cmd.js b/test/cmd.js index 6a5ec69..6c66287 100644 --- a/test/cmd.js +++ b/test/cmd.js @@ -1,19 +1,32 @@ require('should'); -const {getProjectId, getBackupBucket, validateFrequency} = require('../lib/cmd'); +const { getProjectId, getBackupBucket, validateFrequency } = require('../lib/cmd'); describe('getProjectId', () => { - it ('parses from program', () => { - getProjectId({projectId : 'some-project-id'}).should.equal('some-project-id'); + it('parses from program', () => { + getProjectId({ projectId: 'some-project-id' }).should.equal('some-project-id'); + }); +}); + +describe('validateFrequency', () => { + it('passes when frequency exists', () => { + (function () { + validateFrequency({ 'daily': ['Entity'] }, 'daily'); + }).should.not.throw(); }); + it('throws when frequency is missing', () => { + (function () { + validateFrequency({ 'daily': ['Entity'] }, 'weekly'); + }).should.throw(/unknown/); + }); }); describe('getBackupBucket', () => { - it ('parses from program', () => { + it('parses from program', () => { - getBackupBucket({bucketPrefix: 'project_backups'}, 'some-project-id', 'daily') + getBackupBucket({ bucketPrefix: 'project_backups' }, 'some-project-id', 'daily') .should.equal('project_backups_daily'); });