@dbx-tools/projen 0.9.69 → 0.9.71

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 CHANGED
@@ -490,8 +490,9 @@ bun run release --release-notes-instructions "Focus on operator-visible changes.
490
490
 
491
491
  `--demo-deploy` is off by default. After tagging and any local registry
492
492
  publication it runs `bun run demo:deploy`, which stages the AppKit demo
493
- and deploys it to the FEVM workspace. That step is local only and is not
494
- recorded in the annotated tag.
493
+ from locally compiled Node packages and locally built Python wheels, resolves
494
+ the configured/default workspace profile through `@dbx-tools/auth`, and deploys
495
+ it. That step is local only and is not recorded in the annotated tag.
495
496
 
496
497
  After bump and configured validation, release writes
497
498
  `docs/releases/vX.Y.Z.md` with `dbx genie exec -C "$PWD" --sandbox read-only
package/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/projen";
6
- export const PACKAGE_VERSION = "0.9.69";
6
+ export const PACKAGE_VERSION = "0.9.71";
7
7
  export * as barrels from "./src/barrels.ts";
8
8
  export * as bunApp from "./src/bun-app.ts";
9
9
  export * as bunWorkflow from "./src/bun-workflow.ts";
package/package.json CHANGED
@@ -30,9 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@clack/prompts": "^1.7.0",
33
- "@dbx-tools/core": "^0.9.69",
34
- "@dbx-tools/path": "^0.9.69",
35
- "@dbx-tools/shared-core": "^0.9.69",
33
+ "@dbx-tools/core": "^0.9.71",
34
+ "@dbx-tools/path": "^0.9.71",
35
+ "@dbx-tools/shared-core": "^0.9.71",
36
36
  "commander": "^15.0.0",
37
37
  "concurrently": "^10.0.3",
38
38
  "constructs": "^10.6.0",
@@ -54,11 +54,12 @@
54
54
  },
55
55
  "main": "index.ts",
56
56
  "license": "Apache-2.0",
57
- "version": "0.9.69",
57
+ "version": "0.9.71",
58
58
  "types": "index.ts",
59
59
  "type": "module",
60
60
  "exports": {
61
61
  ".": "./index.ts",
62
+ "./python-release-packaging": "./tasks/publish-python.ts",
62
63
  "./release-packaging": "./tasks/publish-npm.ts",
63
64
  "./package.json": "./package.json"
64
65
  },
@@ -217,9 +217,9 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
217
217
  const entrypoint = Bun.resolveSync(binding.entrypoint, binding.projectDirectory);
218
218
  return binding.modules.length > 0
219
219
  ? binding.modules.map((module) => ({
220
- key: runtimeModuleName(binding, module),
221
- entrypoint: resolvePythonNodeBindingModule(entrypoint, module),
222
- }))
220
+ key: runtimeModuleName(binding, module),
221
+ entrypoint: resolvePythonNodeBindingModule(entrypoint, module),
222
+ }))
223
223
  : [{ key: runtimeModuleName(binding), entrypoint }];
224
224
  });
225
225
  const runtimeExports = [
@@ -320,8 +320,9 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
320
320
  const result = await Bun.build({
321
321
  entrypoints: [shimEntry],
322
322
  format: "cjs",
323
- plugins: [runtimePlugin],
324
323
  target: "browser",
324
+ conditions: ["require"],
325
+ plugins: [runtimePlugin],
325
326
  });
326
327
  if (!result.success) {
327
328
  for (const message of result.logs) logger.error(message);
@@ -366,8 +367,8 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
366
367
  ...bindings.flatMap((binding) => [
367
368
  ...(binding.modules.length > 0
368
369
  ? binding.modules.map((module) =>
369
- join(binding.bindingDirectory, `${pythonFunctionName(module)}.py`),
370
- )
370
+ join(binding.bindingDirectory, `${pythonFunctionName(module)}.py`),
371
+ )
371
372
  : [join(binding.bindingDirectory, "index.py")]),
372
373
  ]),
373
374
  ].filter((file): file is string => Boolean(file)),
@@ -986,18 +987,18 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
986
987
  function pythonDataclass(record: PythonRecord): string {
987
988
  const body = record.fields.length
988
989
  ? record.fields
989
- .map((field) => {
990
- const defaultArgument = Object.hasOwn(field, "defaultValue")
991
- ? pythonDefault(field.defaultValue)
992
- : "default=None";
993
- return [
994
- ` ${field.pythonName}: ${pythonOptionalType(field.type)} = field(`,
995
- ` ${defaultArgument},`,
996
- ` metadata={"javascript_name": ${JSON.stringify(field.javascriptName)}},`,
997
- " )",
998
- ].join("\n");
999
- })
1000
- .join("\n")
990
+ .map((field) => {
991
+ const defaultArgument = Object.hasOwn(field, "defaultValue")
992
+ ? pythonDefault(field.defaultValue)
993
+ : "default=None";
994
+ return [
995
+ ` ${field.pythonName}: ${pythonOptionalType(field.type)} = field(`,
996
+ ` ${defaultArgument},`,
997
+ ` metadata={"javascript_name": ${JSON.stringify(field.javascriptName)}},`,
998
+ " )",
999
+ ].join("\n");
1000
+ })
1001
+ .join("\n")
1001
1002
  : " pass";
1002
1003
  return `@dataclass(kw_only=True)\nclass ${record.name}:\n${body}`;
1003
1004
  }
@@ -1025,11 +1026,11 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
1025
1026
  }
1026
1027
  const body = response.fields.length
1027
1028
  ? response.fields
1028
- .map(
1029
- ({ name, required, type }) =>
1030
- ` ${name}: ${required ? type : `NotRequired[${type}]`}`,
1031
- )
1032
- .join("\n")
1029
+ .map(
1030
+ ({ name, required, type }) =>
1031
+ ` ${name}: ${required ? type : `NotRequired[${type}]`}`,
1032
+ )
1033
+ .join("\n")
1033
1034
  : " pass";
1034
1035
  return `class ${response.name}(TypedDict):\n${body}`;
1035
1036
  }
@@ -1044,18 +1045,18 @@ export async function main(args: string[] = process.argv.slice(2)): Promise<void
1044
1045
  );
1045
1046
  const methods = protocol.methods.length
1046
1047
  ? protocol.methods
1047
- .map((method) => {
1048
- const parameters = method.parameters.map(
1049
- ({ name, required, type }) => ` ${name}: ${type}${required ? "" : " = ..."},`,
1050
- );
1051
- return [
1052
- ` async def ${method.name}(`,
1053
- " self,",
1054
- ...parameters,
1055
- ` ) -> ${method.returnType}: ...`,
1056
- ].join("\n");
1057
- })
1058
- .join("\n\n")
1048
+ .map((method) => {
1049
+ const parameters = method.parameters.map(
1050
+ ({ name, required, type }) => ` ${name}: ${type}${required ? "" : " = ..."},`,
1051
+ );
1052
+ return [
1053
+ ` async def ${method.name}(`,
1054
+ " self,",
1055
+ ...parameters,
1056
+ ` ) -> ${method.returnType}: ...`,
1057
+ ].join("\n");
1058
+ })
1059
+ .join("\n\n")
1059
1060
  : "";
1060
1061
  const body = [...properties, methods].filter(Boolean).join("\n\n") || " pass";
1061
1062
  return `class ${protocol.name}(Protocol):\n${body}`;
package/tasks/release.ts CHANGED
@@ -41,31 +41,22 @@ function assertAncestor(root: string, ancestor: string, descendant: string, mess
41
41
  }
42
42
  }
43
43
 
44
- /** Parse a GitHub repository slug from an HTTPS or SSH remote URL. */
45
- export function githubRepositoryFromRemoteUrl(remoteUrl: string): string | undefined {
46
- const value = remoteUrl.trim().replace(/\.git$/, "");
47
- const https = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)$/i.exec(value);
48
- if (https) return `${https[1]}/${https[2]}`;
49
- const ssh = /^(?:ssh:\/\/)?git@([^/:]+)[:/]([^/]+)\/([^/]+)$/i.exec(value);
50
- if (!ssh || !/^github(?:[.-].+)?$/i.test(ssh[1]!)) return undefined;
51
- return `${ssh[2]}/${ssh[3]}`;
52
- }
53
-
54
- /** Infer the `gh auth` account encoded by a `git@github-<account>:` SSH alias. */
55
- export function githubAccountFromRemoteUrl(remoteUrl: string): string | undefined {
56
- return /^git@github-([^:]+):/i.exec(remoteUrl.trim())?.[1];
57
- }
58
-
59
- function githubApiEnvironment(root: string, remoteUrl: string): NodeJS.ProcessEnv | undefined {
60
- const account = githubAccountFromRemoteUrl(remoteUrl);
61
- if (!account) return undefined;
62
- const token = captureTaskCommand(
63
- root,
64
- "gh",
65
- ["auth", "token", "--hostname", "github.com", "--user", account],
66
- { check: true, stderr: "inherit" },
67
- );
68
- return token ? { ...process.env, GH_TOKEN: token } : undefined;
44
+ /** Replace the local annotated tag object with the tag object created on the remote. */
45
+ export function refreshReleaseTagFromRemote(options: {
46
+ readonly expectedObject: string;
47
+ readonly remote: string;
48
+ readonly root: string;
49
+ readonly tag: string;
50
+ }): void {
51
+ const { expectedObject, remote, root, tag } = options;
52
+ const ref = `refs/tags/${tag}`;
53
+ runTaskCommand(root, "git", ["fetch", "--force", remote, `${ref}:${ref}`]);
54
+ const localObject = captureTaskCommand(root, "git", ["rev-parse", ref], { check: true });
55
+ if (localObject !== expectedObject) {
56
+ throw new Error(
57
+ `remote ${tag} resolved to ${localObject || "<empty>"}, expected ${expectedObject}`,
58
+ );
59
+ }
69
60
  }
70
61
 
71
62
  function pushAnnotatedReleaseTag(options: {
@@ -78,15 +69,12 @@ function pushAnnotatedReleaseTag(options: {
78
69
  }): void {
79
70
  const { annotation, branch, head, remote, root, tag } = options;
80
71
  runTaskCommand(root, "git", ["tag", "--annotate", tag, "--message", annotation]);
81
- const remoteUrl = captureTaskCommand(root, "git", ["remote", "get-url", remote], {
82
- check: true,
83
- });
84
- const repository = githubRepositoryFromRemoteUrl(remoteUrl);
85
- if (!repository || !taskCommandSucceeds(root, "gh", ["--version"])) {
72
+ const githubAccount = projectUtils.resolveProjectGhAccount(root, { remote });
73
+ if (!githubAccount) {
74
+ logger.warn("no authenticated GitHub account can access the release repository; using git push");
86
75
  runTaskCommand(root, "git", ["push", remote, `refs/tags/${tag}`]);
87
76
  return;
88
77
  }
89
- const githubEnv = githubApiEnvironment(root, remoteUrl);
90
78
 
91
79
  const remoteBranch = captureTaskCommand(
92
80
  root,
@@ -97,48 +85,70 @@ function pushAnnotatedReleaseTag(options: {
97
85
  .split(/\s+/)[0]
98
86
  ?.trim();
99
87
  if (remoteBranch !== head) {
100
- throw new Error(`cannot tag ${repository}: ${remote}/${branch} does not equal local HEAD`);
88
+ throw new Error(
89
+ `cannot tag ${githubAccount.remote.repository}: ${remote}/${branch} does not equal local HEAD`,
90
+ );
91
+ }
92
+ let remoteRefCreated = false;
93
+ try {
94
+ const tagObject = captureTaskCommand(
95
+ root,
96
+ "gh",
97
+ [
98
+ "api",
99
+ "--hostname",
100
+ githubAccount.host,
101
+ "--method",
102
+ "POST",
103
+ `repos/${githubAccount.remote.repository}/git/tags`,
104
+ "-f",
105
+ `tag=${tag}`,
106
+ "-f",
107
+ `message=${annotation}`,
108
+ "-f",
109
+ `object=${head}`,
110
+ "-f",
111
+ "type=commit",
112
+ "--jq",
113
+ ".sha",
114
+ ],
115
+ { check: true, env: githubAccount.env, stderr: "inherit" },
116
+ );
117
+ if (!tagObject) throw new Error(`GitHub did not return an annotated tag object for ${tag}`);
118
+ runTaskCommand(
119
+ root,
120
+ "gh",
121
+ [
122
+ "api",
123
+ "--hostname",
124
+ githubAccount.host,
125
+ "--method",
126
+ "POST",
127
+ `repos/${githubAccount.remote.repository}/git/refs`,
128
+ "-f",
129
+ `ref=refs/tags/${tag}`,
130
+ "-f",
131
+ `sha=${tagObject}`,
132
+ "--silent",
133
+ ],
134
+ { env: githubAccount.env },
135
+ );
136
+ remoteRefCreated = true;
137
+ refreshReleaseTagFromRemote({ expectedObject: tagObject, remote, root, tag });
138
+ logger.info(`created ${tag} through the GitHub API`, {
139
+ account: githubAccount.login,
140
+ host: githubAccount.host,
141
+ repository: githubAccount.remote.repository,
142
+ sha: head,
143
+ });
144
+ } catch (error) {
145
+ if (remoteRefCreated) throw error;
146
+ logger.warn("GitHub API tag creation failed; using git push", {
147
+ error,
148
+ repository: githubAccount.remote.repository,
149
+ });
150
+ runTaskCommand(root, "git", ["push", remote, `refs/tags/${tag}`]);
101
151
  }
102
-
103
- const tagObject = captureTaskCommand(
104
- root,
105
- "gh",
106
- [
107
- "api",
108
- "--method",
109
- "POST",
110
- `repos/${repository}/git/tags`,
111
- "-f",
112
- `tag=${tag}`,
113
- "-f",
114
- `message=${annotation}`,
115
- "-f",
116
- `object=${head}`,
117
- "-f",
118
- "type=commit",
119
- "--jq",
120
- ".sha",
121
- ],
122
- { check: true, env: githubEnv, stderr: "inherit" },
123
- );
124
- if (!tagObject) throw new Error(`GitHub did not return an annotated tag object for ${tag}`);
125
- runTaskCommand(
126
- root,
127
- "gh",
128
- [
129
- "api",
130
- "--method",
131
- "POST",
132
- `repos/${repository}/git/refs`,
133
- "-f",
134
- `ref=refs/tags/${tag}`,
135
- "-f",
136
- `sha=${tagObject}`,
137
- "--silent",
138
- ],
139
- { env: githubEnv },
140
- );
141
- logger.info(`created ${tag} through the GitHub API`, { repository, sha: head });
142
152
  }
143
153
 
144
154
  function prepareReleaseBranch(options: {