wyvrnpm 2.3.2 → 2.8.1
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 +465 -19
- package/bin/wyvrn.js +175 -9
- package/claude/skills/wyvrnpm.skill +0 -0
- package/cmake/macros.cmake +51 -0
- package/cmake/variables.cmake +13 -3
- package/package.json +1 -1
- package/src/auth.js +66 -0
- package/src/build/cache.js +196 -0
- package/src/build/index.js +23 -2
- package/src/build/msvc-env.js +50 -7
- package/src/build/recipe.js +36 -3
- package/src/commands/add.js +140 -0
- package/src/commands/build.js +111 -7
- package/src/commands/cache.js +189 -0
- package/src/commands/configure.js +15 -4
- package/src/commands/init.js +36 -0
- package/src/commands/install-skill.js +8 -0
- package/src/commands/install.js +271 -5
- package/src/commands/profile.js +70 -7
- package/src/commands/publish.js +41 -4
- package/src/commands/show.js +14 -4
- package/src/compat.js +21 -9
- package/src/conf/index.js +391 -0
- package/src/conf/namespaces.js +94 -0
- package/src/download.js +136 -148
- package/src/logger.js +30 -2
- package/src/manifest.js +34 -7
- package/src/profile.js +148 -12
- package/src/resolve.js +47 -7
- package/src/settings-overrides.js +152 -0
- package/src/toolchain/index.js +71 -0
- package/src/toolchain/presets.js +108 -31
- package/src/toolchain/template.cmake +11 -0
- package/src/upload-built.js +11 -2
- package/src/zip-safe.js +52 -0
package/src/toolchain/presets.js
CHANGED
|
@@ -4,6 +4,21 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
const VENDOR_KEY = 'wyvrnpm/generated';
|
|
7
|
+
|
|
8
|
+
// Local copy of the arch→VS-platform mapping. Duplicated here rather than
|
|
9
|
+
// imported from ./index so preset generation can run standalone (keeps
|
|
10
|
+
// the dependency graph simple — presets.js doesn't otherwise need the
|
|
11
|
+
// toolchain generator). Keep in sync with `archToVsPlatform` in index.js.
|
|
12
|
+
const ARCH_VS_PLATFORM = {
|
|
13
|
+
x86_64: 'x64',
|
|
14
|
+
x86: 'Win32',
|
|
15
|
+
armv8: 'ARM64',
|
|
16
|
+
armv7: 'ARM',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function isVsGenerator(name) {
|
|
20
|
+
return typeof name === 'string' && /^Visual Studio \d/.test(name);
|
|
21
|
+
}
|
|
7
22
|
const SCHEMA_VERSION = 3; // requires CMake ≥ 3.21
|
|
8
23
|
|
|
9
24
|
/**
|
|
@@ -52,6 +67,25 @@ function isWyvrnGenerated(presets) {
|
|
|
52
67
|
* `CMAKE_TOOLCHAIN_FILE`. Usually
|
|
53
68
|
* derived from the project's own
|
|
54
69
|
* `build.configure` -D args.
|
|
70
|
+
* @param {string|null} [args.profileArch] Active profile arch — used to pin
|
|
71
|
+
* `architecture` for VS generators
|
|
72
|
+
* so `cmake --preset` produces
|
|
73
|
+
* binaries matching the profile
|
|
74
|
+
* (VS otherwise defaults to x64).
|
|
75
|
+
* @param {string[]|null} [args.configs] Build recipe's `configs` list
|
|
76
|
+
* (e.g. ['Debug','Release','MinSizeRel']).
|
|
77
|
+
* When provided, drives
|
|
78
|
+
* `CMAKE_CONFIGURATION_TYPES` so
|
|
79
|
+
* multi-config generators (Ninja
|
|
80
|
+
* Multi-Config, VS) actually
|
|
81
|
+
* produce every config the recipe
|
|
82
|
+
* declares. Null/empty → omitted
|
|
83
|
+
* (let CMake use its generator
|
|
84
|
+
* default). `extraCacheVariables`
|
|
85
|
+
* wins on explicit collision so
|
|
86
|
+
* authors that already bake the
|
|
87
|
+
* list into `build.configure`
|
|
88
|
+
* aren't overridden.
|
|
55
89
|
*/
|
|
56
90
|
function buildConfigurePreset({
|
|
57
91
|
profileName,
|
|
@@ -60,16 +94,21 @@ function buildConfigurePreset({
|
|
|
60
94
|
generator = null,
|
|
61
95
|
extraCacheVariables = null,
|
|
62
96
|
installDir = null,
|
|
97
|
+
profileArch = null,
|
|
98
|
+
configs = null,
|
|
63
99
|
}) {
|
|
64
100
|
const binaryDir = `\${sourceDir}/build/wyvrn-${profileName}`;
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
//
|
|
71
|
-
|
|
101
|
+
// CMAKE_CONFIGURATION_TYPES is a defensible default from the recipe's
|
|
102
|
+
// declared `configs` list. Applied FIRST (lowest precedence) so an
|
|
103
|
+
// explicit `-DCMAKE_CONFIGURATION_TYPES=...` in `build.configure`
|
|
104
|
+
// still wins — authors who already pin the list shouldn't silently
|
|
105
|
+
// lose their override. The wyvrn-owned entries (toolchain, install
|
|
106
|
+
// prefix) still win over both below.
|
|
107
|
+
const configsDefault = (Array.isArray(configs) && configs.length > 0)
|
|
108
|
+
? { CMAKE_CONFIGURATION_TYPES: configs.join(';') }
|
|
109
|
+
: {};
|
|
72
110
|
const cacheVariables = {
|
|
111
|
+
...configsDefault,
|
|
73
112
|
...(extraCacheVariables ?? {}),
|
|
74
113
|
CMAKE_TOOLCHAIN_FILE: `\${sourceDir}/${toolchainRelPath}`,
|
|
75
114
|
};
|
|
@@ -90,31 +129,49 @@ function buildConfigurePreset({
|
|
|
90
129
|
cacheVariables,
|
|
91
130
|
};
|
|
92
131
|
if (generator) preset.generator = generator;
|
|
132
|
+
|
|
133
|
+
// Visual Studio generators default to x64 when `architecture` is
|
|
134
|
+
// unspecified — which silently overrides the profile arch and makes
|
|
135
|
+
// find_package() reject 32-bit installed packages with a pointer-size
|
|
136
|
+
// mismatch. Pin `architecture` from the profile so `cmake --preset`
|
|
137
|
+
// produces the right artefacts end-to-end.
|
|
138
|
+
if (isVsGenerator(generator) && profileArch && ARCH_VS_PLATFORM[profileArch]) {
|
|
139
|
+
preset.architecture = {
|
|
140
|
+
value: ARCH_VS_PLATFORM[profileArch],
|
|
141
|
+
strategy: 'set',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
93
145
|
return preset;
|
|
94
146
|
}
|
|
95
147
|
|
|
96
148
|
/**
|
|
97
|
-
*
|
|
149
|
+
* One build preset per configuration the recipe declares (or the
|
|
150
|
+
* legacy default pair `Debug` + `Release` when no list is provided).
|
|
151
|
+
*
|
|
152
|
+
* The preset name suffix is the configuration name lower-cased — matches
|
|
153
|
+
* the previous Debug/Release naming for those two, and extends to
|
|
154
|
+
* `wyvrn-<profile>-relwithdebinfo` / `-minsizerel` for the multi-config
|
|
155
|
+
* cases. Authors whose recipes include `MinSizeRel` in `build.configs`
|
|
156
|
+
* can now `cmake --build --preset wyvrn-<profile>-minsizerel` directly.
|
|
98
157
|
*
|
|
99
158
|
* @param {string} profileName
|
|
159
|
+
* @param {string[]|null} [configs] Recipe's declared configurations. When
|
|
160
|
+
* null/empty, emits the pre-2.8.1 `Debug` + `Release` pair so existing
|
|
161
|
+
* CMakePresets.json files remain byte-identical.
|
|
100
162
|
* @returns {Array<object>}
|
|
101
163
|
*/
|
|
102
|
-
function buildBuildPresets(profileName) {
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
displayName: `wyvrn ${profileName} (Release)`,
|
|
114
|
-
configurePreset: config,
|
|
115
|
-
configuration: 'Release',
|
|
116
|
-
},
|
|
117
|
-
];
|
|
164
|
+
function buildBuildPresets(profileName, configs = null) {
|
|
165
|
+
const base = `wyvrn-${profileName}`;
|
|
166
|
+
const list = (Array.isArray(configs) && configs.length > 0)
|
|
167
|
+
? configs
|
|
168
|
+
: ['Debug', 'Release'];
|
|
169
|
+
return list.map((cfg) => ({
|
|
170
|
+
name: `${base}-${cfg.toLowerCase()}`,
|
|
171
|
+
displayName: `wyvrn ${profileName} (${cfg})`,
|
|
172
|
+
configurePreset: base,
|
|
173
|
+
configuration: cfg,
|
|
174
|
+
}));
|
|
118
175
|
}
|
|
119
176
|
|
|
120
177
|
/**
|
|
@@ -127,6 +184,8 @@ function buildFreshPresetsFile({
|
|
|
127
184
|
generator = null,
|
|
128
185
|
extraCacheVariables = null,
|
|
129
186
|
installDir = null,
|
|
187
|
+
profileArch = null,
|
|
188
|
+
configs = null,
|
|
130
189
|
}) {
|
|
131
190
|
return {
|
|
132
191
|
version: SCHEMA_VERSION,
|
|
@@ -138,9 +197,9 @@ function buildFreshPresetsFile({
|
|
|
138
197
|
},
|
|
139
198
|
},
|
|
140
199
|
configurePresets: [buildConfigurePreset({
|
|
141
|
-
profileName, profileHash, toolchainRelPath, generator, extraCacheVariables, installDir,
|
|
200
|
+
profileName, profileHash, toolchainRelPath, generator, extraCacheVariables, installDir, profileArch, configs,
|
|
142
201
|
})],
|
|
143
|
-
buildPresets: buildBuildPresets(profileName),
|
|
202
|
+
buildPresets: buildBuildPresets(profileName, configs),
|
|
144
203
|
};
|
|
145
204
|
}
|
|
146
205
|
|
|
@@ -161,18 +220,34 @@ function mergeIntoExisting(existing, {
|
|
|
161
220
|
generator = null,
|
|
162
221
|
extraCacheVariables = null,
|
|
163
222
|
installDir = null,
|
|
223
|
+
profileArch = null,
|
|
224
|
+
configs = null,
|
|
164
225
|
}) {
|
|
165
226
|
const newConfigure = buildConfigurePreset({
|
|
166
|
-
profileName, profileHash, toolchainRelPath, generator, extraCacheVariables, installDir,
|
|
227
|
+
profileName, profileHash, toolchainRelPath, generator, extraCacheVariables, installDir, profileArch, configs,
|
|
167
228
|
});
|
|
168
|
-
const newBuilds = buildBuildPresets(profileName);
|
|
229
|
+
const newBuilds = buildBuildPresets(profileName, configs);
|
|
169
230
|
const newConfigureNames = new Set([newConfigure.name]);
|
|
170
|
-
|
|
231
|
+
// When replacing THIS profile's build presets, drop every pre-existing
|
|
232
|
+
// one that targets the same configurePreset — the fresh `newBuilds`
|
|
233
|
+
// list fully replaces them. Scoping to configurePreset avoids stepping
|
|
234
|
+
// on presets for OTHER profiles in the same file, which is the whole
|
|
235
|
+
// point of the merge path.
|
|
236
|
+
//
|
|
237
|
+
// Without this, editing `build.configs` from 4 configs down to 2 would
|
|
238
|
+
// leave orphan build presets pointing at configurations CMake won't
|
|
239
|
+
// generate.
|
|
240
|
+
const myBase = `wyvrn-${profileName}`;
|
|
241
|
+
const isMyBuildPreset = (p) => p && p.configurePreset === myBase;
|
|
171
242
|
|
|
172
243
|
const configurePresets = (existing.configurePresets ?? []).filter((p) => !newConfigureNames.has(p.name));
|
|
173
244
|
configurePresets.push(newConfigure);
|
|
174
245
|
|
|
175
|
-
|
|
246
|
+
// Drop every pre-existing build preset that targets THIS profile's
|
|
247
|
+
// configurePreset — the fresh `newBuilds` list (one per current
|
|
248
|
+
// `configs` entry) fully replaces them. Presets for OTHER profiles
|
|
249
|
+
// stay untouched so the multi-profile merge story is preserved.
|
|
250
|
+
const buildPresets = (existing.buildPresets ?? []).filter((p) => !isMyBuildPreset(p));
|
|
176
251
|
buildPresets.push(...newBuilds);
|
|
177
252
|
|
|
178
253
|
return {
|
|
@@ -223,8 +298,10 @@ function generateCMakePresets({
|
|
|
223
298
|
generator = null,
|
|
224
299
|
extraCacheVariables = null,
|
|
225
300
|
installDir = null,
|
|
301
|
+
profileArch = null,
|
|
302
|
+
configs = null,
|
|
226
303
|
}) {
|
|
227
|
-
const args = { profileName, profileHash, toolchainRelPath, generator, extraCacheVariables, installDir };
|
|
304
|
+
const args = { profileName, profileHash, toolchainRelPath, generator, extraCacheVariables, installDir, profileArch, configs };
|
|
228
305
|
const candidates = [
|
|
229
306
|
{ filePath: path.join(projectRoot, 'CMakePresets.json'), isUser: false },
|
|
230
307
|
{ filePath: path.join(projectRoot, 'CMakeUserPresets.json'), isUser: true },
|
|
@@ -17,6 +17,17 @@ set(WYVRN_PROFILE_NAME "{{PROFILE_NAME}}")
|
|
|
17
17
|
set(WYVRN_PROFILE_HASH "{{PROFILE_HASH}}")
|
|
18
18
|
set(WYVRN_PROFILE_OS "{{PROFILE_OS}}")
|
|
19
19
|
set(WYVRN_PROFILE_ARCH "{{PROFILE_ARCH}}")
|
|
20
|
+
|
|
21
|
+
# "64" for 64-bit arches (x86_64, armv8, …), "32" for 32-bit (x86, armv7, …),
|
|
22
|
+
# empty for arches outside the known mapping. Derived at generation time from
|
|
23
|
+
# the profile so it is valid BEFORE `project()` — unlike CMAKE_SIZEOF_VOID_P,
|
|
24
|
+
# which is only set after the first project() call. Intended for suffixing
|
|
25
|
+
# artefact names (e.g. "String${WYVRN_ARCH_SUFFIX}" → String64 / String32) so
|
|
26
|
+
# x64 and x86 builds can coexist on disk without overwriting each other.
|
|
27
|
+
# Call WYVRN_APPLY_ARCH_SUFFIX(<target>) from macros.cmake for the common
|
|
28
|
+
# per-target shape, or reference the variable directly for other uses.
|
|
29
|
+
set(WYVRN_ARCH_SUFFIX "{{PROFILE_ARCH_SUFFIX}}")
|
|
30
|
+
|
|
20
31
|
set(WYVRN_PROFILE_COMPILER "{{PROFILE_COMPILER}}")
|
|
21
32
|
set(WYVRN_PROFILE_COMPILER_VERSION "{{PROFILE_COMPILER_VERSION}}")
|
|
22
33
|
set(WYVRN_PROFILE_CPPSTD "{{PROFILE_CPPSTD}}")
|
package/src/upload-built.js
CHANGED
|
@@ -7,6 +7,7 @@ const path = require('path');
|
|
|
7
7
|
const { getProvider } = require('./providers');
|
|
8
8
|
const { defaultCompatBlock } = require('./compat');
|
|
9
9
|
const { getBuildPaths } = require('./build/cache');
|
|
10
|
+
const { resolveSourceAuth } = require('./auth');
|
|
10
11
|
const log = require('./logger');
|
|
11
12
|
|
|
12
13
|
const WYVRNPM_VERSION = require('../package.json').version;
|
|
@@ -193,6 +194,7 @@ function resolveUploadDestination(argv, config) {
|
|
|
193
194
|
let src = argv.uploadSource;
|
|
194
195
|
let awsProfile = argv.awsProfile;
|
|
195
196
|
let token = argv.token;
|
|
197
|
+
let sourceEntry = null;
|
|
196
198
|
|
|
197
199
|
const publishSources = config.publishSources ?? [];
|
|
198
200
|
|
|
@@ -202,17 +204,24 @@ function resolveUploadDestination(argv, config) {
|
|
|
202
204
|
if (named) {
|
|
203
205
|
src = named.url;
|
|
204
206
|
awsProfile = awsProfile ?? named.profile;
|
|
205
|
-
|
|
207
|
+
sourceEntry = named;
|
|
206
208
|
log.info(`upload-built: using publish source "${named.name}" from config`);
|
|
207
209
|
}
|
|
208
210
|
} else if (publishSources.length > 0) {
|
|
209
211
|
const first = publishSources[0];
|
|
210
212
|
src = first.url;
|
|
211
213
|
awsProfile = awsProfile ?? first.profile;
|
|
212
|
-
|
|
214
|
+
sourceEntry = first;
|
|
213
215
|
log.info(`upload-built: using first configured publish source "${first.name}"`);
|
|
214
216
|
}
|
|
215
217
|
|
|
218
|
+
// Fall back to the matched source entry's own token / tokenEnv only if
|
|
219
|
+
// the caller didn't pass one already (argv.token wins). resolveSourceAuth
|
|
220
|
+
// handles the literal-vs-env-backed precedence per side.
|
|
221
|
+
if (token === undefined || token === null) {
|
|
222
|
+
token = resolveSourceAuth({}, sourceEntry).token;
|
|
223
|
+
}
|
|
224
|
+
|
|
216
225
|
if (!src) return null;
|
|
217
226
|
return { uploadSource: src, uploadAuth: { awsProfile, token } };
|
|
218
227
|
}
|
package/src/zip-safe.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Defense-in-depth against Zip Slip (CVE-2018-1002200 family). Both
|
|
5
|
+
* node-stream-zip and adm-zip claim to handle this upstream, but we
|
|
6
|
+
* don't want to take the library's word for it on a supply-chain-
|
|
7
|
+
* critical code path. Pre-scan zip entry names before extraction and
|
|
8
|
+
* reject anything that could write outside the target directory.
|
|
9
|
+
*
|
|
10
|
+
* The ZIP spec uses forward slashes only, but Windows-produced archives
|
|
11
|
+
* sometimes leak backslashes — normalise both.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} entryName zip-internal path as reported by the zip library
|
|
14
|
+
* @param {string} [context] optional archive identifier for the error message
|
|
15
|
+
* @throws {Error} if the entry name is absolute, contains a path-traversal
|
|
16
|
+
* segment, or starts with a Windows drive-letter prefix.
|
|
17
|
+
*/
|
|
18
|
+
function assertSafeZipEntryName(entryName, context) {
|
|
19
|
+
const where = context ? ` in ${context}` : '';
|
|
20
|
+
|
|
21
|
+
if (/^[A-Za-z]:[\\/]/.test(entryName)) {
|
|
22
|
+
throw new Error(`unsafe zip entry${where}: drive-letter absolute path ${JSON.stringify(entryName)}`);
|
|
23
|
+
}
|
|
24
|
+
if (entryName.startsWith('/') || entryName.startsWith('\\')) {
|
|
25
|
+
throw new Error(`unsafe zip entry${where}: absolute path ${JSON.stringify(entryName)}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const segments = entryName.split(/[\\/]+/);
|
|
29
|
+
for (const seg of segments) {
|
|
30
|
+
if (seg === '..') {
|
|
31
|
+
throw new Error(`unsafe zip entry${where}: path-traversal segment in ${JSON.stringify(entryName)}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convenience: validate every name in an iterable. Throws on the first
|
|
38
|
+
* offender (fail-fast) rather than collecting all violations.
|
|
39
|
+
*
|
|
40
|
+
* @param {Iterable<string>} names
|
|
41
|
+
* @param {string} [context]
|
|
42
|
+
*/
|
|
43
|
+
function assertAllSafeZipEntryNames(names, context) {
|
|
44
|
+
for (const name of names) {
|
|
45
|
+
assertSafeZipEntryName(name, context);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
assertSafeZipEntryName,
|
|
51
|
+
assertAllSafeZipEntryNames,
|
|
52
|
+
};
|