Skip to content

Commit

Permalink
feat: use hardlinks if possible, do not create empty dirs
Browse files Browse the repository at this point in the history
extra files — dirs always created with 755 permissions

detect a lot of CI — is-ci now is used.

Closes #732
  • Loading branch information
develar committed Nov 28, 2016
1 parent b91c1d1 commit 3f97b86
Show file tree
Hide file tree
Showing 35 changed files with 462 additions and 281 deletions.
2 changes: 1 addition & 1 deletion docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Don't customize paths to background and icon, — just follow conventions.
| files | <a name="BuildMetadata-files"></a><p>A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to include when copying files to create the package.</p> <p>See [File Patterns](#multiple-glob-patterns).</p>
| extraResources | <a name="BuildMetadata-extraResources"></a><p>A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app’s resources directory (<code>Contents/Resources</code> for MacOS, <code>resources</code> for Linux/Windows).</p> <p>Glob rules the same as for [files](#multiple-glob-patterns).</p>
| extraFiles | <a name="BuildMetadata-extraFiles"></a>The same as [extraResources](#BuildMetadata-extraResources) but copy into the app's content directory (`Contents` for MacOS, root directory for Linux/Windows).
| asar | <a name="BuildMetadata-asar"></a><p>Whether to package the application’s source code into an archive, using [Electron’s archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to <code>true</code>. Reasons why you may want to disable this feature are described in [an application packaging tutorial in Electron’s documentation](http://electron.atom.io/docs/tutorial/application-packaging/#limitations-of-the-node-api).</p> <p>Or you can pass object of any asar options.</p> <p>Node modules, that must be unpacked, will be detected automatically, you don’t need to explicitly set <code>asarUnpack</code> - please file issue if this doesn’t work.</p>
| asar | <a name="BuildMetadata-asar"></a><p>Whether to package the application’s source code into an archive, using [Electron’s archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to <code>true</code>. Node modules, that must be unpacked, will be detected automatically, you don’t need to explicitly set [asarUnpack](#BuildMetadata-asarUnpack) - please file issue if this doesn’t work.</p> <p>Or you can pass object of asar options.</p>
| asarUnpack | <a name="BuildMetadata-asarUnpack"></a>A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the [app directory](#MetadataDirectories-app), which specifies which files to unpack when creating the [asar](http://electron.atom.io/docs/tutorial/application-packaging/) archive.
| fileAssociations | <a name="BuildMetadata-fileAssociations"></a>The file associations. See [.build.fileAssociations](#FileAssociation).
| protocols | <a name="BuildMetadata-protocols"></a>The URL protocol scheme(s) to associate the app with. See [.build.protocol](#Protocol).
Expand Down
6 changes: 3 additions & 3 deletions nsis-auto-updater/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "electron-auto-updater",
"version": "0.6.0",
"version": "0.6.2",
"description": "NSIS Auto Updater",
"main": "out/nsis-auto-updater/src/main.js",
"author": "Vladimir Krivosheev",
Expand All @@ -13,8 +13,8 @@
],
"dependencies": {
"bluebird-lst-c": "^1.0.5",
"debug": "^2.3.2",
"fs-extra-p": "^2.0.7",
"debug": "^2.3.3",
"fs-extra-p": "^3.0.2",
"ini": "^1.3.4",
"js-yaml": "^3.7.0",
"semver": "^5.3.0",
Expand Down
37 changes: 30 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"lint": "node ./test/lint.js",
"pretest": "yarn run compile && yarn run lint",
"check-deps": "node ./test/out/helpers/checkDeps.js",
"test": "node ./test/out/helpers/runTests.js",
"test": "node --trace-warnings ./test/out/helpers/runTests.js",
"test-linux": "docker run --rm -ti -v ${PWD}:/project -v ${PWD##*/}-node-modules:/project/node_modules -v ~/.electron:/root/.electron electronuserland/electron-builder:wine /test.sh",
"pack-updater": "cd nsis-auto-updater && yarn --production && cd ..",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
Expand Down Expand Up @@ -70,9 +70,10 @@
"debug": "^2.3.3",
"electron-download-tf": "3.1.0",
"electron-macos-sign": "^1.3.4",
"fs-extra-p": "^2.0.7",
"fs-extra-p": "^3.0.2",
"hosted-git-info": "^2.1.5",
"ini": "^1.3.4",
"is-ci": "^1.0.10",
"isbinaryfile": "^3.0.1",
"js-yaml": "^3.7.0",
"lodash.template": "^4.4.0",
Expand All @@ -89,13 +90,14 @@
"sanitize-filename": "^1.6.1",
"semver": "^5.3.0",
"source-map-support": "^0.4.6",
"stat-mode": "^0.2.2",
"tunnel-agent": "^0.4.3",
"update-notifier": "^1.0.2",
"uuid-1345": "^0.99.6",
"yargs": "^6.4.0"
},
"devDependencies": {
"@develar/semantic-release": "^6.3.21",
"@develar/semantic-release": "^6.3.24",
"@types/ini": "^1.3.29",
"@types/jest": "^16.0.0",
"@types/js-yaml": "^3.5.28",
Expand All @@ -108,13 +110,13 @@
"babel-plugin-transform-inline-imports-commonjs": "^1.2.0",
"decompress-zip": "^0.3.0",
"depcheck": "^0.6.5",
"diff": "^3.0.1",
"diff": "^3.1.0",
"husky": "^0.11.9",
"jest-cli": "^17.0.3",
"json8": "^0.9.2",
"path-sort": "^0.1.0",
"ts-babel": "^1.1.4",
"tslint": "4.0.0-dev.1",
"ts-babel": "^1.1.5",
"tslint": "^4.0.2",
"typescript": "^2.1.1",
"validate-commit-msg": "^2.8.2",
"whitespace": "^2.1.0"
Expand All @@ -140,7 +142,28 @@
]
}
]
]
],
"env": {
"test": {
"plugins": [
[
"transform-async-to-module-method",
{
"module": "bluebird-lst-c",
"method": "coroutine"
}
],
[
"transform-inline-imports-commonjs",
{
"excludeModules": [
"path"
]
}
]
]
}
}
},
"jest": {
"testEnvironment": "node",
Expand Down
80 changes: 38 additions & 42 deletions src/asarUtil.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { AsarFileInfo, listPackage, statFile, AsarOptions } from "asar-electron-builder"
import { debug, isCi } from "./util/util"
import { readFile, Stats, createWriteStream, ensureDir, createReadStream, readJson, writeFile, realpath, link } from "fs-extra-p"
import { debug } from "./util/util"
import { readFile, Stats, createWriteStream, ensureDir, createReadStream, readJson, writeFile, realpath } from "fs-extra-p"
import BluebirdPromise from "bluebird-lst-c"
import * as path from "path"
import { log } from "./util/log"
import { deepAssign } from "./util/deepAssign"
import { Filter } from "./util/filter"
import { walk, statOrNull, CONCURRENCY, MAX_FILE_REQUESTS } from "./util/fs"
import { walk, statOrNull, CONCURRENCY, MAX_FILE_REQUESTS, Filter, FileCopier } from "./util/fs"

const isBinaryFile: any = BluebirdPromise.promisify(require("isbinaryfile"))
const pickle = require ("chromium-pickle-js")
Expand All @@ -31,6 +30,24 @@ function addValue(map: Map<string, Array<string>>, key: string, value: string) {
}
}

interface UnpackedFileTask {
stats: Stats
src?: string
data?: string
destination: string
}

function writeUnpackedFiles(filesToUnpack: Array<UnpackedFileTask>, fileCopier: FileCopier): Promise<any> {
return BluebirdPromise.map(filesToUnpack, it => {
if (it.data == null) {
return fileCopier.copy(it.src!, it.destination, it.stats)
}
else {
return writeFile(it.destination, it.data)
}
})
}

class AsarPackager {
private readonly toPack: Array<string> = []
private readonly fs = new Filesystem(this.src)
Expand All @@ -39,16 +56,13 @@ class AsarPackager {

private srcRealPath: Promise<string>

constructor(private readonly src: string, private readonly resourcesPath: string, private readonly options: AsarOptions, private readonly unpackPattern: Filter | null) {
this.outFile = path.join(this.resourcesPath, "app.asar")
constructor(private readonly src: string, destination: string, private readonly options: AsarOptions, private readonly unpackPattern: Filter | null) {
this.outFile = path.join(destination, "app.asar")
}

async pack(filter: Filter) {
const metadata = new Map<string, Stats>()
const files = await walk(this.src, (it, stat) => {
metadata.set(it, stat)
}, filter)

const files = await walk(this.src, filter, (it, stat) => metadata.set(it, stat))
await this.createPackageFromFiles(this.options.ordering == null ? files : await this.order(files), metadata)
await this.writeAsarFile()
}
Expand Down Expand Up @@ -83,7 +97,7 @@ class AsarPackager {
const nodeModuleDir = file.substring(0, nextSlashIndex)

if (file.length === (nodeModuleDir.length + 1 + packageJsonStringLength) && file.endsWith("package.json")) {
fileIndexToModulePackageData.set(i, readJson(file).then(it => cleanupPackageJson(it)))
fileIndexToModulePackageData.set(i, <BluebirdPromise<string>>readJson(file).then(it => cleanupPackageJson(it)))
}

if (autoUnpackDirs.has(nodeModuleDir)) {
Expand Down Expand Up @@ -147,8 +161,9 @@ class AsarPackager {
await this.detectUnpackedDirs(files, metadata, unpackedDirs, unpackedDest, fileIndexToModulePackageData)
}

const copyPromises: Array<Promise<any>> = []
const filesToUnpack: Array<UnpackedFileTask> = []
const mainPackageJson = path.join(this.src, "package.json")
const fileCopier = new FileCopier()
for (let i = 0, n = files.length; i < n; i++) {
const file = files[i]
const stat = metadata.get(file)!
Expand All @@ -174,14 +189,14 @@ class AsarPackager {

if (!dirNode.unpacked && !unpackedDirs.has(fileParent)) {
unpackedDirs.add(fileParent)
await ensureDir(path.join(unpackedDest, path.relative(this.src, fileParent)))
await ensureDir(fileParent.replace(this.src, unpackedDest))
}

const unpackedFile = path.join(unpackedDest, path.relative(this.src, file))
copyPromises.push(newData == null ? copyFile(file, unpackedFile, stat) : writeFile(unpackedFile, newData))
if (copyPromises.length > MAX_FILE_REQUESTS) {
await BluebirdPromise.all(copyPromises)
copyPromises.length = 0
const unpackedFile = file.replace(this.src, unpackedDest)
filesToUnpack.push(newData == null ? {src: file, destination: unpackedFile, stats: stat} : {destination: unpackedFile, data: newData, stats: stat})
if (filesToUnpack.length > MAX_FILE_REQUESTS) {
await writeUnpackedFiles(filesToUnpack, fileCopier)
filesToUnpack.length = 0
}
}
else {
Expand Down Expand Up @@ -214,7 +229,7 @@ class AsarPackager {
unpackedDirs.add(file)
// not all dirs marked as unpacked after first iteration - because node module dir can be marked as unpacked after processing node module dir content
// e.g. node-notifier/example/advanced.js processed, but only on process vendor/terminal-notifier.app module will be marked as unpacked
await ensureDir(path.join(unpackedDest, path.relative(this.src, file)))
await ensureDir(file.replace(this.src, unpackedDest))
break
}
}
Expand All @@ -226,8 +241,9 @@ class AsarPackager {
}
}

if (copyPromises.length > 0) {
await BluebirdPromise.all(copyPromises)
if (filesToUnpack.length > MAX_FILE_REQUESTS) {
await writeUnpackedFiles(filesToUnpack, fileCopier)
filesToUnpack.length = 0
}
}

Expand Down Expand Up @@ -300,7 +316,7 @@ class AsarPackager {
for (const file of orderingFiles) {
let pathComponents = file.split(path.sep)
let str = this.src
for (let pathComponent of pathComponents) {
for (const pathComponent of pathComponents) {
str = path.join(str, pathComponent)
ordering.push(str)
}
Expand Down Expand Up @@ -378,24 +394,4 @@ export async function checkFileInArchive(asarFile: string, relativeFile: string,
if (stat.size === 0) {
throw error(`is corrupted: size 0`)
}
}

function copyFile(src: string, dest: string, stats: Stats) {
if (process.platform != "win32" && (isCi || process.env.USE_HARD_LINKS === "true")) {
return link(src, dest)
}

return new BluebirdPromise(function (resolve, reject) {
const readStream = createReadStream(src)
const writeStream = createWriteStream(dest, {mode: stats.mode})

readStream.on("error", reject)
writeStream.on("error", reject)

writeStream.on("open", function () {
readStream.pipe(writeStream)
})

writeStream.once("finish", resolve)
})
}
7 changes: 4 additions & 3 deletions src/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import { PublishOptions, Publisher } from "./publish/publisher"
import { GitHubPublisher } from "./publish/gitHubPublisher"
import { executeFinally } from "./util/promise"
import BluebirdPromise from "bluebird-lst-c"
import { isEmptyOrSpaces, isCi, debug } from "./util/util"
import { isEmptyOrSpaces, debug } from "./util/util"
import { log } from "./util/log"
import { Platform, Arch, archFromString } from "./metadata"
import { DIR_TARGET } from "./targets/targetFactory"
import { BintrayPublisher } from "./publish/BintrayPublisher"
import { PublishConfiguration, GithubOptions, BintrayOptions } from "./options/publishOptions"
import isCi from "is-ci"

export interface BuildOptions extends PackagerOptions, PublishOptions {
}
Expand Down Expand Up @@ -209,7 +210,7 @@ export async function build(rawOptions?: CliOptions): Promise<Array<string>> {
options.publish = "onTag"
isPublishOptionGuessed = true
}
else if (isCi()) {
else if (isCi) {
log("CI detected, so artifacts will be published if draft release exists")
options.publish = "onTagOrDraft"
isPublishOptionGuessed = true
Expand All @@ -225,7 +226,7 @@ export async function build(rawOptions?: CliOptions): Promise<Array<string>> {
if (isAuthTokenSet()) {
publishManager(packager, publishTasks, options, isPublishOptionGuessed)
}
else if (isCi()) {
else if (isCi) {
log(`CI detected, publish is set to ${options.publish}, but neither GH_TOKEN nor BT_TOKEN is not set, so artifacts will be not published`)
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/cli/build-cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import * as path from "path"
import { dim, reset, green, cyan } from "chalk"
import updateNotifier from "update-notifier"
import { warn } from "../util/log"
import isCi from "is-ci"

if (process.env.CI == null && process.env.NO_UPDATE_NOTIFIER == null) {
if (!isCi && process.env.NO_UPDATE_NOTIFIER == null) {
readJson(path.join(__dirname, "..", "..", "package.json"))
.then(it => {
const notifier = updateNotifier({pkg: it})
Expand Down
5 changes: 3 additions & 2 deletions src/codeSign.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { exec, getTempName, isEmptyOrSpaces, isCi, getCacheDirectory } from "./util/util"
import { exec, getTempName, isEmptyOrSpaces, getCacheDirectory } from "./util/util"
import { deleteFile, outputFile, copy, rename } from "fs-extra-p"
import { download } from "./util/httpRequest"
import * as path from "path"
Expand All @@ -8,6 +8,7 @@ import { randomBytes } from "crypto"
import { TmpDir } from "./util/tmp"
import { homedir } from "os"
import { statOrNull } from "./util/fs"
import isCi from "is-ci"

export const appleCertificatePrefixes = ["Developer ID Application:", "Developer ID Installer:", "3rd Party Mac Developer Application:", "3rd Party Mac Developer Installer:"]

Expand Down Expand Up @@ -208,7 +209,7 @@ async function _findIdentity(type: CertType, qualifier?: string | null, keychain
export async function findIdentity(certType: CertType, qualifier?: string | null, keychain?: string | null): Promise<string | null> {
let identity = process.env.CSC_NAME || qualifier
if (isEmptyOrSpaces(identity)) {
if (keychain == null && !isCi() && process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
if (keychain == null && !isCi && process.env.CSC_IDENTITY_AUTO_DISCOVERY === "false") {
return null
}
else {
Expand Down
3 changes: 2 additions & 1 deletion src/fileMatcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as path from "path"
import { createFilter, hasMagic, Filter } from "./util/filter"
import { createFilter, hasMagic } from "./util/filter"
import { Minimatch } from "minimatch"
import { asArray } from "./util/util"
import { Filter } from "./util/fs"

export interface FilePattern {
from?: string
Expand Down
14 changes: 6 additions & 8 deletions src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,26 +115,24 @@ export interface BuildMetadata {
*/
readonly files?: Array<string> | string | null

/**
/*
A [glob patterns](https://www.npmjs.com/package/glob#glob-primer) relative to the project directory, when specified, copy the file or directory with matching names directly into the app's resources directory (`Contents/Resources` for MacOS, `resources` for Linux/Windows).
Glob rules the same as for [files](#multiple-glob-patterns).
*/
readonly extraResources?: Array<string> | string | null

/**
/*
The same as [extraResources](#BuildMetadata-extraResources) but copy into the app's content directory (`Contents` for MacOS, root directory for Linux/Windows).
*/
readonly extraFiles?: Array<string> | string | null

/*
Whether to package the application's source code into an archive, using [Electron's archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to `true`.
Reasons why you may want to disable this feature are described in [an application packaging tutorial in Electron's documentation](http://electron.atom.io/docs/tutorial/application-packaging/#limitations-of-the-node-api).
Whether to package the application's source code into an archive, using [Electron's archive format](http://electron.atom.io/docs/tutorial/application-packaging/). Defaults to `true`.
Node modules, that must be unpacked, will be detected automatically, you don't need to explicitly set [asarUnpack](#BuildMetadata-asarUnpack) - please file issue if this doesn't work.
Or you can pass object of any asar options.
Node modules, that must be unpacked, will be detected automatically, you don't need to explicitly set `asarUnpack` - please file issue if this doesn't work.
*/
Or you can pass object of asar options.
*/
readonly asar?: AsarOptions | boolean | null

/**
Expand Down
Loading

0 comments on commit 3f97b86

Please sign in to comment.