Skip to content

Commit 24d57cb

Browse files
Merge pull request #226 from JarvusInnovations/develop
Release: v2.2.0
2 parents d3ee0fc + 25e7cf5 commit 24d57cb

35 files changed

Lines changed: 2824 additions & 390 deletions

.github/workflows/rust-core.yml

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,6 @@ jobs:
3131
steps:
3232
- uses: actions/checkout@v7
3333

34-
# The transaction tests commit to throwaway gix repos; gix's update_ref
35-
# writes a reflog entry, which needs a committer identity from git config.
36-
# CI runners have none configured, so without this the reflog write fails
37-
# ("The reflog could not be created or updated"). Mirrors hologit's own
38-
# holo-tree-napi.yml. (Upstream follow-up: holo-tree's update_ref should
39-
# use the commit's identity for the reflog instead of ambient git config.)
40-
- name: Configure git identity (commit-bearing tests write reflogs)
41-
run: |
42-
git config --global user.name "gitsheets CI"
43-
git config --global user.email "ci@gitsheets.local"
44-
4534
- uses: dtolnay/rust-toolchain@stable
4635

4736
- uses: Swatinem/rust-cache@v2
@@ -79,13 +68,6 @@ jobs:
7968
steps:
8069
- uses: actions/checkout@v7
8170

82-
# Same reflog-identity requirement as the core job (the Sheet/Transaction
83-
# boundary tests commit to scratch repos).
84-
- name: Configure git identity (commit-bearing tests write reflogs)
85-
run: |
86-
git config --global user.name "gitsheets CI"
87-
git config --global user.email "ci@gitsheets.local"
88-
8971
- uses: actions/setup-node@v6
9072
with:
9173
node-version: ${{ matrix.node }}
@@ -141,14 +123,6 @@ jobs:
141123
steps:
142124
- uses: actions/checkout@v7
143125

144-
# The Sheet/Transaction commit tests + the cross-binding commit-parity test
145-
# commit to scratch repos, whose update_ref writes a reflog needing a
146-
# committer identity from git config (same requirement as the other jobs).
147-
- name: Configure git identity (commit-bearing tests write reflogs)
148-
run: |
149-
git config --global user.name "gitsheets CI"
150-
git config --global user.email "ci@gitsheets.local"
151-
152126
- uses: actions/setup-python@v6
153127
with:
154128
python-version: '3.11'

package-lock.json

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

packages/gitsheets-axi/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"dependencies": {
4646
"@toon-format/toon": "^2.2.0",
4747
"axi-sdk-js": "^0.1.6",
48+
"csv-stringify": "^6.8.0",
4849
"gitsheets": "*"
4950
},
5051
"devDependencies": {

packages/gitsheets-axi/src/cli.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ import { createContext, type GitsheetsContext } from './context.js';
77
import { homeCommand } from './commands/home.js';
88
import { sheetsCommand, SHEETS_HELP } from './commands/sheets.js';
99
import { queryCommand, QUERY_HELP } from './commands/query.js';
10+
import { countCommand, COUNT_HELP } from './commands/count.js';
11+
import { distinctCommand, DISTINCT_HELP } from './commands/distinct.js';
1012
import { readCommand, READ_HELP } from './commands/read.js';
1113
import { upsertCommand, UPSERT_HELP } from './commands/upsert.js';
1214
import { patchCommand, PATCH_HELP } from './commands/patch.js';
15+
import { renameCommand, RENAME_HELP } from './commands/rename.js';
1316
import { deleteCommand, DELETE_HELP } from './commands/delete.js';
1417
import { checkCommand, CHECK_HELP } from './commands/check.js';
1518
import { diffCommand, DIFF_HELP } from './commands/diff.js';
@@ -28,35 +31,37 @@ const DESCRIPTION =
2831
const VERSION = readPackageVersion();
2932

3033
export const TOP_HELP = `usage: gitsheets-axi [command] [args] [flags]
31-
commands[16]:
32-
(none)=home, sheets, query, read,
33-
upsert, patch, delete,
34-
check, diff, normalize,
34+
commands[19]:
35+
(none)=home, sheets, query, count,
36+
distinct, read, upsert, patch, rename,
37+
delete, check, diff, normalize,
3538
init, infer, migrate-config,
3639
attachment, push, setup
3740
flags[2]:
3841
--help, -v/-V/--version
3942
examples:
4043
gitsheets-axi
41-
gitsheets-axi query users --filter status=active
44+
gitsheets-axi query repos --filter status=unclassified --sort pushed_at
45+
gitsheets-axi count repos --filter archived=true
46+
gitsheets-axi query repos --group-by target_team
47+
gitsheets-axi distinct repos disposition
4248
gitsheets-axi upsert users --data '{"slug":"jane","email":"jane@x.org"}'
4349
gitsheets-axi patch users '{"slug":"jane"}' --patch '{"name":"Jane"}'
4450
gitsheets-axi check users users/jane.toml --fix
4551
gitsheets-axi diff posts HEAD~10
46-
gitsheets-axi normalize users
47-
gitsheets-axi init users
48-
gitsheets-axi infer users
4952
gitsheets-axi attachment list users jane
50-
gitsheets-axi push
5153
gitsheets-axi setup hooks
5254
`;
5355

5456
const COMMAND_HELP: Record<string, string> = {
5557
sheets: SHEETS_HELP,
5658
query: QUERY_HELP,
59+
count: COUNT_HELP,
60+
distinct: DISTINCT_HELP,
5761
read: READ_HELP,
5862
upsert: UPSERT_HELP,
5963
patch: PATCH_HELP,
64+
rename: RENAME_HELP,
6065
delete: DELETE_HELP,
6166
check: CHECK_HELP,
6267
diff: DIFF_HELP,
@@ -74,9 +79,12 @@ type CommandFn = (args: string[], ctx: GitsheetsContext) => Promise<string | Rec
7479
const COMMANDS: Record<string, CommandFn> = {
7580
sheets: sheetsCommand,
7681
query: queryCommand,
82+
count: countCommand,
83+
distinct: distinctCommand,
7784
read: readCommand,
7885
upsert: upsertCommand,
7986
patch: patchCommand,
87+
rename: renameCommand,
8088
delete: deleteCommand,
8189
check: checkCommand,
8290
diff: diffCommand,
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { AxiError } from 'axi-sdk-js';
2+
3+
import type { GitsheetsContext } from '../context.js';
4+
import { translateError } from '../errors.js';
5+
import { renderObject } from '../output/render.js';
6+
import { openSheetForCommand } from '../util/open-sheet.js';
7+
import { buildPredicate } from '../util/filter.js';
8+
9+
export const COUNT_HELP = `usage: gitsheets-axi count <sheet> [--filter k=v ...] [--prefix p]
10+
flags[2]:
11+
--filter <expr> Filter clause (repeatable). k=v · k!=v · k<v · k>v ·
12+
k<=v · k>=v · k~regex · "k in (a,b)" · k:present · k:empty
13+
--prefix <p> Tenant sub-tree scope
14+
examples:
15+
gitsheets-axi count repos
16+
gitsheets-axi count repos --filter status=unclassified
17+
gitsheets-axi count repos --filter 'pushed_at<2022-01-01' --filter archived=false
18+
note:
19+
With no --filter, counts candidate record paths without parsing (cheap).
20+
A filter scans body-less records and counts matches.
21+
`;
22+
23+
interface CountFlags {
24+
sheet: string;
25+
filters: string[];
26+
prefix: string | undefined;
27+
}
28+
29+
function parseCountFlags(args: string[]): CountFlags {
30+
const sheet = args[0];
31+
if (!sheet || sheet.startsWith('-')) {
32+
throw new AxiError('count requires a sheet name', 'VALIDATION_ERROR', [
33+
'Run `gitsheets-axi count <sheet>`',
34+
]);
35+
}
36+
const flags: CountFlags = { sheet, filters: [], prefix: undefined };
37+
for (let i = 1; i < args.length; i++) {
38+
const arg = args[i];
39+
const next = args[i + 1];
40+
switch (arg) {
41+
case '--filter':
42+
if (!next) throw new AxiError('--filter expects an expression', 'VALIDATION_ERROR');
43+
flags.filters.push(next);
44+
i++;
45+
break;
46+
case '--prefix':
47+
if (!next) throw new AxiError('--prefix expects a path', 'VALIDATION_ERROR');
48+
flags.prefix = next;
49+
i++;
50+
break;
51+
default:
52+
throw new AxiError(`Unknown flag: ${arg}`, 'VALIDATION_ERROR', [
53+
'Run `gitsheets-axi count --help`',
54+
]);
55+
}
56+
}
57+
return flags;
58+
}
59+
60+
export async function countCommand(
61+
args: string[],
62+
ctx: GitsheetsContext,
63+
): Promise<string> {
64+
if (args.length === 0 || (args.length === 1 && args[0] === '--help')) {
65+
return COUNT_HELP;
66+
}
67+
68+
const flags = parseCountFlags(args);
69+
const repo = await ctx.repo();
70+
const sheet = await openSheetForCommand(
71+
repo,
72+
flags.sheet,
73+
flags.prefix !== undefined ? { prefix: flags.prefix } : {},
74+
);
75+
76+
// No filter → the cheap candidate-path count.
77+
if (flags.filters.length === 0) {
78+
try {
79+
return renderObject({ sheet: flags.sheet, count: await sheet.count() });
80+
} catch (error) {
81+
throw translateError(error);
82+
}
83+
}
84+
85+
// Filtered → scan body-less and count matches, reporting the total too.
86+
const predicate = buildPredicate(flags.filters);
87+
let matched = 0;
88+
let total = 0;
89+
try {
90+
const records = await sheet.queryAll({}, { withBody: false });
91+
total = records.length;
92+
for (const r of records) {
93+
if (predicate(r as Record<string, unknown>)) matched++;
94+
}
95+
} catch (error) {
96+
throw translateError(error);
97+
}
98+
return renderObject({ sheet: flags.sheet, count: matched, of: total });
99+
}

0 commit comments

Comments
 (0)