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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Log

## 10.0.1

* Fix CLI Dart model generation issues
* Fix row permissions and security sync
* Fix error when pushing columns with relationships
* Fix resource name from attributes to columns for TablesDB indexes

## 10.0.0

* **Breaking:** Removed Avatars CLI command and all related subcommands; corresponding examples deleted
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Once the installation is complete, you can verify the install using

```sh
$ appwrite -v
10.0.0
10.0.1
```

### Install using prebuilt binaries
Expand Down Expand Up @@ -60,7 +60,7 @@ $ scoop install https://raw.githubusercontent.com/appwrite/sdk-for-cli/master/sc
Once the installation completes, you can verify your install using
```
$ appwrite -v
10.0.0
10.0.1
```

## Getting Started
Expand Down
4 changes: 2 additions & 2 deletions install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
# You can use "View source" of this page to see the full script.

# REPO
$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/10.0.0/appwrite-cli-win-x64.exe"
$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/10.0.0/appwrite-cli-win-arm64.exe"
$GITHUB_x64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/10.0.1/appwrite-cli-win-x64.exe"
$GITHUB_arm64_URL = "https://github.com/appwrite/sdk-for-cli/releases/download/10.0.1/appwrite-cli-win-arm64.exe"

$APPWRITE_BINARY_NAME = "appwrite.exe"

Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ printSuccess() {
downloadBinary() {
echo "[2/4] Downloading executable for $OS ($ARCH) ..."

GITHUB_LATEST_VERSION="10.0.0"
GITHUB_LATEST_VERSION="10.0.1"
GITHUB_FILE="appwrite-cli-${OS}-${ARCH}"
GITHUB_URL="https://github.com/$GITHUB_REPOSITORY_NAME/releases/download/$GITHUB_LATEST_VERSION/$GITHUB_FILE"

Expand Down
4 changes: 2 additions & 2 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ class Client {
'x-sdk-name': 'Command Line',
'x-sdk-platform': 'console',
'x-sdk-language': 'cli',
'x-sdk-version': '10.0.0',
'user-agent' : `AppwriteCLI/10.0.0 (${os.type()} ${os.version()}; ${os.arch()})`,
'x-sdk-version': '10.0.1',
'user-agent' : `AppwriteCLI/10.0.1 (${os.type()} ${os.version()}; ${os.arch()})`,
Comment on lines +19 to +20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid hardcoding the version strings.

The version is hardcoded in the SDK headers. This creates maintenance burden and risks version drift. Consider importing the version from package.json for consistency.

Apply this approach:

+const { version } = require('../package.json');
+
 class Client {
   CHUNK_SIZE = 5*1024*1024; // 5MB
 
   constructor() {
     this.endpoint = 'https://cloud.appwrite.io/v1';
     this.headers = {
       'content-type': '',
       'x-sdk-name': 'Command Line',
       'x-sdk-platform': 'console',
       'x-sdk-language': 'cli',
-      'x-sdk-version': '10.0.1',
-      'user-agent' : `AppwriteCLI/10.0.1 (${os.type()} ${os.version()}; ${os.arch()})`,
+      'x-sdk-version': version,
+      'user-agent' : `AppwriteCLI/${version} (${os.type()} ${os.version()}; ${os.arch()})`,
       'X-Appwrite-Response-Format' : '1.8.0',
     };
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
'x-sdk-version': '10.0.1',
'user-agent' : `AppwriteCLI/10.0.1 (${os.type()} ${os.version()}; ${os.arch()})`,
// lib/client.js
const os = require('os');
const { version } = require('../package.json');
class Client {
CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
constructor() {
this.endpoint = 'https://cloud.appwrite.io/v1';
this.headers = {
'content-type': '',
'x-sdk-name': 'Command Line',
'x-sdk-platform': 'console',
'x-sdk-language': 'cli',
- 'x-sdk-version': '10.0.1',
'x-sdk-version': version,
'user-agent': `AppwriteCLI/${version} (${os.type()} ${os.version()}; ${os.arch()})`,
'X-Appwrite-Response-Format': '1.8.0',
};
}
// ... rest of the class
}
🤖 Prompt for AI Agents
In lib/client.js around lines 19 to 20, the SDK and User-Agent version strings
are hardcoded which can drift; import the package version (e.g.,
require('../package.json').version or appropriate path) at top of the file and
replace the literal '10.0.1' occurrences with that imported variable so both
'x-sdk-version' and the User-Agent interpolate the package version dynamically.

'X-Appwrite-Response-Format' : '1.8.0',
};
}
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,7 @@ const projectsListPlatforms = async ({projectId,parseOutput = true, overrideForC
/**
* @typedef {Object} ProjectsCreatePlatformRequestParams
* @property {string} projectId Project unique ID.
* @property {PlatformType} type Platform type.
* @property {PlatformType} type Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.
* @property {string} name Platform name. Max length: 128 chars.
* @property {string} key Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.
* @property {string} store App store or Google Play store ID. Max length: 256 chars.
Expand Down Expand Up @@ -2354,7 +2354,7 @@ projects
.command(`create-platform`)
.description(`Create a new platform for your project. Use this endpoint to register a new platform where your users will run your application which will interact with the Appwrite API.`)
.requiredOption(`--project-id <project-id>`, `Project unique ID.`)
.requiredOption(`--type <type>`, `Platform type.`)
.requiredOption(`--type <type>`, `Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.`)
.requiredOption(`--name <name>`, `Platform name. Max length: 128 chars.`)
.option(`--key <key>`, `Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.`)
.option(`--store <store>`, `App store or Google Play store ID. Max length: 256 chars.`)
Expand Down
4 changes: 2 additions & 2 deletions lib/commands/pull.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ const pullTable = async () => {
});
if (fetchResponse["databases"].length <= 0) {
log("No tables found.");
success(`Successfully pulled ${chalk.bold(totalTables)} tables from ${chalk.bold(totalTablesDBs)} tables databases.`);
success(`Successfully pulled ${chalk.bold(totalTables)} tables from ${chalk.bold(totalTablesDBs)} tableDBs.`);
return;
}

Expand Down Expand Up @@ -398,7 +398,7 @@ const pullTable = async () => {
}
}

success(`Successfully pulled ${chalk.bold(totalTables)} tables from ${chalk.bold(totalTablesDBs)} tables databases.`);
success(`Successfully pulled ${chalk.bold(totalTables)} tables from ${chalk.bold(totalTablesDBs)} tableDBs.`);
}

const pullBucket = async () => {
Expand Down
49 changes: 32 additions & 17 deletions lib/commands/push.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ const {
databasesUpdateCollection
} = require("./databases");
const {
tablesDBCreate,
tablesDBGet,
tablesDBUpdate,
tablesDBCreateTable,
tablesDBGetTable,
tablesDBUpdateTable,
tablesDBCreateTable
tablesDBUpdateTable
} = require("./tables-db");
const {
storageGetBucket, storageUpdateBucket, storageCreateBucket
Expand Down Expand Up @@ -548,7 +550,7 @@ const createAttribute = (databaseId, collectionId, attribute) => {
return databasesCreateRelationshipAttribute({
databaseId,
collectionId,
relatedCollectionId: attribute.relatedCollection,
relatedCollectionId: attribute.relatedTable ?? attribute.relatedCollection,
type: attribute.relationType,
twoWay: attribute.twoWay,
key: attribute.key,
Expand Down Expand Up @@ -667,7 +669,7 @@ const updateAttribute = (databaseId, collectionId, attribute) => {
return databasesUpdateRelationshipAttribute({
databaseId,
collectionId,
relatedCollectionId: attribute.relatedCollection,
relatedCollectionId: attribute.relatedTable ?? attribute.relatedCollection,
type: attribute.relationType,
twoWay: attribute.twoWay,
key: attribute.key,
Expand Down Expand Up @@ -881,7 +883,7 @@ const createIndexes = async (indexes, collection) => {
collectionId: collection['$id'],
key: index.key,
type: index.type,
attributes: index.attributes,
attributes: index.columns ?? index.attributes,
orders: index.orders,
parseOutput: false
});
Expand Down Expand Up @@ -1730,7 +1732,7 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =

const databases = Array.from(new Set(tables.map(table => table['databaseId'])));

// Parallel db actions
// Parallel tablesDB actions
await Promise.all(databases.map(async (databaseId) => {
const localDatabase = localConfig.getTablesDB(databaseId);

Expand All @@ -1741,7 +1743,7 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =
});

if (database.name !== (localDatabase.name ?? databaseId)) {
await databasesUpdate({
await tablesDBUpdate({
databaseId: databaseId,
name: localDatabase.name ?? databaseId,
parseOutput: false
Expand All @@ -1752,7 +1754,7 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =
} catch (err) {
log(`Database ${databaseId} not found. Creating it now ...`);

await databasesCreate({
await tablesDBCreate({
databaseId: databaseId,
name: localDatabase.name ?? databaseId,
parseOutput: false,
Expand All @@ -1761,10 +1763,12 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =
}));


if (!(await approveChanges(tables, tablesDBGetTable, KeysTable, 'tableId', 'tables', ['columns', 'indexes'], 'databaseId', 'databaseId',))) {
if (!(await approveChanges(tables, tablesDBGetTable, KeysTable, 'tableId', 'tables', ['columns', 'indexes'], 'databaseId', 'databaseId'))) {
return;
}
// Parallel collection actions
let tablesChanged = new Set();

// Parallel tables actions
await Promise.all(tables.map(async (table) => {
try {
const remoteTable = await tablesDBGetTable({
Expand All @@ -1773,15 +1777,23 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =
parseOutput: false,
});

if (remoteTable.name !== table.name) {
const changes = [];
if (remoteTable.name !== table.name) changes.push('name');
if (remoteTable.rowSecurity !== table.rowSecurity) changes.push('rowSecurity');
if (JSON.stringify(remoteTable['$permissions']) !== JSON.stringify(table['$permissions'])) changes.push('permissions');

if (changes.length > 0) {
await tablesDBUpdateTable({
databaseId: table['databaseId'],
tableId: table['$id'],
name: table.name,
parseOutput: false
parseOutput: false,
rowSecurity: table.rowSecurity,
permissions: table['$permissions']
})
Comment on lines +1781 to 1793
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Don't drop table enabled updates

We no longer propagate the table enabled flag when pushing. Any change to enabled in appwrite.config.json now silently does nothing, and newly created tables always come up with the API default instead of the configured state. Please wire the flag back into the change detection and into the create/update payloads so pushes keep tables enabled/disabled as intended.

Apply this diff to restore the missing behavior:

             const changes = [];
             if (remoteTable.name !== table.name) changes.push('name');
             if (remoteTable.rowSecurity !== table.rowSecurity) changes.push('rowSecurity');
+            if (remoteTable.enabled !== table.enabled) changes.push('enabled');
             if (JSON.stringify(remoteTable['$permissions']) !== JSON.stringify(table['$permissions'])) changes.push('permissions');

             if (changes.length > 0) {
                 await tablesDBUpdateTable({
                     databaseId: table['databaseId'],
                     tableId: table['$id'],
                     name: table.name,
                     parseOutput: false,
                     rowSecurity: table.rowSecurity,
-                    permissions: table['$permissions']
+                    permissions: table['$permissions'],
+                    enabled: table.enabled
                 })
@@
                 await tablesDBCreateTable({
                     databaseId: table['databaseId'],
                     tableId: table['$id'],
                     name: table.name,
                     rowSecurity: table.rowSecurity,
-                    permissions: table['$permissions'],
+                    permissions: table['$permissions'],
+                    enabled: table.enabled,
                     parseOutput: false
                 })

Also applies to: 1805-1811

🤖 Prompt for AI Agents
In lib/commands/push.js around lines 1781-1793 (and similarly 1805-1811), the
table 'enabled' flag is not being compared or sent to the API; add a comparison
that pushes 'enabled' into the changes array when remoteTable.enabled !==
table.enabled, and include enabled: table.enabled in the payload passed to
tablesDBUpdateTable (and the equivalent create call around 1805-1811) so both
update and create requests carry the configured enabled state.


success(`Updated ${table.name} ( ${table['$id']} ) name`);
success(`Updated ${table.name} ( ${table['$id']} ) - ${changes.join(', ')}`);
tablesChanged.add(table['$id']);
}
table.remoteVersion = remoteTable;

Expand All @@ -1794,16 +1806,19 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =
databaseId: table['databaseId'],
tableId: table['$id'],
name: table.name,
documentSecurity: table.documentSecurity,
rowSecurity: table.rowSecurity,
permissions: table['$permissions'],
parseOutput: false
})

success(`Created ${table.name} ( ${table['$id']} )`);
tablesChanged.add(table['$id']);
} else {
throw e;
}
}
}))
let numberOfTables = 0;

// Serialize attribute actions
for (let table of tables) {
let columns = table.columns;
Expand Down Expand Up @@ -1831,11 +1846,11 @@ const pushTable = async ({ returnOnZero, attempts } = { returnOnZero: false }) =
} catch (e) {
throw e;
}
numberOfTables++;
tablesChanged.add(table['$id']);
success(`Successfully pushed ${table.name} ( ${table['$id']} )`);
}

success(`Successfully pushed ${numberOfTables} tables`);
success(`Successfully pushed ${tablesChanged.size} tables`);
}

const pushCollection = async ({ returnOnZero, attempts } = { returnOnZero: false }) => {
Expand Down
7 changes: 4 additions & 3 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const KeysSite = new Set(["path", "$id", "name", "enabled", "logging", "timeout"
const KeysFunction = new Set(["path", "$id", "execute", "name", "enabled", "logging", "runtime", "specification", "scopes", "events", "schedule", "timeout", "entrypoint", "commands", "vars"]);
const KeysDatabase = new Set(["$id", "name", "enabled"]);
const KeysCollection = new Set(["$id", "$permissions", "databaseId", "name", "enabled", "documentSecurity", "attributes", "indexes"]);
const KeysTable = new Set(["$id", "$permissions", "databaseId", "name", "enabled", "documentSecurity", "columns", "indexes"]);
const KeysTable = new Set(["$id", "$permissions", "databaseId", "name", "enabled", "rowSecurity", "columns", "indexes"]);
const KeysStorage = new Set(["$id", "$permissions", "fileSecurity", "name", "enabled", "maximumFileSize", "allowedFileExtensions", "compression", "encryption", "antivirus"]);
const KeysTopics = new Set(["$id", "name", "subscribe"]);
const KeysTeams = new Set(["$id", "name"]);
Expand Down Expand Up @@ -62,12 +62,13 @@ const KeysColumns = new Set([
"onDelete",
"side",
// Indexes
"attributes",
"columns",
"orders",
// Strings
"encrypt",
]);
const KeyIndexes = new Set(["key", "type", "status", "attributes", "orders"]);
const KeyIndexesColumns = new Set(["key", "type", "status", "columns", "orders"]);

function whitelistKeys(value, keys, nestedKeys = {}) {
if (Array.isArray(value)) {
Expand Down Expand Up @@ -404,7 +405,7 @@ class Local extends Config {
addTable(props) {
props = whitelistKeys(props, KeysTable, {
columns: KeysColumns,
indexes: KeyIndexes
indexes: KeyIndexesColumns
});

if (!this.has("tables")) {
Expand Down
2 changes: 1 addition & 1 deletion lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const parseError = (err) => {
} catch {
}

const version = '10.0.0';
const version = '10.0.1';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid hardcoding the version string.

The version is hardcoded here but package.json is already imported at the top of the file (line 4). Hardcoding creates a maintenance burden and risks version drift between this file and package.json.

Apply this diff to use the version from package.json:

+const { description, version } = require('../package.json');
-const { description } = require('../package.json');

Then update line 125:

-            const version = '10.0.1';
+            // version is now imported from package.json above
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const version = '10.0.1';
// lib/parser.js
// (near line 4)
const { description, version } = require('../package.json');
// …
// (around line 125)
// version is now imported from package.json above
🤖 Prompt for AI Agents
In lib/parser.js around line 125, the version is hardcoded as '10.0.1' which can
drift from package.json (already imported at line 4); replace the hardcoded
assignment with a reference to the imported package.json version (e.g., use the
imported package object’s version property) so the file reads the version from
package.json instead of a literal string.

const stepsToReproduce = `Running \`appwrite ${cliConfig.reportData.data.args.join(' ')}\``;
const yourEnvironment = `CLI version: ${version}\nOperation System: ${os.type()}\nAppwrite version: ${appwriteVersion}\nIs Cloud: ${isCloud()}`;

Expand Down
Loading