@alchemy.run/pkg 2.0.0-beta.79 → 2.0.0-beta.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -5
- package/dist/Manifest.d.ts.map +1 -1
- package/dist/Manifest.js.map +1 -1
- package/dist/Protocol.d.ts +3 -3
- package/dist/Protocol.d.ts.map +1 -1
- package/dist/Protocol.js +6 -15
- package/dist/Protocol.js.map +1 -1
- package/dist/Registry/Bindings.d.ts.map +1 -1
- package/dist/Registry/Bindings.js.map +1 -1
- package/dist/Registry/GitHub.d.ts +7 -7
- package/dist/Registry/GitHub.d.ts.map +1 -1
- package/dist/Registry/GitHub.js +20 -45
- package/dist/Registry/GitHub.js.map +1 -1
- package/dist/Registry/Handler.d.ts +5 -5
- package/dist/Registry/Handler.d.ts.map +1 -1
- package/dist/Registry/Handler.js +26 -27
- package/dist/Registry/Handler.js.map +1 -1
- package/dist/Registry/KVCache.d.ts.map +1 -1
- package/dist/Registry/KVCache.js +1 -1
- package/dist/Registry/KVCache.js.map +1 -1
- package/dist/Registry/Sweep.d.ts +1 -1
- package/dist/Registry/Sweep.d.ts.map +1 -1
- package/dist/Registry/Sweep.js +3 -8
- package/dist/Registry/Sweep.js.map +1 -1
- package/dist/Registry/Tags.d.ts +8 -8
- package/dist/Registry/Tags.d.ts.map +1 -1
- package/dist/Registry/Tags.js +1 -1
- package/dist/Registry/Tags.js.map +1 -1
- package/dist/Registry.d.ts +2 -2
- package/dist/Registry.d.ts.map +1 -1
- package/dist/Registry.js +1 -1
- package/dist/Registry.js.map +1 -1
- package/dist/cli/bin.d.ts +8 -4
- package/dist/cli/bin.d.ts.map +1 -1
- package/dist/cli/bin.js +22 -10
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/pack.d.ts +11 -4
- package/dist/cli/pack.d.ts.map +1 -1
- package/dist/cli/pack.js +91 -30
- package/dist/cli/pack.js.map +1 -1
- package/dist/cli/publish.d.ts +1 -1
- package/dist/cli/publish.d.ts.map +1 -1
- package/dist/cli/publish.js +6 -2
- package/dist/cli/publish.js.map +1 -1
- package/package.json +23 -23
- package/src/Manifest.ts +1 -3
- package/src/Protocol.ts +15 -31
- package/src/Registry/Bindings.ts +1 -4
- package/src/Registry/GitHub.ts +34 -86
- package/src/Registry/Handler.ts +46 -90
- package/src/Registry/KVCache.ts +6 -17
- package/src/Registry/Sweep.ts +13 -26
- package/src/Registry/Tags.ts +8 -16
- package/src/Registry.ts +5 -15
- package/src/cli/bin.ts +35 -20
- package/src/cli/pack.ts +142 -82
- package/src/cli/publish.ts +16 -23
package/src/Registry/Tags.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
2
|
import * as Schema from "effect/Schema";
|
|
3
|
-
import * as SqlClient from "effect/
|
|
3
|
+
import * as SqlClient from "effect/sql/SqlClient";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* The `tags` table: one row per `(package, tag)` pointing at a tarball hash.
|
|
@@ -71,9 +71,7 @@ export const resolve = (pkg: string, tag: string) =>
|
|
|
71
71
|
Effect.map(get(pkg, tag), (row) => row?.sha256);
|
|
72
72
|
|
|
73
73
|
/** Rows tied to pull requests whose expiry falls before `before`. */
|
|
74
|
-
export const dueLinked = Effect.fn("Tags.dueLinked")(function* (
|
|
75
|
-
before: number,
|
|
76
|
-
) {
|
|
74
|
+
export const dueLinked = Effect.fn("Tags.dueLinked")(function* (before: number) {
|
|
77
75
|
const sql = yield* SqlClient.SqlClient;
|
|
78
76
|
return yield* decodeTags(
|
|
79
77
|
yield* sql`
|
|
@@ -94,9 +92,7 @@ export const setExpiry = Effect.fn("Tags.setExpiry")(function* (
|
|
|
94
92
|
});
|
|
95
93
|
|
|
96
94
|
/** Delete every expired row and return the tarballs those rows pointed at. */
|
|
97
|
-
export const deleteExpired = Effect.fn("Tags.deleteExpired")(function* (
|
|
98
|
-
now: number,
|
|
99
|
-
) {
|
|
95
|
+
export const deleteExpired = Effect.fn("Tags.deleteExpired")(function* (now: number) {
|
|
100
96
|
const sql = yield* SqlClient.SqlClient;
|
|
101
97
|
return yield* decodeTarballs(
|
|
102
98
|
yield* sql`
|
|
@@ -106,12 +102,8 @@ export const deleteExpired = Effect.fn("Tags.deleteExpired")(function* (
|
|
|
106
102
|
});
|
|
107
103
|
|
|
108
104
|
/** Every tarball some tag still points at, as `<package>/<sha256>`. */
|
|
109
|
-
export const referencedTarballs = Effect.fn("Tags.referencedTarballs")(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
115
|
-
return new Set(rows.map((row) => `${row.package}/${row.sha256}`));
|
|
116
|
-
},
|
|
117
|
-
);
|
|
105
|
+
export const referencedTarballs = Effect.fn("Tags.referencedTarballs")(function* () {
|
|
106
|
+
const sql = yield* SqlClient.SqlClient;
|
|
107
|
+
const rows = yield* decodeTarballs(yield* sql`SELECT DISTINCT package, sha256 FROM tags`);
|
|
108
|
+
return new Set(rows.map((row) => `${row.package}/${row.sha256}`));
|
|
109
|
+
});
|
package/src/Registry.ts
CHANGED
|
@@ -20,14 +20,7 @@ import {
|
|
|
20
20
|
} from "./Registry/Bindings.ts";
|
|
21
21
|
import { handler } from "./Registry/Handler.ts";
|
|
22
22
|
|
|
23
|
-
export {
|
|
24
|
-
Bucket,
|
|
25
|
-
Cache,
|
|
26
|
-
Database,
|
|
27
|
-
Policy,
|
|
28
|
-
RegistryConfig,
|
|
29
|
-
Settings,
|
|
30
|
-
} from "./Registry/Bindings.ts";
|
|
23
|
+
export { Bucket, Cache, Database, Policy, RegistryConfig, Settings } from "./Registry/Bindings.ts";
|
|
31
24
|
|
|
32
25
|
export interface RegistryOptions {
|
|
33
26
|
/**
|
|
@@ -99,9 +92,9 @@ const RegistryConfigFromEnv = Layer.effect(
|
|
|
99
92
|
RegistryConfig,
|
|
100
93
|
Effect.gen(function* () {
|
|
101
94
|
const env = yield* Cloudflare.Workers.WorkerEnvironment;
|
|
102
|
-
const settings = yield* Schema.decodeUnknownEffect(Settings)(
|
|
103
|
-
|
|
104
|
-
)
|
|
95
|
+
const settings = yield* Schema.decodeUnknownEffect(Settings)(env[SETTINGS_ENV]).pipe(
|
|
96
|
+
Effect.orDie,
|
|
97
|
+
);
|
|
105
98
|
return {
|
|
106
99
|
...settings,
|
|
107
100
|
worker: {},
|
|
@@ -165,10 +158,7 @@ export default RegistryLive.pipe(Layer.provide(RegistryConfigFromEnv));
|
|
|
165
158
|
* @product Workers
|
|
166
159
|
* @category Workers & Compute
|
|
167
160
|
*/
|
|
168
|
-
export const PkgRegistry = <const Id extends string>(
|
|
169
|
-
id: Id,
|
|
170
|
-
options: RegistryOptions,
|
|
171
|
-
) =>
|
|
161
|
+
export const PkgRegistry = <const Id extends string>(id: Id, options: RegistryOptions) =>
|
|
172
162
|
Registry.pipe(
|
|
173
163
|
Effect.provide(
|
|
174
164
|
RegistryLive.pipe(
|
package/src/cli/bin.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { PlatformServices, runMain } from "alchemy/Util/PlatformServices";
|
|
3
|
+
import { CliConfig, CliError, Command, Flag, GlobalFlag } from "effect/cli";
|
|
3
4
|
import * as Config from "effect/Config";
|
|
4
5
|
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
5
7
|
import * as Layer from "effect/Layer";
|
|
6
|
-
import
|
|
7
|
-
CliConfig,
|
|
8
|
-
CliError,
|
|
9
|
-
Command,
|
|
10
|
-
Flag,
|
|
11
|
-
GlobalFlag,
|
|
12
|
-
} from "effect/unstable/cli";
|
|
13
|
-
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
14
9
|
import pkg from "../../package.json" with { type: "json" };
|
|
15
10
|
import { Group, pack } from "./pack.ts";
|
|
16
11
|
import { publish } from "./publish.ts";
|
|
@@ -38,11 +33,37 @@ const outFlag = Flag.String("out").pipe(
|
|
|
38
33
|
|
|
39
34
|
export const packCommand = Command.make(
|
|
40
35
|
"pack",
|
|
41
|
-
{
|
|
42
|
-
|
|
36
|
+
{
|
|
37
|
+
group: groupFlag,
|
|
38
|
+
registry: registryFlag,
|
|
39
|
+
out: outFlag,
|
|
40
|
+
since: Flag.String("since").pipe(
|
|
41
|
+
Flag.withDescription("Compare HEAD with this Git ref; defaults to the PR base in Actions"),
|
|
42
|
+
Flag.optional,
|
|
43
|
+
),
|
|
44
|
+
all: Flag.Boolean("all").pipe(
|
|
45
|
+
Flag.withDefault(false),
|
|
46
|
+
Flag.withDescription("Pack every configured package regardless of changes"),
|
|
47
|
+
),
|
|
48
|
+
rebuildAllPaths: Flag.String("rebuild-all-path").pipe(
|
|
49
|
+
Flag.withDescription(
|
|
50
|
+
"Additional exact path or directory/** forcing all packages (repeatable)",
|
|
51
|
+
),
|
|
52
|
+
Flag.atLeast(0),
|
|
53
|
+
),
|
|
54
|
+
},
|
|
55
|
+
({ group, registry, out, since, all, rebuildAllPaths }) =>
|
|
43
56
|
Effect.gen(function* () {
|
|
44
57
|
const cwd = yield* Effect.sync(() => process.cwd());
|
|
45
|
-
yield* pack({
|
|
58
|
+
yield* pack({
|
|
59
|
+
cwd,
|
|
60
|
+
groups: group,
|
|
61
|
+
registry,
|
|
62
|
+
out,
|
|
63
|
+
since: Option.getOrUndefined(since),
|
|
64
|
+
all,
|
|
65
|
+
rebuildAllPaths,
|
|
66
|
+
});
|
|
46
67
|
}),
|
|
47
68
|
).pipe(
|
|
48
69
|
Command.withDescription(
|
|
@@ -73,17 +94,13 @@ export const publishCommand = Command.make(
|
|
|
73
94
|
Command.withDescription(
|
|
74
95
|
"Publish a pkg pack directory from the current GitHub Actions job, after its manifest artifact has been uploaded",
|
|
75
96
|
),
|
|
76
|
-
Command.withExamples([
|
|
77
|
-
{ command: "pkg publish --registry https://pkg.alchemy.run" },
|
|
78
|
-
]),
|
|
97
|
+
Command.withExamples([{ command: "pkg publish --registry https://pkg.alchemy.run" }]),
|
|
79
98
|
);
|
|
80
99
|
|
|
81
100
|
export const root = Command.make("pkg", {}, () =>
|
|
82
101
|
Effect.fail(new CliError.ShowHelp({ commandPath: ["pkg"], errors: [] })),
|
|
83
102
|
).pipe(
|
|
84
|
-
Command.withDescription(
|
|
85
|
-
"Pack and publish preview packages for pull requests.",
|
|
86
|
-
),
|
|
103
|
+
Command.withDescription("Pack and publish preview packages for pull requests."),
|
|
87
104
|
Command.withSubcommands([packCommand, publishCommand]),
|
|
88
105
|
);
|
|
89
106
|
|
|
@@ -92,9 +109,7 @@ Command.run(root, { version: pkg.version }).pipe(
|
|
|
92
109
|
Layer.mergeAll(
|
|
93
110
|
PlatformServices,
|
|
94
111
|
FetchHttpClient.layer,
|
|
95
|
-
CliConfig.layer({
|
|
96
|
-
builtIns: [GlobalFlag.Help, GlobalFlag.Version],
|
|
97
|
-
}),
|
|
112
|
+
CliConfig.layer({ builtIns: [GlobalFlag.Help, GlobalFlag.Version] }),
|
|
98
113
|
),
|
|
99
114
|
),
|
|
100
115
|
runMain,
|
package/src/cli/pack.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { gunzipSync, gzipSync } from "node:zlib";
|
|
2
|
+
import { exec } from "alchemy/Util/exec";
|
|
3
|
+
import { sha256 } from "alchemy/Util/sha256";
|
|
1
4
|
import * as Config from "effect/Config";
|
|
2
5
|
import * as Console from "effect/Console";
|
|
3
6
|
import * as Data from "effect/Data";
|
|
@@ -5,14 +8,11 @@ import * as Effect from "effect/Effect";
|
|
|
5
8
|
import * as FileSystem from "effect/FileSystem";
|
|
6
9
|
import * as Option from "effect/Option";
|
|
7
10
|
import * as Path from "effect/Path";
|
|
11
|
+
import * as ChildProcess from "effect/process/ChildProcess";
|
|
8
12
|
import * as Schema from "effect/Schema";
|
|
9
13
|
import * as SchemaIssue from "effect/SchemaIssue";
|
|
10
14
|
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
11
|
-
import * as ChildProcess from "effect/unstable/process/ChildProcess";
|
|
12
|
-
import { exec } from "alchemy/Util/exec";
|
|
13
|
-
import { sha256 } from "alchemy/Util/sha256";
|
|
14
15
|
import { packTar, unpackTar, type TarHeader } from "modern-tar";
|
|
15
|
-
import { gunzipSync, gzipSync } from "node:zlib";
|
|
16
16
|
import {
|
|
17
17
|
GroupName,
|
|
18
18
|
MANIFEST_FILE,
|
|
@@ -48,8 +48,7 @@ export const Group = Schema.String.pipe(
|
|
|
48
48
|
.split(",")
|
|
49
49
|
.map((attribute) => attribute.trim())
|
|
50
50
|
.filter((attribute) => attribute.length > 0);
|
|
51
|
-
return match &&
|
|
52
|
-
attributes.every((attribute) => attribute === "Collapsed")
|
|
51
|
+
return match && attributes.every((attribute) => attribute === "Collapsed")
|
|
53
52
|
? Effect.succeed({
|
|
54
53
|
name: match[1]!,
|
|
55
54
|
pattern: match[3]!,
|
|
@@ -64,9 +63,7 @@ export const Group = Schema.String.pipe(
|
|
|
64
63
|
);
|
|
65
64
|
},
|
|
66
65
|
encode: (group) =>
|
|
67
|
-
Effect.succeed(
|
|
68
|
-
`${group.name}${group.collapsed ? "[Collapsed]" : ""}=${group.pattern}`,
|
|
69
|
-
),
|
|
66
|
+
Effect.succeed(`${group.name}${group.collapsed ? "[Collapsed]" : ""}=${group.pattern}`),
|
|
70
67
|
}),
|
|
71
68
|
),
|
|
72
69
|
);
|
|
@@ -86,9 +83,7 @@ export const DEPENDENCY_SECTIONS = [
|
|
|
86
83
|
"optionalDependencies",
|
|
87
84
|
] as const;
|
|
88
85
|
|
|
89
|
-
const DependencyMap = Schema.optionalKey(
|
|
90
|
-
Schema.Record(Schema.String, Schema.String),
|
|
91
|
-
);
|
|
86
|
+
const DependencyMap = Schema.optionalKey(Schema.Record(Schema.String, Schema.String));
|
|
92
87
|
|
|
93
88
|
export const DependencySections = Schema.Struct({
|
|
94
89
|
dependencies: DependencyMap,
|
|
@@ -140,6 +135,70 @@ export interface WorkspacePackage {
|
|
|
140
135
|
readonly group: string;
|
|
141
136
|
}
|
|
142
137
|
|
|
138
|
+
/** Shared inputs that invalidate every configured package. */
|
|
139
|
+
export const REBUILD_ALL_PATHS = [
|
|
140
|
+
"package.json",
|
|
141
|
+
"pnpm-lock.yaml",
|
|
142
|
+
"pnpm-workspace.yaml",
|
|
143
|
+
"package-lock.json",
|
|
144
|
+
"yarn.lock",
|
|
145
|
+
"bun.lock",
|
|
146
|
+
"bun.lockb",
|
|
147
|
+
"tsconfig.json",
|
|
148
|
+
"tsconfig.base.json",
|
|
149
|
+
"turbo.json",
|
|
150
|
+
".github/workflows/pkg.yml",
|
|
151
|
+
".github/workflows/pr-package.yml",
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
/** Select dependents first, then dependencies, without pulling in unrelated siblings. */
|
|
155
|
+
export const selectPackages = (
|
|
156
|
+
packages: ReadonlyArray<WorkspacePackage>,
|
|
157
|
+
dependencies: ReadonlyMap<string, ReadonlySet<string>>,
|
|
158
|
+
changedFiles: ReadonlyArray<string>,
|
|
159
|
+
rebuildAllPaths: ReadonlyArray<string> = [],
|
|
160
|
+
) => {
|
|
161
|
+
if (
|
|
162
|
+
changedFiles.some((file) =>
|
|
163
|
+
[...REBUILD_ALL_PATHS, ...rebuildAllPaths].some((pattern) =>
|
|
164
|
+
pattern.endsWith("/**")
|
|
165
|
+
? file === pattern.slice(0, -3) || file.startsWith(pattern.slice(0, -2))
|
|
166
|
+
: file === pattern,
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
)
|
|
170
|
+
return [...packages];
|
|
171
|
+
|
|
172
|
+
const selected = new Set(
|
|
173
|
+
packages
|
|
174
|
+
.filter((pkg) =>
|
|
175
|
+
changedFiles.some(
|
|
176
|
+
(file) =>
|
|
177
|
+
file === pkg.dir || file.startsWith(`${pkg.dir}/`) || pkg.dir.startsWith(`${file}/`),
|
|
178
|
+
),
|
|
179
|
+
)
|
|
180
|
+
.map((pkg) => pkg.name),
|
|
181
|
+
);
|
|
182
|
+
// A changed gitlink selects the packages nested inside that submodule.
|
|
183
|
+
let size: number;
|
|
184
|
+
do {
|
|
185
|
+
size = selected.size;
|
|
186
|
+
for (const [name, deps] of dependencies) {
|
|
187
|
+
if ([...deps].some((dep) => selected.has(dep))) selected.add(name);
|
|
188
|
+
}
|
|
189
|
+
} while (selected.size !== size);
|
|
190
|
+
const pending = [...selected];
|
|
191
|
+
for (const name of pending) {
|
|
192
|
+
for (const dep of dependencies.get(name) ?? []) {
|
|
193
|
+
if (!selected.has(dep)) {
|
|
194
|
+
selected.add(dep);
|
|
195
|
+
pending.push(dep);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return packages.filter((pkg) => selected.has(pkg.name));
|
|
200
|
+
};
|
|
201
|
+
|
|
143
202
|
/**
|
|
144
203
|
* Expand one level of `{a,b,c}` alternatives into plain patterns, so
|
|
145
204
|
* `./packages/{alchemy,pkg}` lists exactly those two directories.
|
|
@@ -151,9 +210,7 @@ export const expandBraces = (pattern: string): string[] => {
|
|
|
151
210
|
.split(",")
|
|
152
211
|
.map((alternative) => alternative.trim())
|
|
153
212
|
.filter((alternative) => alternative.length > 0)
|
|
154
|
-
.flatMap((alternative) =>
|
|
155
|
-
expandBraces(`${match[1]}${alternative}${match[3]}`),
|
|
156
|
-
);
|
|
213
|
+
.flatMap((alternative) => expandBraces(`${match[1]}${alternative}${match[3]}`));
|
|
157
214
|
};
|
|
158
215
|
|
|
159
216
|
/**
|
|
@@ -169,10 +226,7 @@ const expand = Effect.fn("expandGlob")(function* (cwd: string, glob: string) {
|
|
|
169
226
|
return [...new Set(results)];
|
|
170
227
|
});
|
|
171
228
|
|
|
172
|
-
const expandPattern = Effect.fn("expandPattern")(function* (
|
|
173
|
-
cwd: string,
|
|
174
|
-
pattern: string,
|
|
175
|
-
) {
|
|
229
|
+
const expandPattern = Effect.fn("expandPattern")(function* (cwd: string, pattern: string) {
|
|
176
230
|
const fs = yield* FileSystem.FileSystem;
|
|
177
231
|
const path = yield* Path.Path;
|
|
178
232
|
if (pattern.includes("**")) {
|
|
@@ -257,10 +311,7 @@ export class GitError extends Data.TaggedError("GitError")<{
|
|
|
257
311
|
}
|
|
258
312
|
|
|
259
313
|
/** Run `git` in `cwd` and return trimmed stdout. */
|
|
260
|
-
export const git = Effect.fn("git")(function* (
|
|
261
|
-
cwd: string,
|
|
262
|
-
args: ReadonlyArray<string>,
|
|
263
|
-
) {
|
|
314
|
+
export const git = Effect.fn("git")(function* (cwd: string, args: ReadonlyArray<string>) {
|
|
264
315
|
const { exitCode, stdout, stderr } = yield* exec(
|
|
265
316
|
ChildProcess.make("git", [...args], { cwd, shell: false }),
|
|
266
317
|
).pipe(Effect.scoped);
|
|
@@ -271,8 +322,7 @@ export const git = Effect.fn("git")(function* (
|
|
|
271
322
|
});
|
|
272
323
|
|
|
273
324
|
/** Absolute path of the repository (or submodule) that owns `cwd`. */
|
|
274
|
-
export const toplevel = (cwd: string) =>
|
|
275
|
-
git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
325
|
+
export const toplevel = (cwd: string) => git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
276
326
|
|
|
277
327
|
/** Full HEAD SHA of the repository that owns `cwd`. */
|
|
278
328
|
const gitHead = (cwd: string) => git(cwd, ["rev-parse", "HEAD"]);
|
|
@@ -282,24 +332,17 @@ export class PackError extends Data.TaggedError("PackError")<{
|
|
|
282
332
|
readonly message: string;
|
|
283
333
|
}> {}
|
|
284
334
|
|
|
285
|
-
const PnpmPackOutput = Schema.fromJsonString(
|
|
286
|
-
Schema.Struct({ filename: Schema.String }),
|
|
287
|
-
);
|
|
335
|
+
const PnpmPackOutput = Schema.fromJsonString(Schema.Struct({ filename: Schema.String }));
|
|
288
336
|
|
|
289
337
|
/**
|
|
290
338
|
* Rewrite every dependency on a package in `links` to that package's
|
|
291
339
|
* tarball URL. Returns the rewritten manifest text and the rewrites made.
|
|
292
340
|
*/
|
|
293
|
-
export const rewriteDependencies = (
|
|
294
|
-
manifestText: string,
|
|
295
|
-
links: ReadonlyMap<string, string>,
|
|
296
|
-
) =>
|
|
341
|
+
export const rewriteDependencies = (manifestText: string, links: ReadonlyMap<string, string>) =>
|
|
297
342
|
Effect.gen(function* () {
|
|
298
343
|
const manifest = yield* Schema.decodeUnknownEffect(
|
|
299
344
|
Schema.fromJsonString(
|
|
300
|
-
Schema.StructWithRest(DependencySections, [
|
|
301
|
-
Schema.Record(Schema.String, Schema.Unknown),
|
|
302
|
-
]),
|
|
345
|
+
Schema.StructWithRest(DependencySections, [Schema.Record(Schema.String, Schema.Unknown)]),
|
|
303
346
|
),
|
|
304
347
|
)(manifestText);
|
|
305
348
|
// Preserve source key order after validation: it contributes to the tarball hash.
|
|
@@ -387,16 +430,12 @@ export const packPackage = Effect.fn("packPackage")(function* (options: {
|
|
|
387
430
|
|
|
388
431
|
let rewrites: PackedTarball["rewrites"] = [];
|
|
389
432
|
const normalized: Array<{ header: TarHeader; data: Uint8Array }> = [];
|
|
390
|
-
for (const entry of entries.sort((a, b) =>
|
|
391
|
-
a.header.name.localeCompare(b.header.name),
|
|
392
|
-
)) {
|
|
433
|
+
for (const entry of entries.sort((a, b) => a.header.name.localeCompare(b.header.name))) {
|
|
393
434
|
let data = entry.data ?? new Uint8Array();
|
|
394
435
|
if (entry.header.name === "package/package.json") {
|
|
395
436
|
const text = yield* Effect.sync(() => new TextDecoder().decode(data));
|
|
396
437
|
const result = yield* rewriteDependencies(text, options.links).pipe(
|
|
397
|
-
Effect.mapError(
|
|
398
|
-
(e) => new PackError({ dir: options.absDir, message: String(e) }),
|
|
399
|
-
),
|
|
438
|
+
Effect.mapError((e) => new PackError({ dir: options.absDir, message: String(e) })),
|
|
400
439
|
);
|
|
401
440
|
rewrites = result.rewrites;
|
|
402
441
|
data = yield* Effect.sync(() => new TextEncoder().encode(result.text));
|
|
@@ -418,31 +457,31 @@ export const packPackage = Effect.fn("packPackage")(function* (options: {
|
|
|
418
457
|
const PullRequestEvent = Schema.fromJsonString(
|
|
419
458
|
Schema.Struct({
|
|
420
459
|
pull_request: Schema.optionalKey(
|
|
421
|
-
Schema.Struct({
|
|
460
|
+
Schema.Struct({
|
|
461
|
+
head: Schema.Struct({ sha: Schema.String }),
|
|
462
|
+
base: Schema.Struct({ sha: Schema.String }),
|
|
463
|
+
}),
|
|
422
464
|
),
|
|
423
465
|
}),
|
|
424
466
|
);
|
|
425
467
|
|
|
426
468
|
/**
|
|
427
|
-
*
|
|
428
|
-
* `
|
|
469
|
+
* Pull request metadata from the GitHub Actions event payload.
|
|
470
|
+
* `undefined` outside pull request events.
|
|
429
471
|
* On that event the default checkout is a synthetic merge commit, which the
|
|
430
472
|
* registry would reject because it does not match the run's head.
|
|
431
473
|
*/
|
|
432
|
-
const
|
|
474
|
+
const pullRequest = Effect.gen(function* () {
|
|
433
475
|
const event = yield* Config.option(Config.String("GITHUB_EVENT_NAME"));
|
|
434
476
|
const eventPath = yield* Config.option(Config.String("GITHUB_EVENT_PATH"));
|
|
435
|
-
if (
|
|
436
|
-
Option.getOrUndefined(event) !== "pull_request" ||
|
|
437
|
-
Option.isNone(eventPath)
|
|
438
|
-
) {
|
|
477
|
+
if (Option.getOrUndefined(event) !== "pull_request" || Option.isNone(eventPath)) {
|
|
439
478
|
return undefined;
|
|
440
479
|
}
|
|
441
480
|
const fs = yield* FileSystem.FileSystem;
|
|
442
481
|
const payload = yield* fs
|
|
443
482
|
.readFileString(eventPath.value)
|
|
444
483
|
.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PullRequestEvent)));
|
|
445
|
-
return payload.pull_request
|
|
484
|
+
return payload.pull_request;
|
|
446
485
|
});
|
|
447
486
|
|
|
448
487
|
export interface PackOptions {
|
|
@@ -450,6 +489,9 @@ export interface PackOptions {
|
|
|
450
489
|
readonly groups: ReadonlyArray<Group>;
|
|
451
490
|
readonly registry: string;
|
|
452
491
|
readonly out: string;
|
|
492
|
+
readonly since?: string;
|
|
493
|
+
readonly all?: boolean;
|
|
494
|
+
readonly rebuildAllPaths?: ReadonlyArray<string>;
|
|
453
495
|
}
|
|
454
496
|
|
|
455
497
|
/**
|
|
@@ -457,8 +499,7 @@ export interface PackOptions {
|
|
|
457
499
|
* replaced by `+`, a character package names cannot contain, so `@a/b-c`
|
|
458
500
|
* and `@a-b/c` get distinct files.
|
|
459
501
|
*/
|
|
460
|
-
export const tarballFile = (name: string) =>
|
|
461
|
-
`${name.replace(/^@/, "").replace("/", "+")}.tgz`;
|
|
502
|
+
export const tarballFile = (name: string) => `${name.replace(/^@/, "").replace("/", "+")}.tgz`;
|
|
462
503
|
|
|
463
504
|
/**
|
|
464
505
|
* Pack every discovered package into `out` with a manifest describing each
|
|
@@ -470,7 +511,8 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
470
511
|
|
|
471
512
|
const root = yield* toplevel(options.cwd);
|
|
472
513
|
const head = yield* gitHead(root);
|
|
473
|
-
const
|
|
514
|
+
const pr = yield* pullRequest;
|
|
515
|
+
const prHead = pr?.head.sha;
|
|
474
516
|
if (prHead !== undefined && prHead !== head) {
|
|
475
517
|
return yield* new WorkspaceError({
|
|
476
518
|
message:
|
|
@@ -478,11 +520,7 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
478
520
|
"Check out github.event.pull_request.head.sha before packing so tarballs are addressed by a commit that exists on the pull request.",
|
|
479
521
|
});
|
|
480
522
|
}
|
|
481
|
-
|
|
482
|
-
if (packages.length === 0) {
|
|
483
|
-
yield* Console.log("No publishable packages matched.");
|
|
484
|
-
return undefined;
|
|
485
|
-
}
|
|
523
|
+
let packages = yield* discover(options.cwd, options.groups);
|
|
486
524
|
|
|
487
525
|
// Dependencies between packed packages are rewritten to the dependency's
|
|
488
526
|
// immutable tarball URL, so a package is packed only after everything it
|
|
@@ -492,31 +530,55 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
492
530
|
for (const pkg of packages) {
|
|
493
531
|
const manifest = yield* fs
|
|
494
532
|
.readFileString(path.join(pkg.absDir, "package.json"))
|
|
495
|
-
.pipe(
|
|
496
|
-
Effect.flatMap(
|
|
497
|
-
Schema.decodeUnknownEffect(Schema.fromJsonString(DependencySections)),
|
|
498
|
-
),
|
|
499
|
-
);
|
|
533
|
+
.pipe(Effect.flatMap(Schema.decodeUnknownEffect(Schema.fromJsonString(DependencySections))));
|
|
500
534
|
dependencies.set(
|
|
501
535
|
pkg.name,
|
|
502
536
|
new Set(
|
|
503
|
-
DEPENDENCY_SECTIONS.flatMap((section) =>
|
|
504
|
-
|
|
505
|
-
)
|
|
537
|
+
DEPENDENCY_SECTIONS.flatMap((section) => Object.keys(manifest[section] ?? {})).filter(
|
|
538
|
+
(name) => name !== pkg.name && byName.has(name),
|
|
539
|
+
),
|
|
506
540
|
),
|
|
507
541
|
);
|
|
508
542
|
}
|
|
543
|
+
const since = options.all ? undefined : (options.since ?? pr?.base.sha);
|
|
544
|
+
if (since !== undefined) {
|
|
545
|
+
// Disable rename detection so moves affect both the old and new packages.
|
|
546
|
+
const changed = (yield* git(root, [
|
|
547
|
+
"diff",
|
|
548
|
+
"--name-only",
|
|
549
|
+
"--no-renames",
|
|
550
|
+
"-z",
|
|
551
|
+
since,
|
|
552
|
+
head,
|
|
553
|
+
"--",
|
|
554
|
+
]))
|
|
555
|
+
.split("\0")
|
|
556
|
+
.filter(Boolean);
|
|
557
|
+
const selected = selectPackages(
|
|
558
|
+
packages.map((pkg) => ({
|
|
559
|
+
...pkg,
|
|
560
|
+
dir: path.relative(root, pkg.absDir).split(path.sep).join("/"),
|
|
561
|
+
})),
|
|
562
|
+
dependencies,
|
|
563
|
+
changed,
|
|
564
|
+
options.rebuildAllPaths,
|
|
565
|
+
);
|
|
566
|
+
const names = new Set(selected.map((pkg) => pkg.name));
|
|
567
|
+
packages = packages.filter((pkg) => names.has(pkg.name));
|
|
568
|
+
for (const name of dependencies.keys()) {
|
|
569
|
+
if (!names.has(name)) dependencies.delete(name);
|
|
570
|
+
}
|
|
571
|
+
yield* Console.log(
|
|
572
|
+
`Selected ${packages.length} package(s) changed since ${since}, including dependents and dependencies.`,
|
|
573
|
+
);
|
|
574
|
+
}
|
|
509
575
|
const levels = yield* dependencyLevels(dependencies);
|
|
510
576
|
|
|
511
577
|
// The output directory is replaced wholesale, so it has to be a proper
|
|
512
578
|
// subdirectory of the workspace: `--out .` would otherwise delete it.
|
|
513
579
|
const outDir = path.resolve(options.cwd, options.out);
|
|
514
580
|
const relative = path.relative(options.cwd, outDir);
|
|
515
|
-
if (
|
|
516
|
-
relative === "" ||
|
|
517
|
-
relative.startsWith("..") ||
|
|
518
|
-
path.isAbsolute(relative)
|
|
519
|
-
) {
|
|
581
|
+
if (relative === "" || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
520
582
|
return yield* new WorkspaceError({
|
|
521
583
|
message: `--out must be a subdirectory of ${options.cwd}, got ${outDir}`,
|
|
522
584
|
});
|
|
@@ -555,10 +617,7 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
555
617
|
{ concurrency: 4 },
|
|
556
618
|
);
|
|
557
619
|
for (const entry of packedLevel) {
|
|
558
|
-
links.set(
|
|
559
|
-
entry.name,
|
|
560
|
-
tarballUrl(options.registry, entry.name, entry.sha256),
|
|
561
|
-
);
|
|
620
|
+
links.set(entry.name, tarballUrl(options.registry, entry.name, entry.sha256));
|
|
562
621
|
packedByName.set(entry.name, entry);
|
|
563
622
|
}
|
|
564
623
|
}
|
|
@@ -568,10 +627,7 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
568
627
|
// One entry per distinct group name, in the order the groups were given.
|
|
569
628
|
const groups = [
|
|
570
629
|
...new Map(
|
|
571
|
-
options.groups.map((group) => [
|
|
572
|
-
group.name,
|
|
573
|
-
{ name: group.name, collapsed: group.collapsed },
|
|
574
|
-
]),
|
|
630
|
+
options.groups.map((group) => [group.name, { name: group.name, collapsed: group.collapsed }]),
|
|
575
631
|
).values(),
|
|
576
632
|
];
|
|
577
633
|
const manifest: Manifest = {
|
|
@@ -594,9 +650,13 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
594
650
|
const artifact = manifestArtifactName(yield* sha256(manifestText));
|
|
595
651
|
const stepOutput = yield* Config.option(Config.String("GITHUB_OUTPUT"));
|
|
596
652
|
if (Option.isSome(stepOutput)) {
|
|
597
|
-
yield* fs.writeFileString(
|
|
598
|
-
|
|
599
|
-
|
|
653
|
+
yield* fs.writeFileString(
|
|
654
|
+
stepOutput.value,
|
|
655
|
+
`artifact-name=${artifact}\npackage-count=${entries.length}\n`,
|
|
656
|
+
{
|
|
657
|
+
flag: "a",
|
|
658
|
+
},
|
|
659
|
+
);
|
|
600
660
|
}
|
|
601
661
|
yield* Console.log(`Manifest artifact name: ${artifact}`);
|
|
602
662
|
return manifest;
|
package/src/cli/publish.ts
CHANGED
|
@@ -3,9 +3,9 @@ import * as Console from "effect/Console";
|
|
|
3
3
|
import * as Data from "effect/Data";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
5
|
import * as FileSystem from "effect/FileSystem";
|
|
6
|
+
import * as HttpApiClient from "effect/http-api/HttpApiClient";
|
|
6
7
|
import * as Path from "effect/Path";
|
|
7
8
|
import * as Schema from "effect/Schema";
|
|
8
|
-
import * as HttpApiClient from "effect/unstable/httpapi/HttpApiClient";
|
|
9
9
|
import { MANIFEST_FILE, ManifestJson, type Manifest } from "../Manifest.ts";
|
|
10
10
|
import { PkgApi, type TarballRef } from "../Protocol.ts";
|
|
11
11
|
|
|
@@ -25,9 +25,7 @@ export interface PublishOptions {
|
|
|
25
25
|
const currentRun = Effect.gen(function* () {
|
|
26
26
|
const repo = yield* Config.String("GITHUB_REPOSITORY");
|
|
27
27
|
const runId = yield* Config.Int("GITHUB_RUN_ID");
|
|
28
|
-
const attempt = yield* Config.Int("GITHUB_RUN_ATTEMPT").pipe(
|
|
29
|
-
Config.withDefault(1),
|
|
30
|
-
);
|
|
28
|
+
const attempt = yield* Config.Int("GITHUB_RUN_ATTEMPT").pipe(Config.withDefault(1));
|
|
31
29
|
return { repo, runId, attempt };
|
|
32
30
|
}).pipe(
|
|
33
31
|
Effect.mapError(
|
|
@@ -39,11 +37,10 @@ const currentRun = Effect.gen(function* () {
|
|
|
39
37
|
);
|
|
40
38
|
|
|
41
39
|
/** Render a registry or transport failure as a `PublishError`. */
|
|
42
|
-
const failed =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
40
|
+
const failed = (what: string) => (e: { readonly _tag: string; readonly message?: string }) =>
|
|
41
|
+
new PublishError({
|
|
42
|
+
message: `${what}: ${e.message ? `${e._tag}: ${e.message}` : e._tag}`,
|
|
43
|
+
});
|
|
47
44
|
|
|
48
45
|
/**
|
|
49
46
|
* Publish a `pkg pack` directory from the current GitHub Actions job. The
|
|
@@ -52,9 +49,7 @@ const failed =
|
|
|
52
49
|
* through GitHub, then either reports the tarballs it lacks, which are
|
|
53
50
|
* uploaded before trying again, or writes the tags.
|
|
54
51
|
*/
|
|
55
|
-
export const publish = Effect.fn("publish")(function* (
|
|
56
|
-
options: PublishOptions,
|
|
57
|
-
) {
|
|
52
|
+
export const publish = Effect.fn("publish")(function* (options: PublishOptions) {
|
|
58
53
|
const fs = yield* FileSystem.FileSystem;
|
|
59
54
|
const path = yield* Path.Path;
|
|
60
55
|
const registry = options.registry.replace(/\/+$/, "");
|
|
@@ -63,14 +58,18 @@ export const publish = Effect.fn("publish")(function* (
|
|
|
63
58
|
|
|
64
59
|
const dir = path.resolve(options.cwd, options.dir);
|
|
65
60
|
const manifestText = yield* fs.readFileString(path.join(dir, MANIFEST_FILE));
|
|
66
|
-
const manifest =
|
|
67
|
-
yield* Schema.decodeUnknownEffect(ManifestJson)(manifestText);
|
|
61
|
+
const manifest = yield* Schema.decodeUnknownEffect(ManifestJson)(manifestText);
|
|
68
62
|
if (manifest.registry !== registry) {
|
|
69
63
|
return yield* new PublishError({
|
|
70
64
|
message: `artifact was packed for ${manifest.registry}, not ${registry}`,
|
|
71
65
|
});
|
|
72
66
|
}
|
|
73
67
|
|
|
68
|
+
if (manifest.packages.length === 0) {
|
|
69
|
+
yield* Console.log("No affected packages to publish.");
|
|
70
|
+
return { packages: [] };
|
|
71
|
+
}
|
|
72
|
+
|
|
74
73
|
const upload = (pkg: Manifest["packages"][number]) =>
|
|
75
74
|
Effect.gen(function* () {
|
|
76
75
|
const bytes = yield* fs.readFile(path.join(dir, pkg.file));
|
|
@@ -91,9 +90,7 @@ export const publish = Effect.fn("publish")(function* (
|
|
|
91
90
|
).pipe(
|
|
92
91
|
Effect.andThen(
|
|
93
92
|
Effect.forEach(
|
|
94
|
-
manifest.packages.filter((pkg) =>
|
|
95
|
-
wanted.has(`${pkg.name}@${pkg.sha256}`),
|
|
96
|
-
),
|
|
93
|
+
manifest.packages.filter((pkg) => wanted.has(`${pkg.name}@${pkg.sha256}`)),
|
|
97
94
|
upload,
|
|
98
95
|
{ concurrency: 4, discard: true },
|
|
99
96
|
),
|
|
@@ -107,9 +104,7 @@ export const publish = Effect.fn("publish")(function* (
|
|
|
107
104
|
const published = yield* attempt.pipe(
|
|
108
105
|
// The first answer is usually the tarballs to upload; publish once more
|
|
109
106
|
// after uploading them.
|
|
110
|
-
Effect.catchTag("MissingTarballs", (e) =>
|
|
111
|
-
Effect.andThen(uploadMissing(e.missing), attempt),
|
|
112
|
-
),
|
|
107
|
+
Effect.catchTag("MissingTarballs", (e) => Effect.andThen(uploadMissing(e.missing), attempt)),
|
|
113
108
|
Effect.catchTag(
|
|
114
109
|
"MissingTarballs",
|
|
115
110
|
(e) =>
|
|
@@ -117,9 +112,7 @@ export const publish = Effect.fn("publish")(function* (
|
|
|
117
112
|
message: `registry still reports missing tarballs after upload: ${e.missing.map((ref) => ref.name).join(", ")}`,
|
|
118
113
|
}),
|
|
119
114
|
),
|
|
120
|
-
Effect.mapError((e) =>
|
|
121
|
-
e._tag === "PublishError" ? e : failed("publish")(e),
|
|
122
|
-
),
|
|
115
|
+
Effect.mapError((e) => (e._tag === "PublishError" ? e : failed("publish")(e))),
|
|
123
116
|
);
|
|
124
117
|
|
|
125
118
|
for (const pkg of published.packages) {
|