Merge branch 'master' into tree-sitter-injections

This commit is contained in:
Max Brunsfeld
2018-07-13 16:10:19 -07:00
47 changed files with 1025 additions and 72 deletions

View File

@@ -15,7 +15,7 @@ matrix:
include:
- os: linux
dist: trusty
env: NODE_VERSION=6.9.4 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1
env: NODE_VERSION=8.9.3 DISPLAY=:99.0 CC=clang CXX=clang++ npm_config_clang=1
sudo: required

View File

@@ -20,7 +20,7 @@ environment:
global:
ATOM_DEV_RESOURCE_PATH: c:\projects\atom
TEST_JUNIT_XML_ROOT: c:\projects\junit-test-results
NODE_VERSION: 6.9.4
NODE_VERSION: 8.9.3
matrix:
- TASK: test
@@ -36,7 +36,6 @@ install:
- IF NOT EXIST %TEST_JUNIT_XML_ROOT% MKDIR %TEST_JUNIT_XML_ROOT%
- SET PATH=C:\Program Files\Atom\resources\cli;%PATH%
- ps: Install-Product node $env:NODE_VERSION $env:PLATFORM
- npm install -g npm@5.3.0
build_script:
- CD %APPVEYOR_BUILD_FOLDER%

10
atom.sh
View File

@@ -13,6 +13,9 @@ case $(basename $0) in
atom-beta)
CHANNEL=beta
;;
atom-nightly)
CHANNEL=nightly
;;
atom-dev)
CHANNEL=dev
;;
@@ -78,6 +81,10 @@ if [ $OS == 'Mac' ]; then
if [ "$CHANNEL" == 'beta' ]; then
ATOM_EXECUTABLE_NAME="Atom Beta"
elif [ "$CHANNEL" == 'nightly' ]; then
ATOM_EXECUTABLE_NAME="Atom Nightly"
elif [ "$CHANNEL" == 'dev' ]; then
ATOM_EXECUTABLE_NAME="Atom Dev"
else
ATOM_EXECUTABLE_NAME="Atom"
fi
@@ -114,6 +121,9 @@ elif [ $OS == 'Linux' ]; then
beta)
ATOM_PATH="$USR_DIRECTORY/share/atom-beta/atom"
;;
nightly)
ATOM_PATH="$USR_DIRECTORY/share/atom-nightly/atom"
;;
dev)
ATOM_PATH="$USR_DIRECTORY/share/atom-dev/atom"
;;

View File

@@ -17,9 +17,8 @@ general:
dependencies:
pre:
- curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.3/install.sh | bash
- nvm install 6.9.4
- nvm use 6.9.4
- npm install -g npm@5.3.0
- nvm install 8.9.3
- nvm use 8.9.3
override:
- script/build --code-sign --compress-artifacts

View File

@@ -2,7 +2,7 @@
## Status
Accepted
Implemented in PR [#17538](https://github.com/atom/atom/pull/17538)
## Summary

View File

@@ -12,7 +12,7 @@
"url": "https://github.com/atom/atom/issues"
},
"license": "MIT",
"electronVersion": "2.0.4",
"electronVersion": "2.0.5",
"dependencies": {
"@atom/nsfw": "^1.0.18",
"@atom/watcher": "1.0.3",
@@ -91,7 +91,7 @@
"one-light-syntax": "1.8.3",
"solarized-dark-syntax": "1.1.5",
"solarized-light-syntax": "1.1.5",
"about": "1.9.1",
"about": "1.10.0",
"archive-view": "0.65.1",
"autocomplete-atom-api": "0.10.7",
"autocomplete-css": "0.17.5",
@@ -111,7 +111,7 @@
"exception-reporting": "0.43.1",
"find-and-replace": "0.215.11",
"fuzzy-finder": "1.8.2",
"github": "0.17.2",
"github": "0.17.3",
"git-diff": "1.3.9",
"go-to-line": "0.33.0",
"grammar-selector": "0.50.1",
@@ -127,7 +127,7 @@
"package-generator": "1.3.0",
"settings-view": "0.255.0",
"snippets": "1.3.3",
"spell-check": "0.73.5",
"spell-check": "0.74.0",
"status-bar": "1.8.15",
"styleguide": "0.49.11",
"symbols-view": "0.118.2",

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -104,8 +104,10 @@ if (!argv.generateApiDocs) {
}
if (argv.createWindowsInstaller) {
return createWindowsInstaller(packagedAppPath)
.then(() => argv.codeSign && codeSignOnWindows([ path.join(CONFIG.buildOutputPath, 'AtomSetup.exe') ]))
.then(() => packagedAppPath)
.then((installerPath) => {
argv.codeSign && codeSignOnWindows([installerPath])
return packagedAppPath
})
} else {
console.log('Skipping creating installer. Specify the --create-windows-installer option to create a Squirrel-based Windows installer.'.gray)
}

View File

@@ -5,6 +5,7 @@
const fs = require('fs')
const path = require('path')
const spawnSync = require('./lib/spawn-sync')
const repositoryRootPath = path.resolve(__dirname, '..')
const apmRootPath = path.join(repositoryRootPath, 'apm')
@@ -19,12 +20,16 @@ const atomHomeDirPath = process.env.ATOM_HOME || path.join(homeDirPath, '.atom')
const appMetadata = require(path.join(repositoryRootPath, 'package.json'))
const apmMetadata = require(path.join(apmRootPath, 'package.json'))
const channel = getChannel()
const computedAppVersion = computeAppVersion(process.env.ATOM_RELEASE_VERSION || appMetadata.version)
const channel = getChannel(computedAppVersion)
const appName = getAppName(channel)
module.exports = {
appMetadata,
apmMetadata,
channel,
appName,
computedAppVersion,
repositoryRootPath,
apmRootPath,
scriptRootPath,
@@ -40,14 +45,30 @@ module.exports = {
snapshotAuxiliaryData: {}
}
function getChannel () {
if (appMetadata.version.match(/dev/)) {
return 'dev'
} else if (appMetadata.version.match(/beta/)) {
return 'beta'
} else {
return 'stable'
function getChannel (version) {
const match = version.match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/)
if (!match) {
throw new Error(`Found incorrectly formatted Atom version ${version}`)
} else if (match[2]) {
return match[2]
}
return 'stable'
}
function getAppName (channel) {
return channel === 'stable'
? 'Atom'
: `Atom ${process.env.ATOM_CHANNEL_DISPLAY_NAME || channel.charAt(0).toUpperCase() + channel.slice(1)}`
}
function computeAppVersion (version) {
if (version.match(/-dev$/)) {
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: repositoryRootPath})
const commitHash = result.stdout.toString().trim()
version += '-' + commitHash
}
return version
}
function getApmBinPath () {

View File

@@ -10,7 +10,7 @@ const path = require('path')
module.exports = function () {
console.log('Deleting problematic package-lock.json files')
let paths = glob.sync(path.join(CONFIG.repositoryRootPath, '**', 'package-lock.json'), {ignore: path.join('**', 'node_modules', '**')})
let paths = glob.sync(path.join(CONFIG.repositoryRootPath, '**', 'package-lock.json'), {ignore: [path.join('**', 'node_modules', '**'), path.join('**', 'vsts', '**')]})
for (let path of paths) {
fs.unlinkSync(path)

View File

@@ -16,6 +16,36 @@ module.exports = function (packagedAppPath) {
downloadFileFromGithub(process.env.ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL, certPath)
}
try {
console.log(`Ensuring keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN} exists`)
try {
spawnSync('security', [
'show-keychain-info',
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
], {stdio: 'inherit'})
} catch (err) {
console.log(`Creating keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
// The keychain doesn't exist, try to create it
spawnSync('security', [
'create-keychain',
'-p', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD,
process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
], {stdio: 'inherit'})
// List the keychain to "activate" it. Somehow this seems
// to be needed otherwise the signing operation fails
spawnSync('security', [
'list-keychains',
'-s', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
], {stdio: 'inherit'})
// Make sure it doesn't time out before we use it
spawnSync('security', [
'set-keychain-settings',
'-t', '3600',
'-u', process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN
], {stdio: 'inherit'})
}
console.log(`Unlocking keychain ${process.env.ATOM_MAC_CODE_SIGNING_KEYCHAIN}`)
const unlockArgs = ['unlock-keychain']
// For signing on local workstations, password could be entered interactively

View File

@@ -3,6 +3,7 @@
const fs = require('fs-extra')
const path = require('path')
const spawnSync = require('./spawn-sync')
const { path7za } = require('7zip-bin')
const CONFIG = require('../config')
@@ -19,7 +20,7 @@ module.exports = function (packagedAppPath) {
function getArchiveName () {
switch (process.platform) {
case 'darwin': return 'atom-mac.zip'
case 'win32': return 'atom-windows.zip'
case 'win32': return `atom-${process.arch === 'x64' ? 'x64-' : ''}windows.zip`
default: return `atom-${getLinuxArchiveArch()}.tar.gz`
}
}
@@ -44,7 +45,7 @@ function compress (inputDirPath, outputArchivePath) {
compressCommand = 'zip'
compressArguments = ['-r', '--symlinks']
} else if (process.platform === 'win32') {
compressCommand = '7z.exe'
compressCommand = path7za
compressArguments = ['a', '-r']
} else {
compressCommand = 'tar'

View File

@@ -10,9 +10,8 @@ const CONFIG = require('../config')
module.exports = function (packagedAppPath) {
console.log(`Creating Debian package for "${packagedAppPath}"`)
const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm'
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`
const appDescription = CONFIG.appMetadata.description
const appVersion = CONFIG.appMetadata.version
let arch
@@ -88,7 +87,7 @@ module.exports = function (packagedAppPath) {
console.log(`Writing desktop entry file into "${debianPackageApplicationsDirPath}"`)
const desktopEntryTemplate = fs.readFileSync(path.join(CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in'))
const desktopEntryContents = template(desktopEntryTemplate)({
appName: appName,
appName: CONFIG.appName,
appFileName: atomExecutableName,
description: appDescription,
installDir: '/usr',

View File

@@ -10,9 +10,9 @@ const CONFIG = require('../config')
module.exports = function (packagedAppPath) {
console.log(`Creating rpm package for "${packagedAppPath}"`)
const atomExecutableName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
const apmExecutableName = CONFIG.channel === 'beta' ? 'apm-beta' : 'apm'
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : `atom-${CONFIG.channel}`
const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : `apm-${CONFIG.channel}`
const appName = CONFIG.appName
const appDescription = CONFIG.appMetadata.description
// RPM versions can't have dashes or tildes in them.
// (Ref.: https://twiki.cern.ch/twiki/bin/view/Main/RPMAndDebVersioning)

View File

@@ -1,7 +1,7 @@
'use strict'
const electronInstaller = require('electron-winstaller')
const fs = require('fs-extra')
const fs = require('fs')
const glob = require('glob')
const path = require('path')
@@ -16,17 +16,31 @@ module.exports = (packagedAppPath) => {
loadingGif: path.join(CONFIG.repositoryRootPath, 'resources', 'win', 'loading.gif'),
outputDirectory: CONFIG.buildOutputPath,
noMsi: true,
remoteReleases: `https://atom.io/api/updates${archSuffix}?version=${CONFIG.appMetadata.version}`,
remoteReleases: `https://atom.io/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')
}
const cleanUp = () => {
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/*.nupkg`)) {
const releasesPath = `${CONFIG.buildOutputPath}/RELEASES`
if (process.arch === 'x64' && fs.existsSync(releasesPath)) {
fs.renameSync(releasesPath, `${releasesPath}-x64`)
}
for (let nupkgPath of glob.sync(`${CONFIG.buildOutputPath}/atom-*.nupkg`)) {
if (!nupkgPath.includes(CONFIG.appMetadata.version)) {
console.log(`Deleting downloaded nupkg for previous version at ${nupkgPath} to prevent it from being stored as an artifact`)
fs.removeSync(nupkgPath)
fs.unlinkSync(nupkgPath)
} else {
if (process.arch === 'x64') {
// Use the original .nupkg filename to generate the `atom-x64` name by inserting `-x64` after `atom`
const newNupkgPath = nupkgPath.replace('atom-', 'atom-x64-')
fs.renameSync(nupkgPath, newNupkgPath)
}
}
}
return `${CONFIG.buildOutputPath}/${options.setupExe}`
}
console.log(`Creating Windows Installer for ${packagedAppPath}`)

View File

@@ -6,7 +6,6 @@ const fs = require('fs-plus')
const normalizePackageData = require('normalize-package-data')
const path = require('path')
const semver = require('semver')
const spawnSync = require('./spawn-sync')
const CONFIG = require('../config')
@@ -16,7 +15,7 @@ module.exports = function () {
CONFIG.appMetadata._atomMenu = buildPlatformMenuMetadata()
CONFIG.appMetadata._atomKeymaps = buildPlatformKeymapsMetadata()
CONFIG.appMetadata._deprecatedPackages = deprecatedPackagesMetadata
CONFIG.appMetadata.version = computeAppVersion()
CONFIG.appMetadata.version = CONFIG.computedAppVersion
checkDeprecatedPackagesMetadata()
fs.writeFileSync(path.join(CONFIG.intermediateAppPath, 'package.json'), JSON.stringify(CONFIG.appMetadata))
}
@@ -162,13 +161,3 @@ function checkDeprecatedPackagesMetadata () {
}
}
}
function computeAppVersion () {
let version = CONFIG.appMetadata.version
if (CONFIG.channel === 'dev') {
const result = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {cwd: CONFIG.repositoryRootPath})
const commitHash = result.stdout.toString().trim()
version += '-' + commitHash
}
return version
}

View File

@@ -74,7 +74,7 @@ module.exports = function (packagedAppPath) {
const verifySnapshotScriptPath = path.join(CONFIG.repositoryRootPath, 'script', 'verify-snapshot-script')
let nodeBundledInElectronPath
if (process.platform === 'darwin') {
const executableName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
const executableName = CONFIG.appName
nodeBundledInElectronPath = path.join(packagedAppPath, 'Contents', 'MacOS', executableName)
} else if (process.platform === 'win32') {
nodeBundledInElectronPath = path.join(packagedAppPath, 'atom.exe')

View File

@@ -114,7 +114,7 @@ function buildAsarUnpackGlobExpression () {
function getAppName () {
if (process.platform === 'darwin') {
return CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
return CONFIG.appName
} else {
return 'atom'
}
@@ -156,7 +156,7 @@ function renamePackagedAppDir (packageOutputDirPath) {
if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
fs.renameSync(path.join(packageOutputDirPath, appBundleName), packagedAppPath)
} else if (process.platform === 'linux') {
const appName = CONFIG.channel === 'beta' ? 'atom-beta' : 'atom'
const appName = CONFIG.channel !== 'stable' ? `atom-${CONFIG.channel}` : 'atom'
let architecture
if (process.arch === 'ia32') {
architecture = 'i386'
@@ -169,8 +169,7 @@ function renamePackagedAppDir (packageOutputDirPath) {
if (fs.existsSync(packagedAppPath)) fs.removeSync(packagedAppPath)
fs.renameSync(packageOutputDirPath, packagedAppPath)
} else {
const appName = CONFIG.channel === 'beta' ? 'Atom Beta' : 'Atom'
packagedAppPath = path.join(CONFIG.buildOutputPath, appName)
packagedAppPath = path.join(CONFIG.buildOutputPath, CONFIG.appName)
if (process.platform === 'win32' && process.arch !== 'ia32') {
packagedAppPath += ` ${process.arch}`
}

View File

@@ -0,0 +1,57 @@
'use strict'
const fs = require('fs')
const path = require('path')
const aws = require('aws-sdk')
module.exports = function (s3Key, s3Secret, s3Bucket, directory, assets) {
const s3 = new aws.S3({
accessKeyId: s3Key,
secretAccessKey: s3Secret,
params: { Bucket: s3Bucket }
})
function listExistingAssetsForDirectory (directory) {
return s3.listObjectsV2({ Prefix: directory }).promise().then((res) => {
return res.Contents.map((obj) => { return { Key: obj.Key } })
})
}
function deleteExistingAssets (existingAssets) {
if (existingAssets.length > 0) {
return s3.deleteObjects({ Delete: { Objects: existingAssets } }).promise()
} else {
return Promise.resolve(true)
}
}
function uploadAssets (assets, directory) {
return assets.reduce(
function (promise, asset) {
return promise.then(() => uploadAsset(directory, asset))
}, Promise.resolve())
}
function uploadAsset (directory, assetPath) {
return new Promise((resolve, reject) => {
console.info(`Uploading ${assetPath}`)
const params = {
Key: `${directory}${path.basename(assetPath)}`,
ACL: 'public-read',
Body: fs.createReadStream(assetPath)
}
s3.upload(params, error => {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}
return listExistingAssetsForDirectory(directory)
.then(deleteExistingAssets)
.then(() => uploadAssets(assets, directory))
}

View File

@@ -2,7 +2,9 @@
"name": "atom-build-scripts",
"description": "Atom build scripts",
"dependencies": {
"7zip-bin": "^4.0.2",
"async": "2.0.1",
"aws-sdk": "^2.5.2",
"babel-core": "5.8.38",
"coffeelint": "1.15.7",
"colors": "1.1.2",
@@ -26,6 +28,7 @@
"npm": "5.3.0",
"passwd-user": "2.1.0",
"pegjs": "0.9.0",
"publish-release": "^1.6.0",
"random-seed": "^0.3.0",
"season": "5.3.0",
"semver": "5.3.0",

52
script/publish-release Normal file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/env node
'use strict'
const path = require('path')
const glob = require('glob')
const publishRelease = require('publish-release')
const uploadToS3 = require('./lib/upload-to-s3')
const CONFIG = require('./config')
const yargs = require('yargs')
const argv = yargs
.usage('Usage: $0 [options]')
.help('help')
.describe('assets-path', 'Path to the folder where all release assets are stored')
.wrap(yargs.terminalWidth())
.argv
let assetsPath = argv.assetsPath || path.join(CONFIG.repositoryRootPath, 'out')
let assets = glob.sync(path.join(assetsPath, '*(*.exe|*.zip|*.nupkg|*.tar.gz|*.rpm|*.deb|RELEASES*)'))
console.log(`Uploading release assets for ${CONFIG.computedAppVersion} to S3`)
uploadToS3(
process.env.ATOM_RELEASES_S3_KEY,
process.env.ATOM_RELEASES_S3_SECRET,
process.env.ATOM_RELEASES_S3_BUCKET,
`releases/v${CONFIG.computedAppVersion}/`,
assets).then(
() => {
console.log(`Publishing GitHub release ${CONFIG.computedAppVersion}`)
publishRelease({
token: process.env.GITHUB_TOKEN,
owner: 'atom',
repo: CONFIG.channel !== 'nightly' ? 'atom' : 'atom-nightly-releases',
name: CONFIG.computedAppVersion,
tag: `v${CONFIG.computedAppVersion}`,
draft: false,
prerelease: CONFIG.channel !== 'stable',
reuseRelease: true,
skipIfPublished: true,
assets
}, function (err, release) {
if (err) {
console.error("An error occurred while publishing the release:\n\n", err)
} else {
console.log("Release published successfully: ", release.html_url)
}
})
}).catch((err) => {
console.error('An error occurred while uploading the release:', err)
})

View File

@@ -0,0 +1,5 @@
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\publish-release" %*
) ELSE (
node "%~dp0\publish-release" %*
)

65
script/vsts/README.md Normal file
View File

@@ -0,0 +1,65 @@
# Atom Release Build Documentation
## Overview
This folder contains build configuration and scripts for automating Atom's
release pipeline using [Visual Studio Team Services](https://azure.microsoft.com/en-us/services/visual-studio-team-services/).
VSTS allows us to leverage [multi-phase jobs](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-jobs.md) to generate Atom installation packages
on Windows, macOS, and Linux and then publish a new release automatically once
the build completes successfully.
## Nightly Release Build
Our scheduled nightly release uses a mutli-phase job to automatically generate Atom
Nightly installation packages and then publish them to GitHub and atom.io.
The [Atom Nightly build definition](https://github.visualstudio.com/Atom/_build/index?context=mine&path=%5C&definitionId=1&_a=completed)
is configured with the [`nightly-release.yml`](nightly-release.yml) file. More
information on VSTS' YAML configuration format can be found in their [Getting Started](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted.md)
documentation.
### Versioning Phase
In this phase, we run [`script/vsts/generate-version.js`](generate-version.js) to
determine the version of the next Atom Nightly release. This script consults the
GitHub v3 API to get the list of releases on the [`atom/atom-nightly-releases`](https://github.com/atom/atom-nightly-releases)
repo. We look for the most recent, non-draft release and then parse its version
number (e.g. `1.30.0-nightly4`) to extract the base version and the monotonically-increasing
nightly release number.
Once we have the version and release number, we compare the base version number
(`1.30.0`) against the one in `package.json` of the latest commit in the local
repo. If those versions are the same, we increment the release number (`1.30.0-nightly5`).
If those versions are different, we use `0` for the release number to start a
new series of Nightly releases for the new version (`1.31.0-nightly0`).
Once the release version has been determined, it is set as our custom `ReleaseVersion`
[output variable](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-outputvariables.md)
by writing out a special string to `stdout` which is recognized by VSTS. This
variable will be used in later build steps.
If any part of the build process fails from this point forward, the same version
number *should* be chosen in the next build unless the base version number has
been changed in `master`.
### OS-specific Build Phases
In this part of the build, we use [phase templates](https://github.com/Microsoft/vsts-agent/blob/master/docs/preview/yamlgettingstarted-templates.md)
for [Windows](windows.yml), [macOS](macos.yml), and [Linux](linux.yml) to build
Atom simultaneously across those platforms and then run the Atom test suite to
verify the builds. If build, test, and linting come back clean, we take the build
assets generated in the `out` folder on each OS and then stage them as build artifacts.
For each OS build, we refer to the `ReleaseVersion` variable, set in the previous
phase, to configure the `ATOM_RELEASE_VERSION` environment variable to override
the version contained in Atom's `package.json`.
### Publish Phase
If all three OS builds have completed successfully, the publish phase will launch the
[`script/publish-release`](../publish-release) script to collect the release
artifacts created from those builds and then upload them to the S3 bucket from
which Atom release assets are served. If the upload process is successful, a new
release will be created on the `atom/atom-nightly-releases` repo using the
`ReleaseVersion` with a `v` prefix as the tag name. The release assets will also
be uploaded to the GitHub release at this time.

View File

@@ -0,0 +1,33 @@
const path = require('path')
const request = require('request-promise-native')
const repositoryRootPath = path.resolve(__dirname, '..', '..')
const appMetadata = require(path.join(repositoryRootPath, 'package.json'))
const baseVersion = appMetadata.version.split('-')[0]
async function generateNightlyVersion () {
const releases = await request({
url: 'https://api.github.com/repos/atom/atom-nightly-releases/releases',
headers: {'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'Atom Release Build'},
json: true
})
let releaseNumber = 0
if (releases && releases.length > 0) {
const latestRelease = releases.find(r => !r.draft)
const versionMatch = latestRelease.tag_name.match(/^v?(\d+\.\d+\.\d+)-nightly(\d+)$/)
if (versionMatch && versionMatch[1] === baseVersion) {
releaseNumber = parseInt(versionMatch[2]) + 1
}
}
// Set our ReleaseVersion build variable and update VSTS' build number to
// include the version. Writing these strings to stdout causes VSTS to set
// the associated variables.
const generatedVersion = `${baseVersion}-nightly${releaseNumber}`
console.log(`##vso[task.setvariable variable=ReleaseVersion;isOutput=true]${generatedVersion}`)
console.log(`##vso[build.updatebuildnumber]${generatedVersion}+${process.env.BUILD_BUILDNUMBER}`)
}
generateNightlyVersion()

58
script/vsts/linux.yml Normal file
View File

@@ -0,0 +1,58 @@
phases:
- phase: Linux
dependsOn: GetReleaseVersion
variables:
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
queue:
name: Hosted Linux Preview
timeoutInMinutes: 180
steps:
- task: NodeTool@0
inputs:
versionSpec: 8.9.3
displayName: Install Node.js 8.9.3
- script: |
apt-get update
apt-get install -y --no-install-recommends build-essential xvfb clang-3.5 fakeroot git libsecret-1-dev rpm libx11-dev libxkbfile-dev xz-utils xorriso zsync libxss1 libgconf2-4 libgtk-3-0
displayName: Install apt dependencies
- script: |
script/build --create-debian-package --create-rpm-package --compress-artifacts
env:
ATOM_RELEASE_VERSION: $(ReleaseVersion)
displayName: Build Atom
- script: script/lint
displayName: Run linter
- script: |
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16
export DISPLAY=':99.0'
Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
script/test
env:
CI: true
CI_PROVIDER: VSTS
displayName: Run tests
# This step is necessary in the short term due to a bug in the *NIX
# implementation of the CopyFiles task which scans the entire file
# system structure just to resolve the glob pattern.
- script: rm -rf $(Build.SourcesDirectory)/out/*/
displayName: Delete Intermediate Output
- task: CopyFiles@2
inputs:
sourceFolder: $(Build.SourcesDirectory)/out
contents: '?(*.deb|*.rpm|*.tar.gz)'
targetFolder: $(Build.ArtifactStagingDirectory)
displayName: Stage Artifacts
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)
ArtifactName: Binaries
ArtifactType: Container
displayName: Upload Artifacts

55
script/vsts/macos.yml Normal file
View File

@@ -0,0 +1,55 @@
phases:
- phase: macOS
dependsOn: GetReleaseVersion
variables:
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
queue:
name: Hosted macOS Preview
timeoutInMinutes: 180
steps:
- task: NodeTool@0
inputs:
versionSpec: 8.9.3
displayName: Install Node.js 8.9.3
- script: |
script/build --code-sign --compress-artifacts
displayName: Build Atom
env:
ATOM_RELEASE_VERSION: $(ReleaseVersion)
ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL: $(ATOM_MAC_CODE_SIGNING_CERT_DOWNLOAD_URL)
ATOM_MAC_CODE_SIGNING_CERT_PASSWORD: $(ATOM_MAC_CODE_SIGNING_CERT_PASSWORD)
ATOM_MAC_CODE_SIGNING_KEYCHAIN: $(ATOM_MAC_CODE_SIGNING_KEYCHAIN)
ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD: $(ATOM_MAC_CODE_SIGNING_KEYCHAIN_PASSWORD)
- script: script/lint
displayName: Run linter
- script: |
osascript -e 'tell application "System Events" to keystroke "x"' # clear screen saver
caffeinate -s script/test # Run with caffeinate to prevent screen saver
env:
CI: true
CI_PROVIDER: VSTS
displayName: Run tests
# This step is necessary in the short term due to a bug in the *NIX
# implementation of the CopyFiles task which scans the entire file
# system structure just to resolve the glob pattern.
- script: rm -rf $(Build.SourcesDirectory)/out/*/
displayName: Delete Intermediate Output
- task: CopyFiles@2
inputs:
sourceFolder: $(Build.SourcesDirectory)/out
contents: '*.zip'
targetFolder: $(Build.ArtifactStagingDirectory)
displayName: Stage Artifacts
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)
ArtifactName: Binaries
ArtifactType: Container
displayName: Upload Artifacts

View File

@@ -0,0 +1,57 @@
phases:
- phase: GetReleaseVersion
steps:
# This has to be done separately because VSTS inexplicably
# exits the script block after `npm install` completes.
- script: |
cd script\vsts
npm install
displayName: npm install
- script: node script\vsts\generate-version.js
name: Version
# Import OS-specific build definitions
- template: windows.yml
- template: macos.yml
- template: linux.yml
- phase: Release
queue: Hosted # Need this for Python 2.7
dependsOn:
- GetReleaseVersion
- Windows
- Linux
- macOS
variables:
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
steps:
- task: NodeTool@0
inputs:
versionSpec: 8.9.3
displayName: Install Node.js 8.9.3
# This has to be done separately because VSTS inexplicably
# exits the script block after `npm install` completes.
- script: |
cd script
npm install
displayName: npm install
- task: DownloadBuildArtifacts@0
displayName: Download Release Artifacts
inputs:
artifactName: Binaries
- script: |
$(Build.SourcesDirectory)\script\publish-release.cmd --assets-path "$(System.ArtifactsDirectory)/Binaries"
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)
displayName: Create Nightly Release

357
script/vsts/package-lock.json generated Normal file
View File

@@ -0,0 +1,357 @@
{
"name": "atom-release-scripts",
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"requires": {
"co": "4.6.0",
"fast-deep-equal": "1.1.0",
"fast-json-stable-stringify": "2.0.0",
"json-schema-traverse": "0.3.1"
}
},
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
},
"combined-stream": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"requires": {
"delayed-stream": "1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.6",
"mime-types": "2.1.18"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"requires": {
"ajv": "5.5.2",
"har-schema": "2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "1.0.0",
"jsprim": "1.4.1",
"sshpk": "1.14.2"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"optional": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"lodash": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"mime-db": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
},
"mime-types": {
"version": "2.1.18",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
"requires": {
"mime-db": "1.33.0"
}
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"request": {
"version": "2.87.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
"requires": {
"aws-sign2": "0.7.0",
"aws4": "1.7.0",
"caseless": "0.12.0",
"combined-stream": "1.0.6",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.3.2",
"har-validator": "5.0.3",
"http-signature": "1.2.0",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.18",
"oauth-sign": "0.8.2",
"performance-now": "2.1.0",
"qs": "6.5.2",
"safe-buffer": "5.1.2",
"tough-cookie": "2.3.4",
"tunnel-agent": "0.6.0",
"uuid": "3.3.0"
}
},
"request-promise-core": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
"requires": {
"lodash": "4.17.10"
}
},
"request-promise-native": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz",
"integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=",
"requires": {
"request-promise-core": "1.1.1",
"stealthy-require": "1.1.1",
"tough-cookie": "2.3.4"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sshpk": {
"version": "1.14.2",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz",
"integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=",
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jsbn": "0.1.1",
"safer-buffer": "2.1.2",
"tweetnacl": "0.14.5"
}
},
"stealthy-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
"integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks="
},
"tough-cookie": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz",
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==",
"requires": {
"punycode": "1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "5.1.2"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"optional": true
},
"uuid": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.0.tgz",
"integrity": "sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw=="
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "1.3.0"
}
}
}
}

8
script/vsts/package.json Normal file
View File

@@ -0,0 +1,8 @@
{
"name": "atom-release-scripts",
"description": "Atom release scripts",
"dependencies": {
"request": "^2.87.0",
"request-promise-native": "^1.0.5"
}
}

54
script/vsts/windows.yml Normal file
View File

@@ -0,0 +1,54 @@
phases:
- phase: Windows
dependsOn: GetReleaseVersion
variables:
ReleaseVersion: $[ dependencies.GetReleaseVersion.outputs['Version.ReleaseVersion'] ]
queue:
name: Hosted
timeoutInMinutes: 180
parallel: 2
matrix:
x64:
buildArch: x64
# TODO: x86 is currently not supported on VSTS
# x86:
# buildArch: x86
steps:
- task: NodeTool@0
inputs:
versionSpec: 8.9.3
displayName: Install Node.js 8.9.3
- script: |
IF NOT EXIST C:\tmp MKDIR C:\tmp
SET SQUIRREL_TEMP=C:\tmp
script\build.cmd --create-windows-installer --code-sign --compress-artifacts
env:
ATOM_RELEASE_VERSION: $(ReleaseVersion)
ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL: $(ATOM_WIN_CODE_SIGNING_CERT_DOWNLOAD_URL)
ATOM_WIN_CODE_SIGNING_CERT_PASSWORD: $(ATOM_WIN_CODE_SIGNING_CERT_PASSWORD)
displayName: Build Atom
- script: script\lint.cmd
displayName: Run linter
- script: script\test.cmd
env:
CI: true
CI_PROVIDER: VSTS
displayName: Run tests
- task: CopyFiles@2
inputs:
sourceFolder: $(Build.SourcesDirectory)/out
contents: '?(*.exe|*.zip|*.nupkg|RELEASES*)'
targetFolder: $(Build.ArtifactStagingDirectory)
displayName: Stage Artifacts
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: $(Build.ArtifactStagingDirectory)
ArtifactName: Binaries
ArtifactType: Container
displayName: Upload Artifacts

View File

@@ -1,6 +1,7 @@
const path = require('path')
const fs = require('fs-plus')
const temp = require('temp').track()
const {it, fit, ffit, fffit, beforeEach, afterEach} = require('./async-spec-helpers');
const CommandInstaller = require('../src/command-installer')
describe('CommandInstaller on #darwin', () => {
@@ -56,8 +57,8 @@ describe('CommandInstaller on #darwin', () => {
const appDelegate = jasmine.createSpyObj('appDelegate', ['confirm'])
installer = new CommandInstaller(appDelegate)
installer.initialize('2.0.2')
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback())
spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback())
spyOn(installer, 'installAtomCommand').andCallFake((__, callback) => callback(undefined, 'atom'))
spyOn(installer, 'installApmCommand').andCallFake((__, callback) => callback(undefined, 'apm'))
installer.installShellCommandsInteractively()
@@ -140,4 +141,41 @@ describe('CommandInstaller on #darwin', () => {
})
})
})
describe('when using a nightly version of atom', () => {
beforeEach(() => {
installer = new CommandInstaller()
installer.initialize('2.2.0-nightly0')
})
it("symlinks the atom command as 'atom-nightly'", () => {
const installedAtomPath = path.join(installationPath, 'atom-nightly')
expect(fs.isFileSync(installedAtomPath)).toBeFalsy()
waitsFor(done => {
installer.installAtomCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedAtomPath)).toBe(fs.realpathSync(atomBinPath))
expect(fs.isExecutableSync(installedAtomPath)).toBe(true)
expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe(false)
done()
})
})
})
it("symlinks the apm command as 'apm-nightly'", () => {
const installedApmPath = path.join(installationPath, 'apm-nightly')
expect(fs.isFileSync(installedApmPath)).toBeFalsy()
waitsFor(done => {
installer.installApmCommand(false, error => {
expect(error).toBeNull()
expect(fs.realpathSync(installedApmPath)).toBe(fs.realpathSync(apmBinPath))
expect(fs.isExecutableSync(installedApmPath)).toBeTruthy()
expect(fs.isFileSync(path.join(installationPath, 'nightly'))).toBe(false)
done()
})
})
})
})
})

View File

@@ -1,6 +1,8 @@
const {dialog} = require('electron')
const FileRecoveryService = require('../../src/main-process/file-recovery-service')
const fs = require('fs-plus')
const fsreal = require('fs')
const EventEmitter = require('events').EventEmitter
const sinon = require('sinon')
const {escapeRegExp} = require('underscore-plus')
const temp = require('temp').track()
@@ -116,13 +118,22 @@ describe("FileRecoveryService", () => {
const mockWindow = {}
const filePath = temp.path()
fs.writeFileSync(filePath, "content")
fs.chmodSync(filePath, 0444)
let logs = []
spies.stub(console, 'log', (message) => logs.push(message))
spies.stub(dialog, 'showMessageBox')
// Copy files to be recovered before mocking fs.createWriteStream
await recoveryService.willSavePath(mockWindow, filePath)
// Stub out fs.createWriteStream so that we can return a fake error when
// attempting to copy the recovered file to its original location
var fakeEmitter = new EventEmitter()
var onStub = spies.stub(fakeEmitter, 'on')
onStub.withArgs('error').yields(new Error('Nope')).returns(fakeEmitter)
onStub.withArgs('open').returns(fakeEmitter)
spies.stub(fsreal, 'createWriteStream').withArgs(filePath).returns(fakeEmitter)
await recoveryService.didCrashWindow(mockWindow)
let recoveryFiles = fs.listTreeSync(recoveryDirectory)
assert.equal(recoveryFiles.length, 1)

View File

@@ -212,6 +212,18 @@ describe('TooltipManager', () => {
})
})
)
describe('when a user types', () =>
it('hides the tooltips', () => {
const disposable = manager.add(element, { title: 'Title' })
hover(element, function () {
expect(document.body.querySelector('.tooltip')).not.toBeNull()
window.dispatchEvent(new CustomEvent('keydown'))
expect(document.body.querySelector('.tooltip')).toBeNull()
disposable.dispose()
})
})
)
describe('findTooltips', () => {
it('adds and remove tooltips correctly', () => {

View File

@@ -487,21 +487,25 @@ class AtomEnvironment {
// Public: Gets the release channel of the Atom application.
//
// Returns the release channel as a {String}. Will return one of `dev`, `beta`, or `stable`.
// Returns the release channel as a {String}. Will return a specific release channel
// name like 'beta' or 'nightly' if one is found in the Atom version or 'stable'
// otherwise.
getReleaseChannel () {
const version = this.getVersion()
if (version.includes('beta')) {
return 'beta'
} else if (version.includes('dev')) {
return 'dev'
} else {
return 'stable'
// This matches stable, dev (with or without commit hash) and any other
// release channel following the pattern '1.00.0-channel0'
const match = this.getVersion().match(/\d+\.\d+\.\d+(-([a-z]+)(\d+|-\w{4,})?)?$/)
if (!match) {
return 'unrecognized'
} else if (match[2]) {
return match[2]
}
return 'stable'
}
// Public: Returns a {Boolean} that is `true` if the current version is an official release.
isReleasedVersion () {
return !/\w{7}/.test(this.getVersion()) // Check if the release is a 7-character SHA prefix
return this.getReleaseChannel().match(/stable|beta|nightly/) != null
}
// Public: Get the time taken to completely load the current window.

View File

@@ -27,22 +27,36 @@ class CommandInstaller {
}, () => {})
}
this.installAtomCommand(true, error => {
this.installAtomCommand(true, (error, atomCommandName) => {
if (error) return showErrorDialog(error)
this.installApmCommand(true, error => {
this.installApmCommand(true, (error, apmCommandName) => {
if (error) return showErrorDialog(error)
this.applicationDelegate.confirm({
message: 'Commands installed.',
detail: 'The shell commands `atom` and `apm` are installed.'
detail: `The shell commands \`${atomCommandName}\` and \`${apmCommandName}\` are installed.`
}, () => {})
})
})
}
getCommandNameForChannel (commandName) {
let channelMatch = this.appVersion.match(/beta|nightly/)
let channel = channelMatch ? channelMatch[0] : ''
switch (channel) {
case 'beta':
return `${commandName}-beta`
case 'nightly':
return `${commandName}-nightly`
default:
return commandName
}
}
installAtomCommand (askForPrivilege, callback) {
this.installCommand(
path.join(this.getResourcesDirectory(), 'app', 'atom.sh'),
this.appVersion.includes('beta') ? 'atom-beta' : 'atom',
this.getCommandNameForChannel('atom'),
askForPrivilege,
callback
)
@@ -51,7 +65,7 @@ class CommandInstaller {
installApmCommand (askForPrivilege, callback) {
this.installCommand(
path.join(this.getResourcesDirectory(), 'app', 'apm', 'node_modules', '.bin', 'apm'),
this.appVersion.includes('beta') ? 'apm-beta' : 'apm',
this.getCommandNameForChannel('apm'),
askForPrivilege,
callback
)
@@ -64,11 +78,11 @@ class CommandInstaller {
fs.readlink(destinationPath, (error, realpath) => {
if (error && error.code !== 'ENOENT') return callback(error)
if (realpath === commandPath) return callback()
if (realpath === commandPath) return callback(null, commandName)
this.createSymlink(fs, commandPath, destinationPath, error => {
if (error && error.code === 'EACCES' && askForPrivilege) {
const fsAdmin = require('fs-admin')
this.createSymlink(fsAdmin, commandPath, destinationPath, callback)
this.createSymlink(fsAdmin, commandPath, destinationPath, (error) => { callback(error, commandName) })
} else {
callback(error)
}

View File

@@ -12,13 +12,18 @@ module.exports = function parseCommandLine (processArgs) {
options.usage(
dedent`Atom Editor v${version}
Usage: atom [options] [path ...]
Usage:
atom [options] [path ...]
atom file[:line[:column]]
One or more paths to files or folders may be specified. If there is an
existing Atom window that contains all of the given folders, the paths
will be opened in that window. Otherwise, they will be opened in a new
window.
A file may be opened at the desired line (and optionally column) by
appending the numbers right after the file name, e.g. \`atom file:5:8\`.
Paths that start with \`atom://\` will be interpreted as URLs.
Environment Variables:

View File

@@ -153,9 +153,11 @@ class TooltipManager {
}
window.addEventListener('resize', hideTooltip)
window.addEventListener('keydown', hideTooltip)
const disposable = new Disposable(() => {
window.removeEventListener('resize', hideTooltip)
window.removeEventListener('keydown', hideTooltip)
hideTooltip()
tooltip.destroy()