Merge branch 'master' into electron-3.1

This commit is contained in:
Antonio Scandurra
2019-05-29 18:35:01 +02:00
26 changed files with 1701 additions and 408 deletions

67
package-lock.json generated
View File

@@ -1722,16 +1722,16 @@
}
},
"atom-keymap": {
"version": "8.2.13",
"resolved": "https://registry.npmjs.org/atom-keymap/-/atom-keymap-8.2.13.tgz",
"integrity": "sha512-RNf+5KbAiXpNV2KZT0+XYpTRFE8rhq7NrBryghJAOlwayY3g3z6Kp9tMfaPJ05BkPo9mChcaFO6SKUL8LTQcBg==",
"version": "8.2.14",
"resolved": "https://registry.npmjs.org/atom-keymap/-/atom-keymap-8.2.14.tgz",
"integrity": "sha512-9ofjA8IG/RNJcqvMvYglc0l7DljavIUQvGs5xdEtd5dEYX4rCQo9coeBfGaC0YM7FB0SBHPZy39QYFROkOzTOw==",
"requires": {
"clear-cut": "^2",
"emissary": "^1.1.0",
"event-kit": "^1.0.0",
"fs-plus": "^3.0.0",
"grim": "^1.2.1",
"keyboard-layout": "2.0.14",
"keyboard-layout": "2.0.16",
"pathwatcher": "^8.0.0",
"property-accessors": "^1",
"season": "^6.0.2"
@@ -3300,8 +3300,8 @@
}
},
"find-and-replace": {
"version": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.10/tarball",
"integrity": "sha512-P9pFWPlUIUO+COMR6VdTQa7Q4JUcrSS8NALq5Pc0RI8scoMoLnZ5RSReKgYp5Gnc7kshPPa09agw3GitLFmorw==",
"version": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.11/tarball",
"integrity": "sha512-STNDeRCSXCV5YjfrWbS5X4TfOFnNX7Z1k96POk3uPoPN8aQvkAIUflT7h6p0MClR9E2/I2O7nYKbYzNqBflZ6Q==",
"requires": {
"binary-search": "^1.3.3",
"element-resize-detector": "^1.1.10",
@@ -4345,12 +4345,19 @@
}
},
"keyboard-layout": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.14.tgz",
"integrity": "sha512-QuCfpEC8oai6F8oaNQdxi5+1QIpaQu9HSVI9yzkC2HbIXeBnahzHFDRVGUtwwAWiNnzjNBjUI/djsrMGUTgK1w==",
"version": "2.0.16",
"resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.16.tgz",
"integrity": "sha512-eGrxmlV6jbm/mbPEOpYGuH53XEC7wIUj9ZxKcT2z9QHJ/RwrT9iVkvxka9zRxqHZHwQzcffgsa5OxoVAKnhK9w==",
"requires": {
"event-kit": "^2.0.0",
"nan": "^2.10.0"
"nan": "^2.13.2"
},
"dependencies": {
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
}
}
},
"keytar": {
@@ -4446,10 +4453,10 @@
}
},
"language-json": {
"version": "https://www.atom.io/api/packages/language-json/versions/1.0.1/tarball",
"integrity": "sha512-mgUFtXCqseXW6bi4oIub1c+lh7qqXN9o8kjf+ljJe8yB8JYSZeRAm5zhqvrrUZO6WDQv4lplq2bJP8vMiKtaxg==",
"version": "https://www.atom.io/api/packages/language-json/versions/1.0.2/tarball",
"integrity": "sha512-2OvmYoTjO5IXnnRMVMfPb7iOMEnqD36otbpOpQUELG4eJJqgrms6Hs7HnevFs5ZB4yLc1ZU2u9h9TAp/WXUmdA==",
"requires": {
"tree-sitter-json": "^0.13.1"
"tree-sitter-json": "git://github.com/tree-sitter/tree-sitter-json.git#337f55be9b9b1ccb0baa7763bfe014a94acea7ea"
}
},
"language-less": {
@@ -4465,8 +4472,8 @@
"integrity": "sha512-1aC1OAoYye+krEJ8t5RzXiLYTEA/RJ/Igv1efDsuxvZHnIkdrSDzS/UsssS3snqPkIGyLI+htRvU/v11famx6A=="
},
"language-objective-c": {
"version": "https://www.atom.io/api/packages/language-objective-c/versions/0.15.1/tarball",
"integrity": "sha512-ZKlTy/xiyb+J7DnHztzM/ss8/rtwbPskSpd+Ox1gKc0k+NpiU7rmzfW6ki9/t/kFHGo1qX7QiImvdCavJ2LsgQ=="
"version": "https://www.atom.io/api/packages/language-objective-c/versions/0.16.0/tarball",
"integrity": "sha512-KFkmXxNuTL2zwL8mfIF9PovRaWUOu/rWPp/fDjSgXPgClXUWeJdZQystXODr6u7kvGYEAdmjYFj/zQu7f/P85Q=="
},
"language-perl": {
"version": "https://www.atom.io/api/packages/language-perl/versions/0.38.1/tarball",
@@ -4481,8 +4488,8 @@
"integrity": "sha512-HD6HI41u57i0/Tu9catiriURhJsef0RDrzJDkGDtdFkE9F9KPxC9Fayq2JBLJrhIyADRVXFxwxsfwQ2Jmh6hxg=="
},
"language-python": {
"version": "https://www.atom.io/api/packages/language-python/versions/0.53.1/tarball",
"integrity": "sha512-yu2DU+Lqy0dzyMwzBIa6Oz6yvQiOeqwwnXl8Gbw4CasnTiAnEW9fk0wFOsUZOs1veqkZqP6Mdd5Zu7n5p1gLcw==",
"version": "https://www.atom.io/api/packages/language-python/versions/0.53.2/tarball",
"integrity": "sha512-ACNHWQWlRUfWrOb5MTvjP2wMTFdAq8Wnd3tWXYEd/TcfECtmCGy+6h33dt9X6SmAZz6OGKQ7V8lnVbOlh3X+Fw==",
"requires": {
"atom-grammar-test": "^0.6.4",
"tree-sitter-python": "^0.14.0"
@@ -6965,15 +6972,15 @@
}
},
"text-buffer": {
"version": "13.15.3",
"resolved": "https://registry.npmjs.org/text-buffer/-/text-buffer-13.15.3.tgz",
"integrity": "sha512-H2fz/N15g0fBP7R33FUFLnIyND+Lji/xmuvHg9rKgmfCh7NAVxiFIvnZTabuBhL9InqPrtV5t4hkUy+r3dNXMg==",
"version": "13.16.0",
"resolved": "https://registry.npmjs.org/text-buffer/-/text-buffer-13.16.0.tgz",
"integrity": "sha512-J00KcJDKvV87I/4o7F6LYu+2/fzmuEb7liBbZsIeCUM+T0kwqW7k0R7ddyk9EJR2Nqq7asng2/hVIBNYldIfEg==",
"requires": {
"delegato": "^1.0.0",
"diff": "^2.2.1",
"emissary": "^1.0.0",
"event-kit": "^2.4.0",
"fs-admin": "^0.1.7",
"fs-admin": "^0.5.0",
"fs-plus": "^3.0.0",
"grim": "^2.0.2",
"mkdirp": "^0.5.1",
@@ -6988,6 +6995,14 @@
"resolved": "https://registry.npmjs.org/diff/-/diff-2.2.3.tgz",
"integrity": "sha1-YOr9DSjukG5Oj/ClLBIpUhAzv5k="
},
"fs-admin": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/fs-admin/-/fs-admin-0.5.0.tgz",
"integrity": "sha512-jU0x86bI6wmhdGGcpaO1rI7EpNx/44cEXPsHqFIRgs9SVsk3HSWn9Zd5fd7bdDw3LcmdnazOcJFK9PZsoNecAA==",
"requires": {
"nan": "^2.13.2"
}
},
"grim": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/grim/-/grim-2.0.2.tgz",
@@ -6995,6 +7010,11 @@
"requires": {
"event-kit": "^2.0.0"
}
},
"nan": {
"version": "2.14.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
"integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
}
}
},
@@ -7150,9 +7170,8 @@
}
},
"tree-sitter-json": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/tree-sitter-json/-/tree-sitter-json-0.13.1.tgz",
"integrity": "sha512-3Z6CC5vaEX+vvCmZ5ULCYVaILQVqkhv1Yw7Wo7ARlVsOAehVEj5JCag+lyU4RGkZY65tgYyrkw5s0Anv4ij4kQ==",
"version": "git://github.com/tree-sitter/tree-sitter-json.git#337f55be9b9b1ccb0baa7763bfe014a94acea7ea",
"from": "git://github.com/tree-sitter/tree-sitter-json.git#v0.14.0",
"requires": {
"nan": "^2.0.0"
}

View File

@@ -22,7 +22,7 @@
"async": "0.2.6",
"atom-dark-syntax": "file:packages/atom-dark-syntax",
"atom-dark-ui": "file:packages/atom-dark-ui",
"atom-keymap": "8.2.13",
"atom-keymap": "8.2.14",
"atom-light-syntax": "file:packages/atom-light-syntax",
"atom-light-ui": "file:packages/atom-light-ui",
"atom-select-list": "^0.7.2",
@@ -54,7 +54,7 @@
"etch": "^0.12.6",
"event-kit": "^2.5.3",
"exception-reporting": "file:packages/exception-reporting",
"find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.10/tarball",
"find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.218.11/tarball",
"find-parent-dir": "^0.3.0",
"first-mate": "7.3.0",
"focus-trap": "2.4.5",
@@ -89,15 +89,15 @@
"language-hyperlink": "https://www.atom.io/api/packages/language-hyperlink/versions/0.17.1/tarball",
"language-java": "https://www.atom.io/api/packages/language-java/versions/0.31.3/tarball",
"language-javascript": "https://www.atom.io/api/packages/language-javascript/versions/0.130.0/tarball",
"language-json": "https://www.atom.io/api/packages/language-json/versions/1.0.1/tarball",
"language-json": "https://www.atom.io/api/packages/language-json/versions/1.0.2/tarball",
"language-less": "https://www.atom.io/api/packages/language-less/versions/0.34.3/tarball",
"language-make": "https://www.atom.io/api/packages/language-make/versions/0.23.0/tarball",
"language-mustache": "https://www.atom.io/api/packages/language-mustache/versions/0.14.5/tarball",
"language-objective-c": "https://www.atom.io/api/packages/language-objective-c/versions/0.15.1/tarball",
"language-objective-c": "https://www.atom.io/api/packages/language-objective-c/versions/0.16.0/tarball",
"language-perl": "https://www.atom.io/api/packages/language-perl/versions/0.38.1/tarball",
"language-php": "https://www.atom.io/api/packages/language-php/versions/0.44.1/tarball",
"language-property-list": "https://www.atom.io/api/packages/language-property-list/versions/0.9.1/tarball",
"language-python": "https://www.atom.io/api/packages/language-python/versions/0.53.1/tarball",
"language-python": "https://www.atom.io/api/packages/language-python/versions/0.53.2/tarball",
"language-ruby": "https://www.atom.io/api/packages/language-ruby/versions/0.72.16/tarball",
"language-ruby-on-rails": "https://www.atom.io/api/packages/language-ruby-on-rails/versions/0.25.3/tarball",
"language-rust-bundled": "file:packages/language-rust-bundled",
@@ -155,13 +155,14 @@
"symbols-view": "https://www.atom.io/api/packages/symbols-view/versions/0.118.2/tarball",
"tabs": "https://www.atom.io/api/packages/tabs/versions/0.110.0/tarball",
"temp": "^0.9.0",
"text-buffer": "13.15.3",
"text-buffer": "13.16.0",
"timecop": "https://www.atom.io/api/packages/timecop/versions/0.36.2/tarball",
"tree-sitter": "0.15.0",
"tree-sitter-css": "^0.13.7",
"tree-view": "https://www.atom.io/api/packages/tree-view/versions/0.228.0/tarball",
"typescript-simple": "1.0.0",
"update-package-dependencies": "https://www.atom.io/api/packages/update-package-dependencies/versions/0.13.1/tarball",
"vscode-ripgrep": "^1.2.5",
"welcome": "https://www.atom.io/api/packages/welcome/versions/0.36.9/tarball",
"whitespace": "https://www.atom.io/api/packages/whitespace/versions/0.37.7/tarball",
"winreg": "^1.2.1",
@@ -199,7 +200,7 @@
"dev-live-reload": "file:./packages/dev-live-reload",
"encoding-selector": "0.23.9",
"exception-reporting": "file:./packages/exception-reporting",
"find-and-replace": "0.218.10",
"find-and-replace": "0.218.11",
"fuzzy-finder": "1.13.3",
"github": "0.29.0",
"git-diff": "file:./packages/git-diff",
@@ -240,15 +241,15 @@
"language-hyperlink": "0.17.1",
"language-java": "0.31.3",
"language-javascript": "0.130.0",
"language-json": "1.0.1",
"language-json": "1.0.2",
"language-less": "0.34.3",
"language-make": "0.23.0",
"language-mustache": "0.14.5",
"language-objective-c": "0.15.1",
"language-objective-c": "0.16.0",
"language-perl": "0.38.1",
"language-php": "0.44.1",
"language-property-list": "0.9.1",
"language-python": "0.53.1",
"language-python": "0.53.2",
"language-ruby": "0.72.16",
"language-ruby-on-rails": "0.25.3",
"language-rust-bundled": "file:./packages/language-rust-bundled",

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
<policyconfig>
<vendor>Atom</vendor>
<action id="atom.pkexec.dd">
<description gettext-domain="atom">Admin privileges required</description>
<message gettext-domain="atom">Please enter your password to save this file</message>
<annotate key="org.freedesktop.policykit.exec.path">/bin/dd</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
<defaults>
<allow_any>auth_admin_keep</allow_any>
<allow_inactive>auth_admin_keep</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
</action>
</policyconfig>

View File

@@ -1,6 +1,6 @@
Package: <%= appFileName %>
Version: <%= version %>
Depends: git, gconf2, gconf-service, libgtk-3-0 (>= 3.9.10), libudev0 | libudev1, libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3 (>= 2:3.22), python, gvfs-bin, xdg-utils, libcap2, libx11-xcb1, libxss1, libasound2 (>= 1.0.16), libxkbfile1, libcurl3 | libcurl4
Depends: git, libgconf-2-4 (>= 3.2.5) | libgconf2-4, libgtk-3-0 (>= 3.9.10), libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3 (>= 2:3.22), python, gvfs-bin, xdg-utils, libx11-xcb1, libxss1, libasound2 (>= 1.0.16), libxkbfile1, libcurl3 | libcurl4, policykit-1
Recommends: lsb-release
Suggests: libsecret-1-0, gir1.2-gnomekeyring-1.0
Section: devel

View File

@@ -8,9 +8,9 @@ AutoReqProv: no # Avoid libchromiumcontent.so missing dependency
Prefix: <%= installDir %>
%ifarch i386 i486 i586 i686
Requires: lsb-core-noarch, libXss.so.1 libsecret-1.so.0
Requires: lsb-core-noarch, libXss.so.1 libsecret-1.so.0, polkit
%else
Requires: lsb-core-noarch, libXss.so.1()(64bit) libsecret-1.so.0()(64bit)
Requires: lsb-core-noarch, libXss.so.1()(64bit) libsecret-1.so.0()(64bit), polkit
%endif
%description
@@ -25,6 +25,8 @@ cp atom.sh "%{buildroot}/<%= installDir %>/bin/<%= appFileName %>"
chmod 755 "%{buildroot}/<%= installDir %>/bin/<%= appFileName %>"
mkdir -p "%{buildroot}/<%= installDir %>/share/applications/"
cp "<%= appFileName %>.desktop" "%{buildroot}/<%= installDir %>/share/applications/"
mkdir -p "%{buildroot}/<%= installDir %>/share/polkit-1/actions/"
cp "atom.policy" "%{buildroot}/<%= installDir %>/share/polkit-1/actions/atom.policy"
mkdir -p "%{buildroot}/<%= installDir %>/share/icons/hicolor/1024x1024/apps"
cp "icons/1024.png" "%{buildroot}/<%= installDir %>/share/icons/hicolor/1024x1024/apps/<%= appFileName %>.png"
@@ -50,4 +52,5 @@ cp "icons/16.png" "%{buildroot}/<%= installDir %>/share/icons/hicolor/16x16/apps
<%= installDir %>/bin/<%= apmFileName %>
<%= installDir %>/share/<%= appFileName %>/
<%= installDir %>/share/applications/<%= appFileName %>.desktop
<%= installDir %>/share/polkit-1/actions/atom.policy
<%= installDir %>/share/icons/hicolor/

View File

@@ -21,6 +21,7 @@ const argv = yargs
.help('help')
.describe('existing-binaries', 'Use existing Atom binaries (skip clean/transpile/cache)')
.describe('code-sign', 'Code-sign executables (macOS and Windows only)')
.describe('test-sign', 'Test-sign executables (macOS only)')
.describe('create-windows-installer', 'Create installer (Windows only)')
.describe('create-debian-package', 'Create .deb package (Linux only)')
.describe('create-rpm-package', 'Create .rpm package (Linux only)')
@@ -49,6 +50,7 @@ const generateStartupSnapshot = require('./lib/generate-startup-snapshot')
const installApplication = require('./lib/install-application')
const packageApplication = require('./lib/package-application')
const prebuildLessCache = require('./lib/prebuild-less-cache')
const testSignOnMac = require('./lib/test-sign-on-mac')
const transpileBabelPaths = require('./lib/transpile-babel-paths')
const transpileCoffeeScriptPaths = require('./lib/transpile-coffee-script-paths')
const transpileCsonPaths = require('./lib/transpile-cson-paths')
@@ -92,12 +94,18 @@ if (!argv.generateApiDocs) {
case 'darwin': {
if (argv.codeSign) {
codeSignOnMac(packagedAppPath)
} else if (argv.testSign) {
testSignOnMac(packagedAppPath)
} else {
console.log('Skipping code-signing. Specify the --code-sign option to perform code-signing'.gray)
}
break
}
case 'win32': {
if (argv.testSign) {
console.log('Test signing is not supported on Windows, skipping.'.gray)
}
if (argv.codeSign) {
const executablesToSign = [ path.join(packagedAppPath, 'Atom.exe') ]
if (argv.createWindowsInstaller) {

View File

@@ -113,6 +113,12 @@ module.exports = function (packagedAppPath) {
path.join(debianPackageLintianOverridesDirPath, atomExecutableName)
)
console.log(`Copying polkit configuration into "${debianPackageShareDirPath}"`)
fs.copySync(
path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.policy'),
path.join(debianPackageShareDirPath, 'polkit-1', 'actions', 'atom.policy')
)
console.log(`Generating .deb file from ${debianPackageDirPath}`)
spawnSync('fakeroot', ['dpkg-deb', '-b', debianPackageDirPath], {stdio: 'inherit'})

View File

@@ -76,6 +76,12 @@ module.exports = function (packagedAppPath) {
path.join(rpmPackageBuildDirPath, 'atom.sh')
)
console.log(`Copying atom.policy into "${rpmPackageBuildDirPath}"`)
fs.copySync(
path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.policy'),
path.join(rpmPackageBuildDirPath, 'atom.policy')
)
console.log(`Generating .rpm package from "${rpmPackageDirPath}"`)
spawnSync('rpmbuild', ['-ba', '--clean', rpmPackageSpecFilePath])
for (let generatedArch of fs.readdirSync(rpmPackageRpmsDirPath)) {

View File

@@ -9,6 +9,7 @@ const CONFIG = require('../config')
module.exports = (packagedAppPath) => {
const archSuffix = process.arch === 'ia32' ? '' : '-' + process.arch
const updateUrlPrefix = process.env.ATOM_UPDATE_URL_PREFIX || 'https://atom.io'
const options = {
appDirectory: packagedAppPath,
authors: 'GitHub Inc.',
@@ -17,7 +18,7 @@ module.exports = (packagedAppPath) => {
outputDirectory: CONFIG.buildOutputPath,
noMsi: true,
noDelta: CONFIG.channel === 'nightly', // Delta packages are broken for nightly versions past nightly9 due to Squirrel/NuGet limitations
remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.computedAppVersion}`,
remoteReleases: `${updateUrlPrefix}/api/updates${archSuffix}?version=${CONFIG.computedAppVersion}`,
setupExe: `AtomSetup${process.arch === 'x64' ? '-x64' : ''}.exe`,
setupIcon: path.join(CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'atom.ico')
}

View File

@@ -69,6 +69,7 @@ module.exports = function (packagedAppPath) {
requiredModuleRelativePath === path.join('..', 'node_modules', 'yauzl', 'index.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', 'winreg', 'lib', 'registry.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', '@atom', 'fuzzy-native', 'lib', 'main.js') ||
requiredModuleRelativePath === path.join('..', 'node_modules', 'vscode-ripgrep', 'lib', 'index.js') ||
// The startup-time script is used by both the renderer and the main process and having it in the
// snapshot causes issues.
requiredModuleRelativePath === path.join('..', 'src', 'startup-time.js')

View File

@@ -0,0 +1,23 @@
const spawnSync = require('./spawn-sync')
module.exports = function (packagedAppPath) {
const result =
spawnSync('security', [
'find-certificate', '-c', 'Mac Developer'
])
const certMatch = (result.stdout || '').toString().match(/\"(Mac Developer.*\))\"/)
if (!certMatch || !certMatch[1]) {
console.error('A "Mac Developer" certificate must be configured to perform test signing')
} else {
// This code-signs the application with a local certificate which won't be
// useful anywhere else but the current machine
// See this issue for more details: https://github.com/electron/electron/issues/7476#issuecomment-356084754
console.log(`Found development certificate '${certMatch[1]}'`)
console.log(`Test-signing application at ${packagedAppPath}`)
spawnSync('codesign', [
'--deep', '--force', '--verbose',
'--sign', certMatch[1], packagedAppPath
], {stdio: 'inherit'})
}
}

View File

@@ -0,0 +1,61 @@
# Atom Update Test Server
This folder contains a simple implementation of Atom's update server to be used for testing the update process with local builds.
## Prerequisites
On macOS, you will need to configure a "Mac Development" certificate for your local machine so that the `script/build --test-sign` parameter will work. Here are the steps to set one up:
1. Install Xcode if it isn't already
1. Launch Xcode and open the Preferences dialog (<kbd>Cmd + ,</kbd>)
1. Switch to the Accounts tab
1. If you don't already see your Apple account in the leftmost column, click the `+` button at the bottom left of the window, select "Apple ID" and then click Continue. Sign in with your Apple account and then you'll be sent back to the Accounts tab.
1. Click the "Manage Certificates..." button in the lower right of the Accounts page
1. Click the `+` button in the lower left of the Signing Certificates popup and then select "Mac Development"
1. A new certificate should now be in the list of the Signing Certificates window with the name of your macOS machine. Click "Done"
1. In a Terminal, verify that your Mac Development certificate is set up by running
```
security find-certificate -c 'Mac Developer'
```
If it returns a lot of information with "Mac Developer: your@apple-id-email.com" inside of it, your certificate is configured correctly and you're now ready to run an Atom build with the `--test-sign` parameter.
## How to use it
1. Since you probably want to try upgrading an installed Atom release to a newer version, start your shell and set the `ATOM_RELEASE_VERSION` environment var to the version that you want the server to advertise as the latest version:
**Windows**
```
set ATOM_RELEASE_VERSION="1.32.0-beta1"
```
**macOS**
```
export ATOM_RELEASE_VERSION="1.32.0-beta1"
```
2. Run a full build of Atom such that the necessary release artifacts are in the `out` folder:
**Windows**
```
script/build --create-windows-installer
```
**macOS**
```
script/build --compress-artifacts --test-sign
```
3. Start up the server in this folder:
```
npm install
npm start
```
**NOTE:** You can customize the port by setting the `PORT` environment variable.
4. Start Atom from the command line with the `ATOM_UPDATE_URL_PREFIX` environment variable set to `http://localhost:3456` (change this to reflect any `PORT` override you might have used)
5. Open the About page and try to update Atom. The update server will write output to the console when requests are received.

378
script/update-server/package-lock.json generated Normal file
View File

@@ -0,0 +1,378 @@
{
"name": "atom-test-update-server",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"body-parser": {
"version": "1.18.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.1",
"http-errors": "~1.6.2",
"iconv-lite": "0.4.19",
"on-finished": "~2.3.0",
"qs": "6.5.1",
"raw-body": "2.3.2",
"type-is": "~1.6.15"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"colors": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz",
"integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ=="
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.16.3",
"resolved": "http://registry.npmjs.org/express/-/express-4.16.3.tgz",
"integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
"requires": {
"accepts": "~1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.2",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.3",
"qs": "6.5.1",
"range-parser": "~1.2.0",
"safe-buffer": "5.1.1",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"http-errors": {
"version": "1.6.3",
"resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"iconv-lite": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.36.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz",
"integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw=="
},
"mime-types": {
"version": "2.1.20",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
"integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
"requires": {
"mime-db": "~1.36.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
"integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.8.0"
}
},
"qs": {
"version": "6.5.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
},
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"raw-body": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
"integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.2",
"iconv-lite": "0.4.19",
"unpipe": "1.0.0"
},
"dependencies": {
"depd": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
"requires": {
"depd": "1.1.1",
"inherits": "2.0.3",
"setprototypeof": "1.0.3",
"statuses": ">= 1.3.1 < 2"
}
},
"setprototypeof": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
"integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
}
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
}
},
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"send": "0.16.2"
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.18"
}
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "atom-test-update-server",
"version": "0.1.0",
"private": true,
"description": "A test update server that replicates the one on atom.io",
"main": "run-server.js",
"scripts": {
"start": "node run-server.js"
},
"author": "David Wilson",
"dependencies": {
"colors": "^1.3.2",
"express": "^4.16.3"
}
}

View File

@@ -0,0 +1,98 @@
require('colors')
const fs = require('fs')
const path = require('path')
const express = require('express')
const app = express()
const port = process.env.PORT || 3456
// Load the metadata for the local build of Atom
const buildPath = path.resolve(__dirname, '..', '..', 'out')
const packageJsonPath = path.join(buildPath, 'app', 'package.json')
if (!fs.existsSync(buildPath) || !fs.existsSync(packageJsonPath)) {
console.log(`This script requires a full Atom build with release packages for the current platform in the following path:\n ${buildPath}\n`)
if (process.platform === 'darwin') {
console.log(`Run this command before trying again:\n script/build --compress-artifacts --test-sign\n\n`)
} else if (process.platform === 'win32') {
console.log(`Run this command before trying again:\n script/build --create-windows-installer\n\n`)
}
process.exit(1)
}
const appMetadata = require(packageJsonPath)
const versionMatch = appMetadata.version.match(/-(beta|nightly)\d+$/)
const releaseChannel = versionMatch ? versionMatch[1] : 'stable'
console.log(`Serving ${appMetadata.productName} release assets (channel = ${releaseChannel})\n`.green)
function getMacZip (req, res) {
console.log(`Received request for atom-mac.zip, sending it`)
res.sendFile(path.join(buildPath, 'atom-mac.zip'))
}
function getMacUpdates (req, res) {
if (req.query.version !== appMetadata.version) {
const updateInfo = {
name: appMetadata.version,
pub_date: new Date().toISOString(),
url: `http://localhost:${port}/mac/atom-mac.zip`,
notes: '<p>No Details</p>'
}
console.log(`Received request for macOS updates (version = ${req.query.version}), sending\n`, updateInfo)
res.json(updateInfo)
} else {
console.log(`Received request for macOS updates, sending 204 as Atom is up to date (version = ${req.query.version})`)
res.sendStatus(204)
}
}
function getReleasesFile (fileName) {
return function (req, res) {
console.log(`Received request for ${fileName}, version: ${req.query.version}`)
if (req.query.version) {
const versionMatch = (req.query.version || '').match(/-(beta|nightly)\d+$/)
const versionChannel = (versionMatch && versionMatch[1]) || 'stable'
if (releaseChannel !== versionChannel) {
console.log(`Atom requested an update for version ${req.query.version} but the current release channel is ${releaseChannel}`)
res.sendStatus(404)
return
}
}
res.sendFile(path.join(buildPath, fileName))
}
}
function getNupkgFile (is64bit) {
return function (req, res) {
let nupkgFile = req.params.nupkg
if (is64bit) {
const nupkgMatch = nupkgFile.match(/atom-(.+)-(delta|full)\.nupkg/)
if (nupkgMatch) {
nupkgFile = `atom-x64-${nupkgMatch[1]}-${nupkgMatch[2]}.nupkg`
}
}
console.log(`Received request for ${req.params.nupkg}, sending ${nupkgFile}`)
res.sendFile(path.join(buildPath, nupkgFile))
}
}
if (process.platform === 'darwin') {
app.get('/mac/atom-mac.zip', getMacZip)
app.get('/api/updates', getMacUpdates)
} else if (process.platform === 'win32') {
app.get('/api/updates/RELEASES', getReleasesFile('RELEASES'))
app.get('/api/updates/:nupkg', getNupkgFile())
app.get('/api/updates-x64/RELEASES', getReleasesFile('RELEASES-x64'))
app.get('/api/updates-x64/:nupkg', getNupkgFile(true))
} else {
console.log(`The current platform '${process.platform}' doesn't support Squirrel updates, exiting.`.red)
process.exit(1)
}
app.listen(port, () => {
console.log(`Run Atom with ATOM_UPDATE_URL_PREFIX="http://localhost:${port}" set to test updates!\n`.yellow)
})

View File

@@ -62,19 +62,17 @@ jobs:
downloadType: 'specific'
displayName: Download Release Artifacts
# NOTE: This has been disabled until one final issue has been resolved
# around setting the SHA on GitHub releases.
# - script: |
# node $(Build.SourcesDirectory)\script\vsts\upload-artifacts.js --create-github-release --assets-path "$(System.ArtifactsDirectory)" --linux-repo-name "atom-staging"
# env:
# GITHUB_TOKEN: $(GITHUB_TOKEN)
# ATOM_RELEASE_VERSION: $(ReleaseVersion)
# ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY)
# ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET)
# ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET)
# PACKAGE_CLOUD_API_KEY: $(PACKAGE_CLOUD_API_KEY)
# displayName: Create Draft Release
# condition: and(succeeded(), eq(variables['IsReleaseBranch'], 'true'))
- script: |
node $(Build.SourcesDirectory)\script\vsts\upload-artifacts.js --create-github-release --assets-path "$(System.ArtifactsDirectory)" --linux-repo-name "atom-staging"
env:
GITHUB_TOKEN: $(GITHUB_TOKEN)
ATOM_RELEASE_VERSION: $(ReleaseVersion)
ATOM_RELEASES_S3_KEY: $(ATOM_RELEASES_S3_KEY)
ATOM_RELEASES_S3_SECRET: $(ATOM_RELEASES_S3_SECRET)
ATOM_RELEASES_S3_BUCKET: $(ATOM_RELEASES_S3_BUCKET)
PACKAGE_CLOUD_API_KEY: $(PACKAGE_CLOUD_API_KEY)
displayName: Create Draft Release
condition: and(succeeded(), eq(variables['Atom.AutoDraftRelease'], 'true'), eq(variables['IsReleaseBranch'], 'true'))
- script: |
node $(Build.SourcesDirectory)\script\vsts\upload-artifacts.js --assets-path "$(System.ArtifactsDirectory)" --s3-path "vsts-artifacts/$(Build.BuildId)/"

View File

@@ -4,6 +4,7 @@ const fs = require('fs')
const os = require('os')
const path = require('path')
const glob = require('glob')
const spawnSync = require('../lib/spawn-sync')
const publishRelease = require('publish-release')
const releaseNotes = require('./lib/release-notes')
const uploadToS3 = require('./lib/upload-to-s3')
@@ -93,6 +94,11 @@ async function uploadArtifacts () {
console.log(`New release notes:\n\n${newReleaseNotes}`)
const releaseSha =
!isNightlyRelease
? spawnSync('git', ['rev-parse', 'HEAD']).stdout.toString().trimEnd()
: 'master' // Nightly tags are created in atom/atom-nightly-releases so the SHA is irrelevant
console.log(`Creating GitHub release v${releaseVersion}`)
const release =
await publishReleaseAsync({
@@ -101,6 +107,7 @@ async function uploadArtifacts () {
repo: !isNightlyRelease ? 'atom' : 'atom-nightly-releases',
name: CONFIG.computedAppVersion,
notes: newReleaseNotes,
target_commitish: releaseSha,
tag: `v${CONFIG.computedAppVersion}`,
draft: !isNightlyRelease,
prerelease: CONFIG.channel !== 'stable',

19
spec/fixtures/dir/c vendored Normal file
View File

@@ -0,0 +1,19 @@
line 1
line 2
line 3
line 4
line 5
result 1
line 6
line 7
line 8
line 9
line 10
result 2
result 3
line 11
line 12
result 4
line 13
line 14
line 15

View File

@@ -0,0 +1,3 @@
asciiProperty=Foo
utf8Property=Fòò
latin1Property=Fòò

View File

@@ -0,0 +1,7 @@
newline1
newline2
newline3
first
second\nthird
newline4
newline5

1
spec/fixtures/dir/file-with-unicode vendored Normal file
View File

@@ -0,0 +1 @@
ДДДДДДДДДДДДДДДДДД line with unicode

File diff suppressed because it is too large Load Diff

View File

@@ -71,7 +71,7 @@ module.exports = class DefaultDirectorySearcher {
// * `filePath` {String} absolute path to the matching file.
// * `matches` {Array} with object elements with the following keys:
// * `lineText` {String} The full text of the matching line (without a line terminator character).
// * `lineTextOffset` {Number} (This always seems to be 0?)
// * `lineTextOffset` {Number} If > 0, the provided line text is truncated and starts at this offset
// * `matchText` {String} The text that matched the `regex` used for the search.
// * `range` {Range} Identifies the matching region in the file. (Likely as an array of numeric arrays.)
// * `didError` {Function} call with an Error if there is a problem during the search.

View File

@@ -22,15 +22,16 @@ class AutoUpdateManager extends EventEmitter {
this.config = config
this.state = IdleState
this.iconPath = path.resolve(__dirname, '..', '..', 'resources', 'atom.png')
this.updateUrlPrefix = process.env.ATOM_UPDATE_URL_PREFIX || 'https://atom.io'
}
initialize () {
if (process.platform === 'win32') {
const archSuffix = process.arch === 'ia32' ? '' : `-${process.arch}`
this.feedUrl = `https://atom.io/api/updates${archSuffix}?version=${this.version}`
this.feedUrl = `${this.updateUrlPrefix}/api/updates${archSuffix}?version=${this.version}`
autoUpdater = require('./auto-updater-win32')
} else {
this.feedUrl = `https://atom.io/api/updates?version=${this.version}`;
this.feedUrl = `${this.updateUrlPrefix}/api/updates?version=${this.version}`;
({autoUpdater} = require('electron'))
}

View File

@@ -0,0 +1,399 @@
const { spawn } = require('child_process')
const path = require('path')
// `ripgrep` and `scandal` have a different way of handling the trailing and leading
// context lines:
// * `scandal` returns all the context lines that are requested, even if they include
// previous or future results.
// * `ripgrep` is a bit smarter and only returns the context lines that do not correspond
// to any result (in a similar way that is shown in the find and replace UI).
//
// For example, if we have the following file and we request to leading context lines:
//
// line 1
// line 2
// result 1
// result 2
// line 3
// line 4
//
// `scandal` will return two results:
// * First result with `['line 1', line 2']` as leading context.
// * Second result with `['line 2', result 1']` as leading context.
// `ripgrep` on the other hand will return a JS object that is more similar to the way that
// the results are shown:
// [
// {type: 'begin', ...},
// {type: 'context', ...}, // context for line 1
// {type: 'context', ...}, // context for line 2
// {type: 'match', ...}, // result 1
// {type: 'match', ...}, // result 2
// {type: 'end', ...},
// ]
//
// In order to keep backwards compatibility, and avoid doing changes to the find and replace logic,
// for `ripgrep` we need to keep some state with the context lines (and matches) to be able to build
// a data structure that has the same behaviour as the `scandal` one.
//
// We use the `pendingLeadingContext` array to generate the leading context. This array gets mutated
// to always contain the leading `n` lines and is cloned every time a match is found. It's currently
// implemented as a standard array but we can easily change it to use a linked list if we find that
// the shift operations are slow.
//
// We use the `pendingTrailingContexts` Set to generate the trailing context. Since the trailing
// context needs to be generated after receiving a match, we keep all trailing context arrays that
// haven't been fulfilled in this Set, and mutate them adding new lines until they are fulfilled.
function updateLeadingContext (message, pendingLeadingContext, options) {
if (message.type !== 'match' && message.type !== 'context') {
return
}
if (options.leadingContextLineCount) {
pendingLeadingContext.push(cleanResultLine(message.data.lines))
if (pendingLeadingContext.length > options.leadingContextLineCount) {
pendingLeadingContext.shift()
}
}
}
function updateTrailingContexts (message, pendingTrailingContexts, options) {
if (message.type !== 'match' && message.type !== 'context') {
return
}
if (options.trailingContextLineCount) {
for (const trailingContextLines of pendingTrailingContexts) {
trailingContextLines.push(cleanResultLine(message.data.lines))
if (trailingContextLines.length === options.trailingContextLineCount) {
pendingTrailingContexts.delete(trailingContextLines)
}
}
}
}
function cleanResultLine (resultLine) {
resultLine = getText(resultLine)
return resultLine[resultLine.length - 1] === '\n' ? resultLine.slice(0, -1) : resultLine
}
function getPositionFromColumn (lines, column) {
let currentLength = 0
let currentLine = 0
let previousLength = 0
while (column >= currentLength) {
previousLength = currentLength
currentLength += lines[currentLine].length + 1
currentLine++
}
return [currentLine - 1, column - previousLength]
}
function processUnicodeMatch (match) {
const text = getText(match.lines)
if (text.length === Buffer.byteLength(text)) {
// fast codepath for lines that only contain characters of 1 byte length.
return
}
let remainingBuffer = Buffer.from(text)
let currentLength = 0
let previousPosition = 0
function convertPosition (position) {
const currentBuffer = remainingBuffer.slice(0, position - previousPosition)
currentLength = currentBuffer.toString().length + currentLength
remainingBuffer = remainingBuffer.slice(position)
previousPosition = position
return currentLength
}
// Iterate over all the submatches to find the convert the start and end values
// (which come as bytes from ripgrep) to character positions.
// We can do this because submatches come ordered by position.
for (const submatch of match.submatches) {
submatch.start = convertPosition(submatch.start)
submatch.end = convertPosition(submatch.end)
}
}
// This function processes a ripgrep submatch to create the correct
// range. This is mostly needed for multi-line results, since the range
// will have differnt start and end rows and we need to calculate these
// based on the lines that ripgrep returns.
function processSubmatch (submatch, lineText, offsetRow) {
const lineParts = lineText.split('\n')
const start = getPositionFromColumn(lineParts, submatch.start)
const end = getPositionFromColumn(lineParts, submatch.end)
// Make sure that the lineText string only contains lines that are
// relevant to this submatch. This means getting rid of lines above
// the start row and below the end row.
for (let i = start[0]; i > 0; i--) {
lineParts.shift()
}
while (end[0] < lineParts.length - 1) {
lineParts.pop()
}
start[0] += offsetRow
end[0] += offsetRow
return {
range: [start, end],
lineText: cleanResultLine({ text: lineParts.join('\n') })
}
}
function getText (input) {
return input.text ? input.text : Buffer.from(input.bytes, 'base64').toString()
}
module.exports = class RipgrepDirectorySearcher {
canSearchDirectory () {
return true
}
// Performs a text search for files in the specified `Directory`s, subject to the
// specified parameters.
//
// Results are streamed back to the caller by invoking methods on the specified `options`,
// such as `didMatch` and `didError`.
//
// * `directories` {Array} of {Directory} objects to search, all of which have been accepted by
// this searcher's `canSearchDirectory()` predicate.
// * `regex` {RegExp} to search with.
// * `options` {Object} with the following properties:
// * `didMatch` {Function} call with a search result structured as follows:
// * `searchResult` {Object} with the following keys:
// * `filePath` {String} absolute path to the matching file.
// * `matches` {Array} with object elements with the following keys:
// * `lineText` {String} The full text of the matching line (without a line terminator character).
// * `lineTextOffset` {Number} Always 0, present for backwards compatibility
// * `matchText` {String} The text that matched the `regex` used for the search.
// * `range` {Range} Identifies the matching region in the file. (Likely as an array of numeric arrays.)
// * `didError` {Function} call with an Error if there is a problem during the search.
// * `didSearchPaths` {Function} periodically call with the number of paths searched that contain results thus far.
// * `inclusions` {Array} of glob patterns (as strings) to search within. Note that this
// array may be empty, indicating that all files should be searched.
//
// Each item in the array is a file/directory pattern, e.g., `src` to search in the "src"
// directory or `*.js` to search all JavaScript files. In practice, this often comes from the
// comma-delimited list of patterns in the bottom text input of the ProjectFindView dialog.
// * `ignoreHidden` {boolean} whether to ignore hidden files.
// * `excludeVcsIgnores` {boolean} whether to exclude VCS ignored paths.
// * `exclusions` {Array} similar to inclusions
// * `follow` {boolean} whether symlinks should be followed.
//
// Returns a *thenable* `DirectorySearch` that includes a `cancel()` method. If `cancel()` is
// invoked before the `DirectorySearch` is determined, it will resolve the `DirectorySearch`.
search (directories, regexp, options) {
const numPathsFound = { num: 0 }
const allPromises = directories.map(
directory => this.searchInDirectory(directory, regexp, options, numPathsFound)
)
const promise = Promise.all(allPromises)
promise.cancel = () => {
for (const promise of allPromises) {
promise.cancel()
}
}
return promise
}
searchInDirectory (directory, regexp, options, numPathsFound) {
// Delay the require of vscode-ripgrep to not mess with the snapshot creation.
if (!this.rgPath) {
this.rgPath = require('vscode-ripgrep').rgPath.replace(/\bapp\.asar\b/, 'app.asar.unpacked')
}
const directoryPath = directory.getPath()
const regexpStr = this.prepareRegexp(regexp.source)
const args = ['--hidden', '--json', '--regexp', regexpStr]
if (options.leadingContextLineCount) {
args.push('--before-context', options.leadingContextLineCount)
}
if (options.trailingContextLineCount) {
args.push('--after-context', options.trailingContextLineCount)
}
if (regexp.ignoreCase) {
args.push('--ignore-case')
}
for (const inclusion of this.prepareGlobs(options.inclusions, directoryPath)) {
args.push('--glob', inclusion)
}
for (const exclusion of this.prepareGlobs(options.exclusions, directoryPath)) {
args.push('--glob', '!' + exclusion)
}
if (this.isMultilineRegexp(regexpStr)) {
args.push('--multiline')
}
args.push(directoryPath)
const child = spawn(this.rgPath, args, {
cwd: directory.getPath(),
stdio: ['pipe', 'pipe', 'pipe']
})
const didMatch = options.didMatch || (() => {})
let cancelled = false
const returnedPromise = new Promise((resolve, reject) => {
let buffer = ''
let bufferError = ''
let pendingEvent
let pendingLeadingContext
let pendingTrailingContexts
child.on('close', (code, signal) => {
// code 1 is used when no results are found.
if (code !== null && code > 1) {
reject(new Error(bufferError))
} else {
resolve()
}
})
child.stderr.on('data', chunk => {
bufferError += chunk
})
child.stdout.on('data', chunk => {
if (cancelled) {
return
}
buffer += chunk
const lines = buffer.split('\n')
buffer = lines.pop()
for (const line of lines) {
const message = JSON.parse(line)
updateTrailingContexts(message, pendingTrailingContexts, options)
if (message.type === 'begin') {
pendingEvent = {
filePath: getText(message.data.path),
matches: []
}
pendingLeadingContext = []
pendingTrailingContexts = new Set()
} else if (message.type === 'match') {
const trailingContextLines = []
pendingTrailingContexts.add(trailingContextLines)
processUnicodeMatch(message.data)
for (const submatch of message.data.submatches) {
const { lineText, range } = processSubmatch(
submatch,
getText(message.data.lines),
message.data.line_number - 1
)
pendingEvent.matches.push({
matchText: getText(submatch.match),
lineText,
lineTextOffset: 0,
range,
leadingContextLines: [...pendingLeadingContext],
trailingContextLines
})
}
} else if (message.type === 'end') {
options.didSearchPaths(++numPathsFound.num)
didMatch(pendingEvent)
pendingEvent = null
}
updateLeadingContext(message, pendingLeadingContext, options)
}
})
})
returnedPromise.cancel = () => {
child.kill()
cancelled = true
}
return returnedPromise
}
// We need to prepare the "globs" that we receive from the user to make their behaviour more
// user-friendly (e.g when adding `src/` the user probably means `src/**/*`).
// This helper function takes care of that.
prepareGlobs (globs, projectRootPath) {
const output = []
for (let pattern of globs) {
// we need to replace path separators by slashes since globs should
// always use always slashes as path separators.
pattern = pattern.replace(new RegExp(`\\${path.sep}`, 'g'), '/')
if (pattern.length === 0) {
continue
}
const projectName = path.basename(projectRootPath)
// The user can just search inside one of the opened projects. When we detect
// this scenario we just consider the glob to include every file.
if (pattern === projectName) {
output.push('**/*')
continue
}
if (pattern.startsWith(projectName + '/')) {
pattern = pattern.slice(projectName.length + 1)
}
if (pattern.endsWith('/')) {
pattern = pattern.slice(0, -1)
}
pattern = pattern.startsWith('**/') ? pattern : `**/${pattern}`
output.push(pattern)
output.push(pattern.endsWith('/**') ? pattern : `${pattern}/**`)
}
return output
}
prepareRegexp (regexpStr) {
// ripgrep handles `--` as the arguments separator, so we need to escape it if the
// user searches for that exact same string.
if (regexpStr === '--') {
return '\\-\\-'
}
// ripgrep is quite picky about unnecessarily escaped sequences, so we need to unescape
// them: https://github.com/BurntSushi/ripgrep/issues/434.
regexpStr = regexpStr.replace(/\\\//g, '/')
return regexpStr
}
isMultilineRegexp (regexpStr) {
if (regexpStr.includes('\\n')) {
return true
}
return false
}
}

View File

@@ -6,6 +6,7 @@ const fs = require('fs-plus')
const {Directory} = require('pathwatcher')
const Grim = require('grim')
const DefaultDirectorySearcher = require('./default-directory-searcher')
const RipgrepDirectorySearcher = require('./ripgrep-directory-searcher')
const Dock = require('./dock')
const Model = require('./model')
const StateStore = require('./state-store')
@@ -203,7 +204,8 @@ module.exports = class Workspace extends Model {
this.destroyedItemURIs = []
this.stoppedChangingActivePaneItemTimeout = null
this.defaultDirectorySearcher = new DefaultDirectorySearcher()
this.scandalDirectorySearcher = new DefaultDirectorySearcher()
this.ripgrepDirectorySearcher = new RipgrepDirectorySearcher()
this.consumeServices(this.packageManager)
this.paneContainers = {
@@ -1853,7 +1855,7 @@ module.exports = class Workspace extends Model {
// will be associated with an Array of Directory objects in the Map.
const directoriesForSearcher = new Map()
for (const directory of this.project.getDirectories()) {
let searcher = this.defaultDirectorySearcher
let searcher = options.ripgrep ? this.ripgrepDirectorySearcher : this.scandalDirectorySearcher
for (const directorySearcher of this.directorySearchers) {
if (directorySearcher.canSearchDirectory(directory)) {
searcher = directorySearcher
@@ -1926,7 +1928,7 @@ module.exports = class Workspace extends Model {
var matches = []
buffer.scan(regex, match => matches.push(match))
if (matches.length > 0) {
iterator({filePath, matches})
iterator({ filePath, matches })
}
}
}
@@ -1945,9 +1947,9 @@ module.exports = class Workspace extends Model {
}
}
const onFailure = function () {
const onFailure = function (error) {
for (let promise of allSearches) { promise.cancel() }
reject()
reject(error)
}
searchPromise.then(onSuccess, onFailure)
@@ -1960,12 +1962,6 @@ module.exports = class Workspace extends Model {
allSearches.map((promise) => promise.cancel())
}
// Although this method claims to return a `Promise`, the `ResultsPaneView.onSearch()`
// method in the find-and-replace package expects the object returned by this method to have a
// `done()` method. Include a done() method until find-and-replace can be updated.
cancellablePromise.done = onSuccessOrFailure => {
cancellablePromise.then(onSuccessOrFailure, onSuccessOrFailure)
}
return cancellablePromise
}