Content-addressed version deploy system for Diffuse
elements.diffuse.sh
1// ipfs add artifacts/ --recursive --cid-version=1 --hidden --max-directory-links=1000 --chunker=size-1048576
2
3import * as Fs from "@std/fs";
4import * as Path from "@std/path";
5import { cpSync } from "node:fs";
6import { load } from "@std/dotenv";
7
8import { MemoryBlockstore } from "blockstore-core/memory";
9import { importer } from "ipfs-unixfs-importer";
10import { CID } from "multiformats/cid";
11import * as DagPB from "@ipld/dag-pb";
12
13import artifactsJson from "../../artifacts/artifacts.json" with {
14 type: "json",
15};
16
17import type { Artifact } from "../../common/types.d.ts";
18import { execSync, writeCar } from "../../common/index.ts";
19
20const artifacts: Record<string, Artifact> = artifactsJson as any;
21
22////////////////////////////////////////////
23// ADD CARS
24////////////////////////////////////////////
25
26Fs.ensureDirSync("cars");
27
28Object.values(artifacts).forEach((artifact) => {
29 const carPath = Path.join("cars", artifact.cid + ".car");
30 const exists = Fs.existsSync(carPath);
31
32 if (!exists) {
33 const inputPath = Path.join("artifacts", artifact.cid);
34 const tmpDir = Deno.makeTempDirSync();
35
36 cpSync(inputPath, tmpDir, {
37 dereference: true,
38 preserveTimestamps: true,
39 recursive: true,
40 });
41
42 writeCar(tmpDir, artifact.cid);
43
44 Deno.removeSync(tmpDir, { recursive: true });
45 }
46
47 execSync(
48 "ipfs",
49 "dag",
50 "import",
51 carPath,
52 );
53});
54
55////////////////////////////////////////////
56// GENERATE ROOT DAG
57////////////////////////////////////////////
58
59const blockstore = new MemoryBlockstore();
60
61const source = [
62 "artifacts/_redirects",
63 "artifacts/artifacts.json",
64 "artifacts/versions.json",
65].map(
66 (path) => {
67 return {
68 path: path,
69 content: Deno.readFileSync(path),
70 };
71 },
72);
73
74const it = Fs.walkSync("dist");
75
76for (const entry of it) {
77 if (!entry.isFile) continue;
78
79 source.push({
80 path: entry.path.split("/").slice(1).join("/"),
81 content: Deno.readFileSync(entry.path),
82 });
83}
84
85const links = [];
86
87for await (const entry of importer(source, blockstore)) {
88 links.push(
89 DagPB.createLink(entry.path ?? "", Number(entry.size), entry.cid),
90 );
91}
92
93Object.values(artifacts).forEach((artifact) => {
94 links.push(
95 DagPB.createLink(artifact.cid, 0, CID.parse(artifact.cid)),
96 );
97});
98
99const bytes = DagPB.encode({
100 Data: new Uint8Array([8, 1]),
101 Links: links.toSorted((a, b) => {
102 const aName = a.Name ?? "";
103 const bName = b.Name ?? "";
104
105 if (aName < bName) return -1;
106 if (aName > bName) return 1;
107 return 0;
108 }),
109});
110
111////////////////////////////////////////////
112// ADD ROOT DAG TO IPFS
113////////////////////////////////////////////
114
115const cmd = new Deno.Command("ipfs", {
116 args: ["dag", "put", "--store-codec", "dag-pb", "--input-codec", "dag-pb"],
117 stdin: "piped",
118 stdout: "piped",
119});
120
121const process = cmd.spawn();
122const writer = process.stdin.getWriter();
123
124await writer.write(bytes);
125await writer.close();
126
127const cid = (await process.stdout.text()).trim();
128
129console.log("✅️ Added artifacts DAG to IPFS with CID: " + cid);
130
131////////////////////////////////////////////
132// SET DNSLINK
133////////////////////////////////////////////
134
135const env = await load();
136
137const deleteCmd = new Deno.Command("lexicon", {
138 args: [
139 "dnsimple",
140 "delete",
141 "diffuse.sh",
142 "TXT",
143 "--name",
144 "_dnslink.elements.diffuse.sh",
145 ],
146 env,
147});
148
149await deleteCmd.spawn().output();
150
151const createCmd = new Deno.Command("lexicon", {
152 args: [
153 "dnsimple",
154 "create",
155 "diffuse.sh",
156 "TXT",
157 "--name",
158 "_dnslink.elements.diffuse.sh",
159 "--content",
160 `"dnslink=/ipfs/${cid}"`,
161 ],
162 env,
163});
164
165await createCmd.spawn().output();
166
167console.log("✅️ DNSLink set");