Skip to content

Commit c278045

Browse files
committed
node: Add yarn2 support
1 parent 773545e commit c278045

File tree

3 files changed

+516
-20
lines changed

3 files changed

+516
-20
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
const PackageManager = {
2+
Yarn1 : `Yarn Classic`,
3+
Yarn2 : `Yarn`,
4+
Npm : `npm`,
5+
Pnpm : `pnpm`,
6+
}
7+
8+
module.exports = {
9+
name: `flatpak-builder`,
10+
factory: require => {
11+
const { BaseCommand } = require(`@yarnpkg/cli`);
12+
const { Configuration, Manifest, scriptUtils, structUtils, tgzUtils, execUtils, miscUtils, hashUtils } = require('@yarnpkg/core')
13+
const { Filename, ZipFS, npath, ppath, PortablePath, xfs } = require('@yarnpkg/fslib');
14+
const { getLibzipPromise } = require('@yarnpkg/libzip');
15+
const { gitUtils } = require('@yarnpkg/plugin-git');
16+
const { PassThrough, Readable, Writable } = require('stream');
17+
const { Command, Option } = require(`clipanion`);
18+
const { YarnVersion } = require('@yarnpkg/core');
19+
const fs = require('fs');
20+
21+
// from https://github.com/yarnpkg/berry/blob/%40yarnpkg/shell/3.2.3/packages/plugin-essentials/sources/commands/set/version.ts#L194
22+
async function setPackageManager(projectCwd) {
23+
const bundleVersion = YarnVersion;
24+
25+
const manifest = (await Manifest.tryFind(projectCwd)) || new Manifest();
26+
27+
if(bundleVersion && miscUtils.isTaggedYarnVersion(bundleVersion)) {
28+
manifest.packageManager = `yarn@${bundleVersion}`;
29+
const data = {};
30+
manifest.exportTo(data);
31+
32+
const path = ppath.join(projectCwd, Manifest.fileName);
33+
const content = `${JSON.stringify(data, null, manifest.indent)}\n`;
34+
35+
await xfs.changeFilePromise(path, content, {
36+
automaticNewlines: true,
37+
});
38+
}
39+
}
40+
41+
// func from https://github.com/yarnpkg/berry/blob/%40yarnpkg/shell/3.2.3/packages/yarnpkg-core/sources/scriptUtils.ts#L215
42+
async function prepareExternalProject(cwd, outputPath , {configuration, locator, stdout, yarn_v1, workspace=null}) {
43+
const devirtualizedLocator = locator && structUtils.isVirtualLocator(locator)
44+
? structUtils.devirtualizeLocator(locator)
45+
: locator;
46+
47+
const name = devirtualizedLocator
48+
? structUtils.stringifyLocator(devirtualizedLocator)
49+
: `an external project`;
50+
51+
const stderr = stdout;
52+
53+
stdout.write(`Packing ${name} from sources\n`);
54+
55+
const packageManagerSelection = await scriptUtils.detectPackageManager(cwd);
56+
let effectivePackageManager;
57+
if (packageManagerSelection !== null) {
58+
stdout.write(`Using ${packageManagerSelection.packageManager} for bootstrap. Reason: ${packageManagerSelection.reason}\n\n`);
59+
effectivePackageManager = packageManagerSelection.packageManager;
60+
} else {
61+
stdout.write(`No package manager configuration detected; defaulting to Yarn\n\n`);
62+
effectivePackageManager = PackageManager.Yarn2;
63+
}
64+
if (effectivePackageManager === PackageManager.Pnpm) {
65+
effectivePackageManager = PackageManager.Npm;
66+
}
67+
68+
const workflows = new Map([
69+
[PackageManager.Yarn1, async () => {
70+
const workspaceCli = workspace !== null
71+
? [`workspace`, workspace]
72+
: [];
73+
74+
await setPackageManager(cwd);
75+
76+
await Configuration.updateConfiguration(cwd, {
77+
yarnPath: yarn_v1,
78+
});
79+
80+
await xfs.appendFilePromise(ppath.join(cwd, `.npmignore`), `/.yarn\n`);
81+
82+
const pack = await execUtils.pipevp(`yarn`, [...workspaceCli, `pack`, `--filename`, npath.fromPortablePath(outputPath)], {cwd, stdout, stderr});
83+
if (pack.code !== 0)
84+
return pack.code;
85+
86+
return 0;
87+
}],
88+
[PackageManager.Yarn2, async () => {
89+
const workspaceCli = workspace !== null
90+
? [`workspace`, workspace]
91+
: [];
92+
const lockfilePath = ppath.join(cwd, Filename.lockfile);
93+
if (!(await xfs.existsPromise(lockfilePath)))
94+
await xfs.writeFilePromise(lockfilePath, ``);
95+
96+
const pack = await execUtils.pipevp(`yarn`, [...workspaceCli, `pack`, `--filename`, npath.fromPortablePath(outputPath)], {cwd, stdout, stderr});
97+
if (pack.code !== 0)
98+
return pack.code;
99+
return 0;
100+
}],
101+
[PackageManager.Npm, async () => {
102+
const workspaceCli = workspace !== null
103+
? [`--workspace`, workspace]
104+
: [];
105+
const packStream = new PassThrough();
106+
const packPromise = miscUtils.bufferStream(packStream);
107+
const pack = await execUtils.pipevp(`npm`, [`pack`, `--silent`, ...workspaceCli], {cwd, stdout: packStream, stderr});
108+
if (pack.code !== 0)
109+
return pack.code;
110+
111+
const packOutput = (await packPromise).toString().trim().replace(/^.*\n/s, ``);
112+
const packTarget = ppath.resolve(cwd, npath.toPortablePath(packOutput));
113+
await xfs.renamePromise(packTarget, outputPath);
114+
return 0;
115+
}],
116+
]);
117+
const workflow = workflows.get(effectivePackageManager);
118+
const code = await workflow();
119+
if (code === 0 || typeof code === `undefined`)
120+
return;
121+
else
122+
throw `Packing the package failed (exit code ${code})`;
123+
}
124+
125+
class convertToZipCommand extends BaseCommand {
126+
static paths = [[`convertToZip`]];
127+
yarn_v1 = Option.String({required: true});
128+
129+
async execute() {
130+
const configuration = await Configuration.find(this.context.cwd,
131+
this.context.plugins);
132+
//const lockfile = configuration.get('lockfileFilename');
133+
const cacheFolder = configuration.get('cacheFolder');
134+
const locatorFolder = `${cacheFolder}/locator`;
135+
136+
const compressionLevel = configuration.get(`compressionLevel`);
137+
const stdout = this.context.stdout;
138+
const gitChecksumPatches = []; // {name:, oriHash:, newHash:}
139+
140+
async function patchLockfileChecksum(cwd, configuration, patches) {
141+
const lockfilePath = ppath.join(cwd, configuration.get(`lockfileFilename`));
142+
143+
let currentContent = ``;
144+
try {
145+
currentContent = await xfs.readFilePromise(lockfilePath, `utf8`);
146+
} catch (error) {
147+
}
148+
const newContent = patches.reduce((acc, item, i) => {
149+
stdout.write(`patch '${item.name}' checksum:\n-${item.oriHash}\n+${item.newHash}\n\n\n`);
150+
const regex = new RegExp(item.oriHash, "g");
151+
return acc.replace(regex, item.newHash);
152+
}, currentContent);
153+
154+
await xfs.writeFilePromise(lockfilePath, newContent);
155+
}
156+
157+
158+
stdout.write(`yarn cacheFolder: ${cacheFolder}\n`);
159+
160+
const convertToZip = async (tgz, target, opts) => {
161+
const { compressionLevel, ...bufferOpts } = opts;
162+
const zipFs = new ZipFS(target, {
163+
create: true,
164+
libzip: await getLibzipPromise(),
165+
level: compressionLevel
166+
});
167+
const tgzBuffer = fs.readFileSync(tgz);
168+
await tgzUtils.extractArchiveTo(tgzBuffer, zipFs,
169+
bufferOpts);
170+
zipFs.saveAndClose();
171+
}
172+
173+
stdout.write(`converting cache to zip\n`);
174+
175+
const files = fs.readdirSync(locatorFolder);
176+
const tasks = []
177+
for (const i in files) {
178+
const file = `${files[i]}`;
179+
let tgzFile = `${locatorFolder}/${file}`;
180+
const match = file.match(/([^-]+)-([^\.]{1,10}).(tgz|git)/);
181+
if (!match) {
182+
stdout.write(`ignore ${file}\n`);
183+
continue;
184+
}
185+
let resolution, locator;
186+
const entry_type = match[3];
187+
const sha = match[2];
188+
let checksum;
189+
190+
if(entry_type === 'tgz') {
191+
resolution = Buffer.from(match[1], 'base64').toString();
192+
locator = structUtils.parseLocator(resolution, true);
193+
}
194+
else if(entry_type === 'git') {
195+
const gitJson = JSON.parse(fs.readFileSync(tgzFile, 'utf8'));
196+
197+
resolution = gitJson.resolution;
198+
locator = structUtils.parseLocator(resolution, true);
199+
checksum = gitJson.checksum;
200+
201+
const repoPathRel = gitJson.repo_dir_rel;
202+
203+
const cloneTarget = `${cacheFolder}/${repoPathRel}`;
204+
205+
const repoUrlParts = gitUtils.splitRepoUrl(locator.reference);
206+
const packagePath = ppath.join(cloneTarget, `package.tgz`);
207+
208+
await prepareExternalProject(cloneTarget, packagePath, {
209+
configuration: configuration,
210+
stdout,
211+
workspace: repoUrlParts.extra.workspace,
212+
locator,
213+
yarn_v1: this.yarn_v1,
214+
});
215+
216+
tgzFile = packagePath;
217+
218+
}
219+
const filename =
220+
`${structUtils.slugifyLocator(locator)}-${sha}.zip`;
221+
const targetFile = `${cacheFolder}/${filename}`
222+
tasks.push(async () => {
223+
await convertToZip(tgzFile, targetFile, {
224+
compressionLevel: compressionLevel,
225+
prefixPath: `node_modules/${structUtils.stringifyIdent(locator)}`,
226+
stripComponents: 1,
227+
});
228+
229+
if (entry_type === 'git') {
230+
const file_checksum = await hashUtils.checksumFile(targetFile);
231+
232+
if (file_checksum !== checksum) {
233+
const newSha = file_checksum.slice(0, 10);
234+
const newTarget = `${cacheFolder}/${structUtils.slugifyLocator(locator)}-${newSha}.zip`;
235+
fs.renameSync(targetFile, newTarget);
236+
237+
gitChecksumPatches.push({
238+
name: locator.name,
239+
oriHash: checksum,
240+
newHash: file_checksum,
241+
});
242+
}
243+
}
244+
});
245+
}
246+
247+
while (tasks.length) {
248+
await Promise.all(tasks.splice(0, 128).map(t => t()));
249+
}
250+
251+
patchLockfileChecksum(this.context.cwd, configuration, gitChecksumPatches);
252+
stdout.write(`converting finished\n`);
253+
}
254+
}
255+
return {
256+
commands: [
257+
convertToZipCommand
258+
],
259+
};
260+
}
261+
};
262+

node/flatpak_node_generator/manifest.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,18 @@ def add_data_source(self, data: Union[str, bytes], destination: Path) -> None:
162162
self._add_source_with_destination(source, destination, is_dir=False)
163163

164164
def add_git_source(
165-
self, url: str, commit: str, destination: Optional[Path] = None
165+
self,
166+
url: str,
167+
commit: Optional[str] = None,
168+
destination: Optional[Path] = None,
169+
tag: Optional[str] = None,
166170
) -> None:
167-
source = {'type': 'git', 'url': url, 'commit': commit}
171+
source = {'type': 'git', 'url': url}
172+
assert commit or tag
173+
if commit:
174+
source['commit'] = commit
175+
if tag:
176+
source['tag'] = tag
168177
self._add_source_with_destination(source, destination, is_dir=True)
169178

170179
def add_script_source(self, commands: List[str], destination: Path) -> None:

0 commit comments

Comments
 (0)