Compare commits

..
Author SHA1 Message Date
CircleCI aa13c1367e Bump version to 1.0.0-RC.6 [skip ci] 2026-10-14 12:26:21 +00:00
james.maesandClaude Opus 5.5 660278e020 Merge develop into release/1.0 for 1.0.0-RC.6 (#21 UI API extension, guardrails)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:22:53 -05:00
CircleCI 6a0eae112c Bump version to 1.0.0-RC.5 [skip ci] 2026-10-12 21:28:20 +00:00
james.maesandClaude Opus 5.5 bb43871212 Merge develop into release/1.0 for 1.0.0-RC.5
Users get sql_workbench_result_snapshot.insert, so pinning results works
under core permission enforcement (#16).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:26:05 -05:00
CircleCI a0e3c222f7 Bump version to 1.0.0-RC.4 [skip ci] 2026-10-12 21:13:23 +00:00
james.maesandClaude Opus 5.5 b531d9c046 Merge develop into release/1.0 for 1.0.0-RC.4
snake_case names (#7), app home and Runner (#9), record developer panel
(#6) and record-screen polish (#14). 1.0.0-RC.3 on Central is an
accidental republish of RC.2, so this candidate is RC.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:10:45 -05:00
james.maesandClaude Opus 5.5 763c0e4352 ci: fix the GA tag filter YAML (no brackets in the flow sequence) [skip ci]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:01:51 -05:00
james.maesandClaude Opus 5.5 d0153ebd9c Set version to 1.0.0-RC.3 and publish only GA tags [skip ci]
1.0.0-RC.3 is already on Maven Central (an RC tag build republished the previous code), so the next release-branch build must publish the following RC. RC tags no longer trigger a publish (/^v[0-9]+\.[0-9]+\.[0-9]+$/ only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:56:55 -05:00
james.maesandClaude Opus 5.5 0d8af84fbc Bump version to 1.0.0-RC.2 [skip ci]
The orb published 1.0.0-RC.2 but could not push this commit (the
CircleCI deploy key is read only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:39:06 -05:00
james.maesandClaude Opus 5.5 0d06a04953 Merge develop into release/1.0 for 1.0.0-RC.2
Sets <revision> to 1.0.0-RC.1, the version already on Maven Central,
because RC.1's version bump never landed on release/1.0. The orb
increments an existing RC, so this publishes 1.0.0-RC.2 instead of a
duplicate RC.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:27:56 -05:00
5 changed files with 2 additions and 110 deletions

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
version: 2.1
orbs:
qqq-orb: kingsrook/qqq-orb@0.6.9
qqq-orb: kingsrook/qqq-orb@0.6.8
commands:
configure-package-install:
-4
View File
@@ -74,8 +74,6 @@ CREATE TABLE IF NOT EXISTS sql_workbench_result_snapshot (
CONSTRAINT pk_sql_workbench_result_snapshot PRIMARY KEY (id),
CONSTRAINT fk_sql_workbench_result_snapshot_notebook FOREIGN KEY (notebook_id) REFERENCES sql_workbench_notebook (id)
);
-- the notebook page lists its snapshots, and deleting a notebook deletes them, by notebook_id
CREATE INDEX IF NOT EXISTS ix_sql_workbench_result_snapshot_notebook ON sql_workbench_result_snapshot (notebook_id);
-- Shares (1.0.0-RC.2): who, besides the owner, may read a notebook.
-- audience_key is "user:<id>", "group:<name>" or "all" (the shared library);
@@ -135,5 +133,3 @@ CREATE TABLE IF NOT EXISTS sql_workbench_execution (
);
CREATE INDEX IF NOT EXISTS ix_sql_workbench_execution_user ON sql_workbench_execution (user_id, id);
CREATE INDEX IF NOT EXISTS ix_sql_workbench_execution_start ON sql_workbench_execution (start_time);
-- the notebook page's "Runs" list joins executions by notebook_id
CREATE INDEX IF NOT EXISTS ix_sql_workbench_execution_notebook ON sql_workbench_execution (notebook_id);
+1 -1
View File
@@ -20,7 +20,7 @@
<properties>
<qbit.guardrails.enforce>true</qbit.guardrails.enforce>
<qbit.guardrails.strictUiApiRoutes>true</qbit.guardrails.strictUiApiRoutes>
<revision>1.0.0-SNAPSHOT</revision>
<revision>1.0.0-RC.6</revision>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<jacoco.version>0.8.15</jacoco.version>
@@ -203,106 +203,6 @@ class PostgresIntegrationTest
/*******************************************************************************
** The shipped DDL gives every foreign-key column and the notebook_id the
** "Runs" list joins on a leading index, so notebook pages and notebook
** deletes do not scan the child tables.
*******************************************************************************/
@Test
void testShippedDdlIndexesNotebookChildColumns() throws Exception
{
String sql = """
SELECT c.conrelid::regclass::text || '.' || a.attname
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1]
WHERE c.contype = 'f' AND c.conrelid::regclass::text LIKE 'sql_workbench_%'
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE i.indrelid = c.conrelid AND i.indkey[0] = c.conkey[1])
""";
try(Connection connection = java.sql.DriverManager.getConnection(POSTGRES.getJdbcUrl(), POSTGRES.getUsername(), POSTGRES.getPassword());
Statement statement = connection.createStatement())
{
java.util.List<String> unindexed = new java.util.ArrayList<>();
try(java.sql.ResultSet result = statement.executeQuery(sql))
{
while(result.next())
{
unindexed.add(result.getString(1));
}
}
assertThat(unindexed).isEmpty();
try(java.sql.ResultSet result = statement.executeQuery("SELECT 1 FROM pg_indexes WHERE indexname = 'ix_sql_workbench_execution_notebook'"))
{
assertThat(result.next()).isTrue();
}
}
}
/*******************************************************************************
** Referential integrity guardrail: every join and every table possible-value
** reference between the workbench's tables has a foreign key in the shipped
** DDL, or an exemption with its reason. A new reference without a
** constraint fails here.
*******************************************************************************/
@Test
void testShippedDdlHasAForeignKeyForEveryReference() throws Exception
{
///////////////////////////////////////////////////////////////////////////
// "child.column" -> why it is deliberately not a foreign key //
///////////////////////////////////////////////////////////////////////////
Map<String, String> exempt = Map.of(
"sql_workbench_execution.notebook_id", "the execution audit is append-only and outlives its notebook: no CASCADE into it, "
+ "RESTRICT would block deleting any notebook that ever ran, SET NULL would rewrite audit rows, and an execution records "
+ "the client's notebook id even if the notebook was deleted meanwhile (the audit insert must never fail)");
java.util.Set<String> references = new java.util.TreeSet<>();
for(com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData table : qInstance.getTables().values())
{
for(com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData field : table.getFields().values())
{
if(field.getPossibleValueSourceName() != null && qInstance.getTable(field.getPossibleValueSourceName()) != null)
{
references.add(table.getName() + "." + field.getBackendName());
}
}
}
for(com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData join : qInstance.getJoins().values())
{
////////////////////////////////////////////////////////////////////////
// the referencing side is the one whose field is not its primary key //
////////////////////////////////////////////////////////////////////////
com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData left = qInstance.getTable(join.getLeftTable());
com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData right = qInstance.getTable(join.getRightTable());
join.getJoinOns().forEach(on -> references.add(on.getRightField().equals(right.getPrimaryKeyField())
? left.getName() + "." + left.getField(on.getLeftField()).getBackendName()
: right.getName() + "." + right.getField(on.getRightField()).getBackendName()));
}
assertThat(references).as("the workbench's references").contains("sql_workbench_cell.notebook_id", "sql_workbench_execution.notebook_id");
java.util.Set<String> foreignKeys = new java.util.HashSet<>();
try(Connection connection = java.sql.DriverManager.getConnection(POSTGRES.getJdbcUrl(), POSTGRES.getUsername(), POSTGRES.getPassword());
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("""
SELECT c.conrelid::regclass::text || '.' || a.attname
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = c.conkey[1]
WHERE c.contype = 'f'
"""))
{
while(result.next())
{
foreignKeys.add(result.getString(1));
}
}
java.util.List<String> missing = references.stream().filter(r -> !foreignKeys.contains(r) && !exempt.containsKey(r)).toList();
assertThat(missing).as("references without a foreign key (add one to docs/ddl/postgres.sql, or an exemption with a reason)").isEmpty();
assertThat(foreignKeys).as("exemptions that now have a foreign key").doesNotContainAnyElementsOf(exempt.keySet());
}
/*******************************************************************************
** The database itself refuses writes in a workbench read-only session,
** even for SQL the classifier would never let through.
-4
View File
@@ -70,8 +70,6 @@ CREATE TABLE IF NOT EXISTS sql_workbench_result_snapshot (
CONSTRAINT pk_sql_workbench_result_snapshot PRIMARY KEY (id),
CONSTRAINT fk_sql_workbench_result_snapshot_notebook FOREIGN KEY (notebook_id) REFERENCES sql_workbench_notebook (id)
);
-- the notebook page lists its snapshots, and deleting a notebook deletes them, by notebook_id
CREATE INDEX IF NOT EXISTS ix_sql_workbench_result_snapshot_notebook ON sql_workbench_result_snapshot (notebook_id);
CREATE TABLE IF NOT EXISTS sql_workbench_notebook_share (
id INTEGER AUTO_INCREMENT NOT NULL,
@@ -119,8 +117,6 @@ CREATE TABLE IF NOT EXISTS sql_workbench_execution (
);
CREATE INDEX IF NOT EXISTS ix_sql_workbench_execution_user ON sql_workbench_execution (user_id, id);
CREATE INDEX IF NOT EXISTS ix_sql_workbench_execution_start ON sql_workbench_execution (start_time);
-- the notebook page's "Runs" list joins executions by notebook_id
CREATE INDEX IF NOT EXISTS ix_sql_workbench_execution_notebook ON sql_workbench_execution (notebook_id);
CREATE TABLE person (
id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,