snapshot of https://github.com/marmelab/atomic-crm.git (main, 2026-09-18; blobs > 131072B omitted)
Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s
Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s
This commit is contained in:
100
scripts/generate-registry.mjs
Executable file
100
scripts/generate-registry.mjs
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { globSync } from "glob";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const registryPath = "registry.json";
|
||||
const basePath = "src";
|
||||
const atomicCrmComponentsPath = path.join(basePath, "components", "atomic-crm");
|
||||
const supabaseComponentsPath = path.join(basePath, "components", "supabase");
|
||||
const hooksPath = path.join(basePath, "hooks");
|
||||
const libPath = path.join(basePath, "lib");
|
||||
|
||||
const excludedHooks = [
|
||||
"filter-context.tsx",
|
||||
"saved-queries.tsx",
|
||||
"use-mobile.ts",
|
||||
"useSupportCreateSuggestion.tsx",
|
||||
];
|
||||
|
||||
const excludedLibFiles = [
|
||||
"field.type.ts",
|
||||
"genericMemo.ts",
|
||||
"i18nProvider.ts",
|
||||
"sanitizeInputRestProps.ts",
|
||||
"utils.ts",
|
||||
];
|
||||
|
||||
const testFilePattern = "**/*.{test,spec}.*";
|
||||
const storyFilePattern = "**/*.stories.*";
|
||||
|
||||
const atomicCrmComponents = globSync(
|
||||
path.join(atomicCrmComponentsPath, "**", "*.ts*"),
|
||||
{ ignore: [testFilePattern, storyFilePattern] },
|
||||
);
|
||||
const supabaseComponents = globSync(
|
||||
path.join(supabaseComponentsPath, "**", "*.ts*"),
|
||||
{ ignore: [testFilePattern, storyFilePattern] },
|
||||
);
|
||||
const hooks = globSync(path.join(hooksPath, "**", "*.ts*")).filter((hook) => {
|
||||
return !excludedHooks.includes(path.basename(hook));
|
||||
});
|
||||
const libFiles = globSync(path.join(libPath, "**", "*.ts*")).filter((file) => {
|
||||
return !excludedLibFiles.includes(path.basename(file));
|
||||
});
|
||||
const changelogPath = "CHANGELOG.md";
|
||||
|
||||
const registryContent = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
|
||||
|
||||
const files = [
|
||||
...atomicCrmComponents.map((path) => {
|
||||
return {
|
||||
path,
|
||||
type: "registry:component",
|
||||
};
|
||||
}),
|
||||
...supabaseComponents.map((path) => {
|
||||
return {
|
||||
path,
|
||||
type: "registry:component",
|
||||
};
|
||||
}),
|
||||
...hooks.map((path) => {
|
||||
return {
|
||||
path,
|
||||
type: "registry:hook",
|
||||
};
|
||||
}),
|
||||
...libFiles.map((path) => {
|
||||
return {
|
||||
path,
|
||||
type: "registry:lib",
|
||||
};
|
||||
}),
|
||||
{
|
||||
path: changelogPath,
|
||||
type: "registry:file",
|
||||
target: "~/CHANGELOG.md",
|
||||
},
|
||||
];
|
||||
|
||||
const newRegistryContent = {
|
||||
...registryContent,
|
||||
items: registryContent.items.map((item) => {
|
||||
if (item.name === "atomic-crm") {
|
||||
return {
|
||||
...item,
|
||||
files,
|
||||
};
|
||||
}
|
||||
|
||||
return item;
|
||||
}),
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
registryPath,
|
||||
JSON.stringify(newRegistryContent, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
14
scripts/ghpages-deploy.mjs
Normal file
14
scripts/ghpages-deploy.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import * as ghpages from "gh-pages";
|
||||
|
||||
ghpages.publish(
|
||||
"dist",
|
||||
{
|
||||
branch: process.env.DEPLOY_BRANCH || "gh-pages",
|
||||
repo: process.env.DEPLOY_REPO_URL || undefined,
|
||||
},
|
||||
function (err) {
|
||||
if (err) {
|
||||
console.error("Failed to deploy to GitHub Pages", err);
|
||||
}
|
||||
},
|
||||
);
|
||||
340
scripts/supabase-remote-init.mjs
Normal file
340
scripts/supabase-remote-init.mjs
Normal file
@@ -0,0 +1,340 @@
|
||||
import { input, select } from "@inquirer/prompts";
|
||||
import { execa } from "execa";
|
||||
import fs from "node:fs";
|
||||
|
||||
(async () => {
|
||||
await loginToSupabase();
|
||||
const projectName = await input({
|
||||
message: "Enter the name of the project:",
|
||||
default: "Atomic CRM",
|
||||
});
|
||||
const databasePassword = await input({
|
||||
message: "Enter a database password:",
|
||||
default: generatePassword(16),
|
||||
});
|
||||
|
||||
const organizationId = await selectOrganization();
|
||||
|
||||
const region = await selectRegion();
|
||||
|
||||
const projectRef = await createProject({
|
||||
projectName,
|
||||
databasePassword,
|
||||
organizationId,
|
||||
region,
|
||||
});
|
||||
await waitForProjectToBeReady({ projectRef });
|
||||
|
||||
// This also ensures the project is ready
|
||||
const { publishableKey } = await fetchApiKeys({
|
||||
projectRef,
|
||||
});
|
||||
|
||||
await linkProject({
|
||||
projectRef,
|
||||
databasePassword,
|
||||
});
|
||||
|
||||
await setupDatabase({
|
||||
databasePassword,
|
||||
});
|
||||
|
||||
await setupSupabaseSecrets({
|
||||
projectRef,
|
||||
publishableKey,
|
||||
});
|
||||
|
||||
await persistSupabaseEnv({
|
||||
projectRef,
|
||||
publishableKey,
|
||||
});
|
||||
})();
|
||||
|
||||
async function loginToSupabase() {
|
||||
await execa("npx", ["supabase", "login"], { stdio: "inherit" });
|
||||
}
|
||||
|
||||
async function createProject({
|
||||
projectName,
|
||||
databasePassword,
|
||||
organizationId,
|
||||
region,
|
||||
}) {
|
||||
const { stdout } = await execa(
|
||||
"npx",
|
||||
[
|
||||
"supabase",
|
||||
"projects",
|
||||
"create",
|
||||
"--output",
|
||||
"json",
|
||||
"--db-password",
|
||||
databasePassword,
|
||||
"--region",
|
||||
region,
|
||||
...(organizationId ? ["--org-id", organizationId] : []),
|
||||
projectName,
|
||||
],
|
||||
{
|
||||
stdin: "pipe",
|
||||
stdout: "pipe",
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
const matchJSON = stdout.match(new RegExp("{.*}", "s"));
|
||||
if (!matchJSON) {
|
||||
throw new Error("Invalid JSON output");
|
||||
}
|
||||
const jsonOuput = JSON.parse(matchJSON[0]);
|
||||
return jsonOuput.id;
|
||||
} catch (e) {
|
||||
console.error("Failed to create project");
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async function selectOrganization() {
|
||||
const { stdout: organizationsJson } = await execa(
|
||||
"npx",
|
||||
["supabase", "orgs", "list", "--output", "json"],
|
||||
{
|
||||
stdout: "pipe",
|
||||
},
|
||||
);
|
||||
|
||||
const organizations = JSON.parse(organizationsJson);
|
||||
|
||||
if (organizations.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (organizations.length === 1) {
|
||||
return organizations[0].id;
|
||||
}
|
||||
|
||||
const selectedOrganizationId = await select({
|
||||
message: "Select an organization to create the project in:",
|
||||
choices: organizations.map((org) => ({
|
||||
name: org.name,
|
||||
value: org.id,
|
||||
})),
|
||||
});
|
||||
|
||||
return selectedOrganizationId;
|
||||
}
|
||||
|
||||
const regions = [
|
||||
{ value: "us-west-1", name: "West US (North California)" },
|
||||
{ value: "us-west-2", name: "West US (Oregon)" },
|
||||
{ value: "us-east-1", name: "East US (North Virginia)" },
|
||||
{ value: "us-east-2", name: "East US (Ohio)" },
|
||||
{ value: "ca-central-1", name: "Canada (Central)" },
|
||||
{ value: "eu-west-1", name: "West EU (Ireland)" },
|
||||
{ value: "eu-west-2", name: "West Europe (London)" },
|
||||
{ value: "eu-west-3", name: "West EU (Paris)" },
|
||||
{ value: "eu-central-1", name: "Central EU (Frankfurt)" },
|
||||
{ value: "eu-central-2", name: "Central Europe (Zurich)" },
|
||||
{ value: "eu-north-1", name: "North EU (Stockholm)" },
|
||||
{ value: "ap-south-1", name: "South Asia (Mumbai)" },
|
||||
{ value: "ap-southeast-1", name: "Southeast Asia (Singapore)" },
|
||||
{ value: "ap-northeast-1", name: "Northeast Asia (Tokyo)" },
|
||||
{ value: "ap-northeast-2", name: "Northeast Asia (Seoul)" },
|
||||
{ value: "ap-southeast-2", name: "Oceania (Sydney)" },
|
||||
{ value: "sa-east-1", name: "South America (São Paulo)" },
|
||||
];
|
||||
|
||||
async function selectRegion() {
|
||||
const selectedRegion = await select({
|
||||
message:
|
||||
"Select a region for the project (close to you for best performance):",
|
||||
choices: regions.map((region) => ({
|
||||
name: region.name,
|
||||
value: region.value,
|
||||
})),
|
||||
});
|
||||
|
||||
return selectedRegion;
|
||||
}
|
||||
|
||||
async function waitForProjectToBeReady({ projectRef }) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Waiting for project to be ready...");
|
||||
const { stdout } = await execa(
|
||||
"npx",
|
||||
["supabase", "projects", "list", "--output", "json"],
|
||||
{
|
||||
stdout: "pipe",
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
// The response is an Array of objects or null if there are no projects
|
||||
const matchJSON =
|
||||
stdout === null ? "[]" : stdout.match(new RegExp("\\[.*\\]", "s"));
|
||||
if (!matchJSON) {
|
||||
throw new Error("Invalid JSON output");
|
||||
}
|
||||
const jsonOuput = JSON.parse(matchJSON[0]);
|
||||
|
||||
const project = jsonOuput.find((project) => project.id === projectRef);
|
||||
if (project?.status !== "ACTIVE_HEALTHY") {
|
||||
await sleep(1000);
|
||||
return waitForProjectToBeReady({ projectRef });
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to create project");
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
let retry = 0;
|
||||
async function linkProject({ projectRef, databasePassword }) {
|
||||
await execa(
|
||||
"npx",
|
||||
[
|
||||
"supabase",
|
||||
"link",
|
||||
"--project-ref",
|
||||
projectRef,
|
||||
"--password",
|
||||
databasePassword,
|
||||
],
|
||||
{
|
||||
stdout: "ignore",
|
||||
stderr: "ignore",
|
||||
},
|
||||
).catch(() => {
|
||||
retry++;
|
||||
if (retry === 1) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Waiting for project to be ready...");
|
||||
}
|
||||
return sleep(1000).then(() =>
|
||||
linkProject({ projectRef, databasePassword }),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async function setupDatabase({ databasePassword }) {
|
||||
await execa(
|
||||
"npx",
|
||||
[
|
||||
"supabase",
|
||||
"db",
|
||||
"push",
|
||||
"--linked",
|
||||
"--include-roles",
|
||||
"--include-seed",
|
||||
"--password",
|
||||
databasePassword,
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function fetchApiKeys({ projectRef }) {
|
||||
let publishableKey = "";
|
||||
try {
|
||||
const { stdout, exitCode } = await execa(
|
||||
"npx",
|
||||
[
|
||||
"supabase",
|
||||
"projects",
|
||||
"api-keys",
|
||||
"--output",
|
||||
"json",
|
||||
"--project-ref",
|
||||
projectRef,
|
||||
],
|
||||
{
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
},
|
||||
);
|
||||
// If the exitCode is not 0, the command failed most probably because the project is not ready
|
||||
if (exitCode === 0) {
|
||||
// The response is an Array of objects
|
||||
const matchJSON = stdout.match(new RegExp("\\[.*\\]", "s"));
|
||||
if (!matchJSON) {
|
||||
throw new Error("Invalid JSON output");
|
||||
}
|
||||
const jsonOutput = JSON.parse(matchJSON[0]);
|
||||
|
||||
// Prioritize the default publishable key, but any publishable key will work.
|
||||
publishableKey = jsonOutput.find(
|
||||
(key) => key.type === "publishable" && key.name === "default",
|
||||
)?.api_key;
|
||||
if (!publishableKey) {
|
||||
publishableKey = jsonOutput.find(
|
||||
(key) => key.type === "publishable",
|
||||
)?.api_key;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to fetch API keys");
|
||||
console.error(e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (publishableKey === "") {
|
||||
await sleep(1000);
|
||||
return fetchApiKeys({ projectRef });
|
||||
}
|
||||
|
||||
return { publishableKey };
|
||||
}
|
||||
|
||||
async function setupSupabaseSecrets({ projectRef, publishableKey }) {
|
||||
await execa(
|
||||
"npx",
|
||||
[
|
||||
"supabase",
|
||||
"secrets",
|
||||
"set",
|
||||
`SB_PUBLISHABLE_KEY=${publishableKey}`,
|
||||
"--project-ref",
|
||||
projectRef,
|
||||
],
|
||||
{
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function persistSupabaseEnv({ projectRef, publishableKey }) {
|
||||
fs.writeFileSync(
|
||||
`${process.cwd()}/.env.production.local`,
|
||||
`
|
||||
VITE_SUPABASE_URL=https://${projectRef}.supabase.co
|
||||
VITE_SB_PUBLISHABLE_KEY=${publishableKey}`,
|
||||
{ flag: "a" },
|
||||
);
|
||||
}
|
||||
|
||||
function generatePassword(length) {
|
||||
const password = crypto
|
||||
.getRandomValues(new BigUint64Array(4))
|
||||
.reduce(
|
||||
(prev, curr, index) =>
|
||||
(!index ? prev : prev.toString(36)) +
|
||||
(index % 2 ? curr.toString(36).toUpperCase() : curr.toString(36)),
|
||||
)
|
||||
.split("")
|
||||
.sort(() => 128 - crypto.getRandomValues(new Uint8Array(1))[0])
|
||||
.join("");
|
||||
|
||||
if (length) {
|
||||
return password.slice(0, length);
|
||||
}
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
Reference in New Issue
Block a user