@alchemy.run/pkg 2.0.0-beta.79 → 2.0.0-beta.80
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/Protocol.d.ts +3 -3
- package/dist/Protocol.d.ts.map +1 -1
- package/dist/Protocol.js +4 -4
- package/dist/Protocol.js.map +1 -1
- package/dist/Registry/GitHub.d.ts +6 -6
- package/dist/Registry/GitHub.d.ts.map +1 -1
- package/dist/Registry/GitHub.js +12 -10
- 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 +24 -17
- package/dist/Registry/Handler.js.map +1 -1
- package/dist/Registry/Sweep.d.ts +1 -1
- package/dist/Registry/Sweep.d.ts.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 +1 -1
- package/dist/cli/bin.d.ts +8 -4
- package/dist/cli/bin.d.ts.map +1 -1
- package/dist/cli/bin.js +20 -4
- 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 +86 -13
- 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 +5 -1
- package/dist/cli/publish.js.map +1 -1
- package/package.json +8 -8
- package/src/Protocol.ts +4 -4
- package/src/Registry/GitHub.ts +18 -11
- package/src/Registry/Handler.ts +29 -17
- package/src/Registry/Tags.ts +1 -1
- package/src/cli/bin.ts +36 -11
- package/src/cli/pack.ts +121 -15
- package/src/cli/publish.ts +6 -1
package/src/Registry/GitHub.ts
CHANGED
|
@@ -4,7 +4,6 @@ import * as Apps from "@distilled.cloud/github/apps";
|
|
|
4
4
|
import * as Checks from "@distilled.cloud/github/checks";
|
|
5
5
|
import * as Issues from "@distilled.cloud/github/issues";
|
|
6
6
|
import * as Pulls from "@distilled.cloud/github/pulls";
|
|
7
|
-
import * as Repos from "@distilled.cloud/github/repos";
|
|
8
7
|
import * as Arr from "effect/Array";
|
|
9
8
|
import * as Clock from "effect/Clock";
|
|
10
9
|
import * as Context from "effect/Context";
|
|
@@ -12,13 +11,13 @@ import * as Data from "effect/Data";
|
|
|
12
11
|
import * as DateTime from "effect/DateTime";
|
|
13
12
|
import * as Duration from "effect/Duration";
|
|
14
13
|
import * as Effect from "effect/Effect";
|
|
15
|
-
import
|
|
14
|
+
import { Base64Url } from "effect/encoding";
|
|
16
15
|
import * as Layer from "effect/Layer";
|
|
17
16
|
import * as Option from "effect/Option";
|
|
18
17
|
import * as Redacted from "effect/Redacted";
|
|
19
18
|
import * as Schema from "effect/Schema";
|
|
20
19
|
import * as Stream from "effect/Stream";
|
|
21
|
-
import * as HttpClient from "effect/
|
|
20
|
+
import * as HttpClient from "effect/http/HttpClient";
|
|
22
21
|
import * as crypto from "node:crypto";
|
|
23
22
|
import { COMMENT_MARKER, RegistryConfig } from "./Bindings.ts";
|
|
24
23
|
import * as KVCache from "./KVCache.ts";
|
|
@@ -50,13 +49,13 @@ export const signJwt = (
|
|
|
50
49
|
) =>
|
|
51
50
|
Effect.try({
|
|
52
51
|
try: () => {
|
|
53
|
-
const header =
|
|
52
|
+
const header = Base64Url.encode(
|
|
54
53
|
JSON.stringify({ alg: "RS256", typ: "JWT" }),
|
|
55
54
|
);
|
|
56
|
-
const payload =
|
|
55
|
+
const payload = Base64Url.encode(JSON.stringify(claims));
|
|
57
56
|
const input = `${header}.${payload}`;
|
|
58
57
|
const signature = crypto.sign("sha256", Buffer.from(input), key);
|
|
59
|
-
return `${input}.${
|
|
58
|
+
return `${input}.${Base64Url.encode(new Uint8Array(signature))}`;
|
|
60
59
|
},
|
|
61
60
|
catch: (cause) => new CryptoError({ message: `signing failed: ${cause}` }),
|
|
62
61
|
});
|
|
@@ -243,15 +242,22 @@ const make = Effect.gen(function* () {
|
|
|
243
242
|
).pipe(Effect.map((page) => page.artifacts)),
|
|
244
243
|
|
|
245
244
|
/**
|
|
246
|
-
*
|
|
247
|
-
* repository the commit was pushed to.
|
|
245
|
+
* Open pull requests against `repo` whose head is `sha` in `headRepo`, the
|
|
246
|
+
* repository the commit was pushed to. Commit-associated PR lookups can
|
|
247
|
+
* return no results for forks, so discover candidates by owner and branch.
|
|
248
248
|
*/
|
|
249
|
-
pullRequestsForCommit: (
|
|
249
|
+
pullRequestsForCommit: (
|
|
250
|
+
repo: string,
|
|
251
|
+
headRepo: string,
|
|
252
|
+
headBranch: string,
|
|
253
|
+
sha: string,
|
|
254
|
+
) =>
|
|
250
255
|
asInstallation(
|
|
251
256
|
repo,
|
|
252
|
-
|
|
257
|
+
Pulls.list({
|
|
253
258
|
...split(repo),
|
|
254
|
-
|
|
259
|
+
head: `${split(headRepo).owner}:${headBranch}`,
|
|
260
|
+
state: "open",
|
|
255
261
|
per_page: 100,
|
|
256
262
|
}),
|
|
257
263
|
).pipe(
|
|
@@ -259,6 +265,7 @@ const make = Effect.gen(function* () {
|
|
|
259
265
|
pulls.filter(
|
|
260
266
|
(pr) =>
|
|
261
267
|
pr.head.sha === sha &&
|
|
268
|
+
pr.head.ref === headBranch &&
|
|
262
269
|
pr.head.repo?.full_name === headRepo &&
|
|
263
270
|
pr.base.repo.full_name === repo,
|
|
264
271
|
),
|
package/src/Registry/Handler.ts
CHANGED
|
@@ -4,18 +4,18 @@ import { sha256 } from "alchemy/Util/sha256";
|
|
|
4
4
|
import * as Clock from "effect/Clock";
|
|
5
5
|
import * as Duration from "effect/Duration";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
7
|
-
import
|
|
7
|
+
import { Hex } from "effect/encoding";
|
|
8
8
|
import * as Layer from "effect/Layer";
|
|
9
9
|
import * as Path from "effect/Path";
|
|
10
10
|
import * as Result from "effect/Result";
|
|
11
11
|
import * as Schema from "effect/Schema";
|
|
12
|
-
import * as Etag from "effect/
|
|
13
|
-
import * as FetchHttpClient from "effect/
|
|
14
|
-
import * as HttpPlatform from "effect/
|
|
15
|
-
import * as HttpRouter from "effect/
|
|
16
|
-
import { HttpServerRequest } from "effect/
|
|
17
|
-
import * as HttpServerResponse from "effect/
|
|
18
|
-
import * as HttpApiBuilder from "effect/
|
|
12
|
+
import * as Etag from "effect/http/Etag";
|
|
13
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
14
|
+
import * as HttpPlatform from "effect/http/HttpPlatform";
|
|
15
|
+
import * as HttpRouter from "effect/http/HttpRouter";
|
|
16
|
+
import { HttpServerRequest } from "effect/http/HttpServerRequest";
|
|
17
|
+
import * as HttpServerResponse from "effect/http/HttpServerResponse";
|
|
18
|
+
import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
|
|
19
19
|
import { ManifestJson, type ManifestPackage } from "../Manifest.ts";
|
|
20
20
|
import {
|
|
21
21
|
BadRequest,
|
|
@@ -81,24 +81,26 @@ const isFork = (run: Run) => run.headRepo !== run.repo;
|
|
|
81
81
|
/**
|
|
82
82
|
* Tags every package in a publication receives, all derived from the run.
|
|
83
83
|
* Runs on the repository's own commits get the head commit, the short
|
|
84
|
-
* commit, `branch:<name>`, and `pr:N` for pull requests.
|
|
85
|
-
* only `pr:N
|
|
84
|
+
* commit, `branch:<name>`, and `pr:N` plus `pr:N:<short-sha>` for pull requests.
|
|
85
|
+
* A fork's run gets only `pr:N:<short-sha>`: commit tags are shared by every publisher, and a fork can
|
|
86
86
|
* run any commit it likes, including one already published from the
|
|
87
87
|
* repository, so letting it write them would let it repoint them.
|
|
88
88
|
*/
|
|
89
89
|
export const tagsFor = (run: Run): string[] => {
|
|
90
90
|
if (isFork(run)) {
|
|
91
|
-
return run.pr === null ? [] : [
|
|
91
|
+
return run.pr === null ? [] : [installTag(run)];
|
|
92
92
|
}
|
|
93
93
|
const tags = [run.headSha, run.headSha.slice(0, SHORT)];
|
|
94
|
-
if (run.pr !== null) tags.push(`pr:${run.pr}
|
|
94
|
+
if (run.pr !== null) tags.push(`pr:${run.pr}`, installTag(run));
|
|
95
95
|
if (run.headBranch) tags.push(`branch:${run.headBranch}`);
|
|
96
96
|
return tags;
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
/** The tag install commands are written against. */
|
|
100
100
|
export const installTag = (run: Run) =>
|
|
101
|
-
|
|
101
|
+
run.pr !== null
|
|
102
|
+
? `pr:${run.pr}:${run.headSha.slice(0, SHORT)}`
|
|
103
|
+
: run.headSha.slice(0, SHORT);
|
|
102
104
|
|
|
103
105
|
const upstream = (e: { readonly _tag: string; readonly message?: string }) =>
|
|
104
106
|
new Upstream({ message: GitHub.describe(e) });
|
|
@@ -138,18 +140,28 @@ const lookupRun = Effect.fn("lookupRun")(function* (ref: RunRef) {
|
|
|
138
140
|
const headRepo = data.head_repository?.full_name ?? data.repository.full_name;
|
|
139
141
|
let pr: number | null = null;
|
|
140
142
|
if (data.event === "pull_request") {
|
|
143
|
+
if (data.head_branch === null) {
|
|
144
|
+
return yield* new BadRequest({
|
|
145
|
+
message: `pull request run ${ref.runId} has no head branch`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
141
148
|
// The pull request whose head is this run's head, from the repository
|
|
142
149
|
// the run's head lives in. The same commit can head several pull
|
|
143
150
|
// requests, including one opened from a fork against a commit the
|
|
144
151
|
// repository already published; matching the head repository keeps a
|
|
145
152
|
// run's publication on its own pull request.
|
|
146
153
|
const pulls = yield* github
|
|
147
|
-
.pullRequestsForCommit(
|
|
154
|
+
.pullRequestsForCommit(
|
|
155
|
+
ref.repo,
|
|
156
|
+
headRepo,
|
|
157
|
+
data.head_branch,
|
|
158
|
+
data.head_sha,
|
|
159
|
+
)
|
|
148
160
|
.pipe(Effect.mapError(upstream));
|
|
149
161
|
const first = pulls[0];
|
|
150
162
|
if (first === undefined) {
|
|
151
163
|
return yield* new BadRequest({
|
|
152
|
-
message: `no pull request from ${headRepo} has head ${data.head_sha}`,
|
|
164
|
+
message: `no open pull request from ${headRepo} has head ${data.head_sha}`,
|
|
153
165
|
});
|
|
154
166
|
}
|
|
155
167
|
pr = first.number;
|
|
@@ -339,7 +351,7 @@ const uploadTarball = Effect.fn("uploadTarball")(function* (
|
|
|
339
351
|
.put(key, request.stream, {
|
|
340
352
|
contentLength,
|
|
341
353
|
// The params schema already constrained this to 64 hex chars.
|
|
342
|
-
sha256: Result.getOrThrow(
|
|
354
|
+
sha256: Result.getOrThrow(Hex.decode(sha256)),
|
|
343
355
|
})
|
|
344
356
|
.pipe(
|
|
345
357
|
Effect.mapError(
|
|
@@ -444,7 +456,7 @@ const install = Effect.gen(function* () {
|
|
|
444
456
|
|
|
445
457
|
const banner = Effect.map(origin, (base) =>
|
|
446
458
|
HttpServerResponse.text(
|
|
447
|
-
`Preview package registry. Install with: bun add ${base}/<package>/<commit|branch:name|pr:N>\n`,
|
|
459
|
+
`Preview package registry. Install with: bun add ${base}/<package>/<commit|branch:name|pr:N|pr:N:short-sha>\n`,
|
|
448
460
|
),
|
|
449
461
|
);
|
|
450
462
|
|
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.
|
package/src/cli/bin.ts
CHANGED
|
@@ -2,15 +2,10 @@
|
|
|
2
2
|
import { PlatformServices, runMain } from "alchemy/Util/PlatformServices";
|
|
3
3
|
import * as Config from "effect/Config";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
5
6
|
import * as Layer from "effect/Layer";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
CliError,
|
|
9
|
-
Command,
|
|
10
|
-
Flag,
|
|
11
|
-
GlobalFlag,
|
|
12
|
-
} from "effect/unstable/cli";
|
|
13
|
-
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
7
|
+
import { CliConfig, CliError, Command, Flag, GlobalFlag } from "effect/cli";
|
|
8
|
+
import * as FetchHttpClient from "effect/http/FetchHttpClient";
|
|
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,41 @@ 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(
|
|
42
|
+
"Compare HEAD with this Git ref; defaults to the PR base in Actions",
|
|
43
|
+
),
|
|
44
|
+
Flag.optional,
|
|
45
|
+
),
|
|
46
|
+
all: Flag.Boolean("all").pipe(
|
|
47
|
+
Flag.withDefault(false),
|
|
48
|
+
Flag.withDescription(
|
|
49
|
+
"Pack every configured package regardless of changes",
|
|
50
|
+
),
|
|
51
|
+
),
|
|
52
|
+
rebuildAllPaths: Flag.String("rebuild-all-path").pipe(
|
|
53
|
+
Flag.withDescription(
|
|
54
|
+
"Additional exact path or directory/** forcing all packages (repeatable)",
|
|
55
|
+
),
|
|
56
|
+
Flag.atLeast(0),
|
|
57
|
+
),
|
|
58
|
+
},
|
|
59
|
+
({ group, registry, out, since, all, rebuildAllPaths }) =>
|
|
43
60
|
Effect.gen(function* () {
|
|
44
61
|
const cwd = yield* Effect.sync(() => process.cwd());
|
|
45
|
-
yield* pack({
|
|
62
|
+
yield* pack({
|
|
63
|
+
cwd,
|
|
64
|
+
groups: group,
|
|
65
|
+
registry,
|
|
66
|
+
out,
|
|
67
|
+
since: Option.getOrUndefined(since),
|
|
68
|
+
all,
|
|
69
|
+
rebuildAllPaths,
|
|
70
|
+
});
|
|
46
71
|
}),
|
|
47
72
|
).pipe(
|
|
48
73
|
Command.withDescription(
|
package/src/cli/pack.ts
CHANGED
|
@@ -8,7 +8,7 @@ import * as Path from "effect/Path";
|
|
|
8
8
|
import * as Schema from "effect/Schema";
|
|
9
9
|
import * as SchemaIssue from "effect/SchemaIssue";
|
|
10
10
|
import * as SchemaTransformation from "effect/SchemaTransformation";
|
|
11
|
-
import * as ChildProcess from "effect/
|
|
11
|
+
import * as ChildProcess from "effect/process/ChildProcess";
|
|
12
12
|
import { exec } from "alchemy/Util/exec";
|
|
13
13
|
import { sha256 } from "alchemy/Util/sha256";
|
|
14
14
|
import { packTar, unpackTar, type TarHeader } from "modern-tar";
|
|
@@ -140,6 +140,73 @@ export interface WorkspacePackage {
|
|
|
140
140
|
readonly group: string;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** Shared inputs that invalidate every configured package. */
|
|
144
|
+
export const REBUILD_ALL_PATHS = [
|
|
145
|
+
"package.json",
|
|
146
|
+
"pnpm-lock.yaml",
|
|
147
|
+
"pnpm-workspace.yaml",
|
|
148
|
+
"package-lock.json",
|
|
149
|
+
"yarn.lock",
|
|
150
|
+
"bun.lock",
|
|
151
|
+
"bun.lockb",
|
|
152
|
+
"tsconfig.json",
|
|
153
|
+
"tsconfig.base.json",
|
|
154
|
+
"turbo.json",
|
|
155
|
+
".github/workflows/pkg.yml",
|
|
156
|
+
".github/workflows/pr-package.yml",
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
/** Select dependents first, then dependencies, without pulling in unrelated siblings. */
|
|
160
|
+
export const selectPackages = (
|
|
161
|
+
packages: ReadonlyArray<WorkspacePackage>,
|
|
162
|
+
dependencies: ReadonlyMap<string, ReadonlySet<string>>,
|
|
163
|
+
changedFiles: ReadonlyArray<string>,
|
|
164
|
+
rebuildAllPaths: ReadonlyArray<string> = [],
|
|
165
|
+
) => {
|
|
166
|
+
if (
|
|
167
|
+
changedFiles.some((file) =>
|
|
168
|
+
[...REBUILD_ALL_PATHS, ...rebuildAllPaths].some((pattern) =>
|
|
169
|
+
pattern.endsWith("/**")
|
|
170
|
+
? file === pattern.slice(0, -3) ||
|
|
171
|
+
file.startsWith(pattern.slice(0, -2))
|
|
172
|
+
: file === pattern,
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
return [...packages];
|
|
177
|
+
|
|
178
|
+
const selected = new Set(
|
|
179
|
+
packages
|
|
180
|
+
.filter((pkg) =>
|
|
181
|
+
changedFiles.some(
|
|
182
|
+
(file) =>
|
|
183
|
+
file === pkg.dir ||
|
|
184
|
+
file.startsWith(`${pkg.dir}/`) ||
|
|
185
|
+
pkg.dir.startsWith(`${file}/`),
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
.map((pkg) => pkg.name),
|
|
189
|
+
);
|
|
190
|
+
// A changed gitlink selects the packages nested inside that submodule.
|
|
191
|
+
let size: number;
|
|
192
|
+
do {
|
|
193
|
+
size = selected.size;
|
|
194
|
+
for (const [name, deps] of dependencies) {
|
|
195
|
+
if ([...deps].some((dep) => selected.has(dep))) selected.add(name);
|
|
196
|
+
}
|
|
197
|
+
} while (selected.size !== size);
|
|
198
|
+
const pending = [...selected];
|
|
199
|
+
for (const name of pending) {
|
|
200
|
+
for (const dep of dependencies.get(name) ?? []) {
|
|
201
|
+
if (!selected.has(dep)) {
|
|
202
|
+
selected.add(dep);
|
|
203
|
+
pending.push(dep);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return packages.filter((pkg) => selected.has(pkg.name));
|
|
208
|
+
};
|
|
209
|
+
|
|
143
210
|
/**
|
|
144
211
|
* Expand one level of `{a,b,c}` alternatives into plain patterns, so
|
|
145
212
|
* `./packages/{alchemy,pkg}` lists exactly those two directories.
|
|
@@ -418,18 +485,21 @@ export const packPackage = Effect.fn("packPackage")(function* (options: {
|
|
|
418
485
|
const PullRequestEvent = Schema.fromJsonString(
|
|
419
486
|
Schema.Struct({
|
|
420
487
|
pull_request: Schema.optionalKey(
|
|
421
|
-
Schema.Struct({
|
|
488
|
+
Schema.Struct({
|
|
489
|
+
head: Schema.Struct({ sha: Schema.String }),
|
|
490
|
+
base: Schema.Struct({ sha: Schema.String }),
|
|
491
|
+
}),
|
|
422
492
|
),
|
|
423
493
|
}),
|
|
424
494
|
);
|
|
425
495
|
|
|
426
496
|
/**
|
|
427
|
-
*
|
|
428
|
-
* `
|
|
497
|
+
* Pull request metadata from the GitHub Actions event payload.
|
|
498
|
+
* `undefined` outside pull request events.
|
|
429
499
|
* On that event the default checkout is a synthetic merge commit, which the
|
|
430
500
|
* registry would reject because it does not match the run's head.
|
|
431
501
|
*/
|
|
432
|
-
const
|
|
502
|
+
const pullRequest = Effect.gen(function* () {
|
|
433
503
|
const event = yield* Config.option(Config.String("GITHUB_EVENT_NAME"));
|
|
434
504
|
const eventPath = yield* Config.option(Config.String("GITHUB_EVENT_PATH"));
|
|
435
505
|
if (
|
|
@@ -442,7 +512,7 @@ const pullRequestHead = Effect.gen(function* () {
|
|
|
442
512
|
const payload = yield* fs
|
|
443
513
|
.readFileString(eventPath.value)
|
|
444
514
|
.pipe(Effect.flatMap(Schema.decodeUnknownEffect(PullRequestEvent)));
|
|
445
|
-
return payload.pull_request
|
|
515
|
+
return payload.pull_request;
|
|
446
516
|
});
|
|
447
517
|
|
|
448
518
|
export interface PackOptions {
|
|
@@ -450,6 +520,9 @@ export interface PackOptions {
|
|
|
450
520
|
readonly groups: ReadonlyArray<Group>;
|
|
451
521
|
readonly registry: string;
|
|
452
522
|
readonly out: string;
|
|
523
|
+
readonly since?: string;
|
|
524
|
+
readonly all?: boolean;
|
|
525
|
+
readonly rebuildAllPaths?: ReadonlyArray<string>;
|
|
453
526
|
}
|
|
454
527
|
|
|
455
528
|
/**
|
|
@@ -470,7 +543,8 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
470
543
|
|
|
471
544
|
const root = yield* toplevel(options.cwd);
|
|
472
545
|
const head = yield* gitHead(root);
|
|
473
|
-
const
|
|
546
|
+
const pr = yield* pullRequest;
|
|
547
|
+
const prHead = pr?.head.sha;
|
|
474
548
|
if (prHead !== undefined && prHead !== head) {
|
|
475
549
|
return yield* new WorkspaceError({
|
|
476
550
|
message:
|
|
@@ -478,11 +552,7 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
478
552
|
"Check out github.event.pull_request.head.sha before packing so tarballs are addressed by a commit that exists on the pull request.",
|
|
479
553
|
});
|
|
480
554
|
}
|
|
481
|
-
|
|
482
|
-
if (packages.length === 0) {
|
|
483
|
-
yield* Console.log("No publishable packages matched.");
|
|
484
|
-
return undefined;
|
|
485
|
-
}
|
|
555
|
+
let packages = yield* discover(options.cwd, options.groups);
|
|
486
556
|
|
|
487
557
|
// Dependencies between packed packages are rewritten to the dependency's
|
|
488
558
|
// immutable tarball URL, so a package is packed only after everything it
|
|
@@ -506,6 +576,38 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
506
576
|
),
|
|
507
577
|
);
|
|
508
578
|
}
|
|
579
|
+
const since = options.all ? undefined : (options.since ?? pr?.base.sha);
|
|
580
|
+
if (since !== undefined) {
|
|
581
|
+
// Disable rename detection so moves affect both the old and new packages.
|
|
582
|
+
const changed = (yield* git(root, [
|
|
583
|
+
"diff",
|
|
584
|
+
"--name-only",
|
|
585
|
+
"--no-renames",
|
|
586
|
+
"-z",
|
|
587
|
+
since,
|
|
588
|
+
head,
|
|
589
|
+
"--",
|
|
590
|
+
]))
|
|
591
|
+
.split("\0")
|
|
592
|
+
.filter(Boolean);
|
|
593
|
+
const selected = selectPackages(
|
|
594
|
+
packages.map((pkg) => ({
|
|
595
|
+
...pkg,
|
|
596
|
+
dir: path.relative(root, pkg.absDir).split(path.sep).join("/"),
|
|
597
|
+
})),
|
|
598
|
+
dependencies,
|
|
599
|
+
changed,
|
|
600
|
+
options.rebuildAllPaths,
|
|
601
|
+
);
|
|
602
|
+
const names = new Set(selected.map((pkg) => pkg.name));
|
|
603
|
+
packages = packages.filter((pkg) => names.has(pkg.name));
|
|
604
|
+
for (const name of dependencies.keys()) {
|
|
605
|
+
if (!names.has(name)) dependencies.delete(name);
|
|
606
|
+
}
|
|
607
|
+
yield* Console.log(
|
|
608
|
+
`Selected ${packages.length} package(s) changed since ${since}, including dependents and dependencies.`,
|
|
609
|
+
);
|
|
610
|
+
}
|
|
509
611
|
const levels = yield* dependencyLevels(dependencies);
|
|
510
612
|
|
|
511
613
|
// The output directory is replaced wholesale, so it has to be a proper
|
|
@@ -594,9 +696,13 @@ export const pack = Effect.fn("pack")(function* (options: PackOptions) {
|
|
|
594
696
|
const artifact = manifestArtifactName(yield* sha256(manifestText));
|
|
595
697
|
const stepOutput = yield* Config.option(Config.String("GITHUB_OUTPUT"));
|
|
596
698
|
if (Option.isSome(stepOutput)) {
|
|
597
|
-
yield* fs.writeFileString(
|
|
598
|
-
|
|
599
|
-
|
|
699
|
+
yield* fs.writeFileString(
|
|
700
|
+
stepOutput.value,
|
|
701
|
+
`artifact-name=${artifact}\npackage-count=${entries.length}\n`,
|
|
702
|
+
{
|
|
703
|
+
flag: "a",
|
|
704
|
+
},
|
|
705
|
+
);
|
|
600
706
|
}
|
|
601
707
|
yield* Console.log(`Manifest artifact name: ${artifact}`);
|
|
602
708
|
return manifest;
|
package/src/cli/publish.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as Effect from "effect/Effect";
|
|
|
5
5
|
import * as FileSystem from "effect/FileSystem";
|
|
6
6
|
import * as Path from "effect/Path";
|
|
7
7
|
import * as Schema from "effect/Schema";
|
|
8
|
-
import * as HttpApiClient from "effect/
|
|
8
|
+
import * as HttpApiClient from "effect/http-api/HttpApiClient";
|
|
9
9
|
import { MANIFEST_FILE, ManifestJson, type Manifest } from "../Manifest.ts";
|
|
10
10
|
import { PkgApi, type TarballRef } from "../Protocol.ts";
|
|
11
11
|
|
|
@@ -71,6 +71,11 @@ export const publish = Effect.fn("publish")(function* (
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
if (manifest.packages.length === 0) {
|
|
75
|
+
yield* Console.log("No affected packages to publish.");
|
|
76
|
+
return { packages: [] };
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
const upload = (pkg: Manifest["packages"][number]) =>
|
|
75
80
|
Effect.gen(function* () {
|
|
76
81
|
const bytes = yield* fs.readFile(path.join(dir, pkg.file));
|