mirror of
https://github.com/electron/electron.git
synced 2026-02-26 03:01:17 -05:00
Compare commits
22 Commits
v3.0.0-bet
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d9775b0b1 | ||
|
|
9237d46dba | ||
|
|
8f8d198c5a | ||
|
|
fcdbb8c4a7 | ||
|
|
b49b071a1f | ||
|
|
1feb45da29 | ||
|
|
2c43dbdb25 | ||
|
|
7eff91dde1 | ||
|
|
780483ff35 | ||
|
|
c2f770df02 | ||
|
|
74e6e063d4 | ||
|
|
7702cc1090 | ||
|
|
97319e5a3a | ||
|
|
041c952574 | ||
|
|
6c9b6f9cac | ||
|
|
c4884da601 | ||
|
|
59b05ed661 | ||
|
|
e3204a5ec1 | ||
|
|
adf49daaac | ||
|
|
9431677e79 | ||
|
|
f63e5ffa65 | ||
|
|
32e40cb4c3 |
@@ -81,9 +81,13 @@ build-steps: &build-steps
|
||||
mkdir junit
|
||||
script/test.py --ci --rebuild_native_modules
|
||||
else
|
||||
echo 'Testing Electron release build'
|
||||
mkdir junit
|
||||
script/test.py --ci --rebuild_native_modules -c R
|
||||
if [ "$UPLOAD_TO_S3" == "1" ]; then
|
||||
echo 'Testing Electron release build'
|
||||
mkdir junit
|
||||
script/test.py --ci --rebuild_native_modules -c R
|
||||
else
|
||||
echo 'Skipping tests on GitHub release'
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo 'Skipping tests due to configuration'
|
||||
@@ -118,10 +122,7 @@ build-steps: &build-steps
|
||||
- store_artifacts:
|
||||
path: junit
|
||||
- store_artifacts:
|
||||
path: out/electron.d.ts
|
||||
- store_artifacts:
|
||||
path: out/electron-api.json
|
||||
|
||||
path: out
|
||||
|
||||
build-defaults: &build-defaults
|
||||
docker:
|
||||
|
||||
29
appveyor.yml
29
appveyor.yml
@@ -1,25 +1,22 @@
|
||||
version: 1.0.{build}
|
||||
branches:
|
||||
except:
|
||||
- /^release$|^release-\d-\d-x$/
|
||||
build_cloud: electron-16
|
||||
image: electron-16-vs2017
|
||||
environment:
|
||||
DISABLE_CRASH_REPORTER_TESTS: true
|
||||
matrix:
|
||||
- TARGET_ARCH: ia32
|
||||
- TARGET_ARCH: x64
|
||||
build_script:
|
||||
- ps: >-
|
||||
if(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
|
||||
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
|
||||
|
||||
} else {
|
||||
Add-Path "$env:ProgramFiles (x86)\Windows Kits\10\Debuggers\x64"
|
||||
|
||||
$env:path = "$env:ProgramFiles (x86)\Windows Kits\10\Debuggers\x64;$env:path"
|
||||
if($env:APPVEYOR_SCHEDULED_BUILD -eq 'True') {
|
||||
$env:RUN_RELEASE_BUILD = "1"
|
||||
}
|
||||
$Message = (git log --format=%B -n 1 HEAD) | Out-String
|
||||
|
||||
if ((Test-Path Env:\RUN_RELEASE_BUILD)) {
|
||||
$env:ELECTRON_RELEASE = '1'
|
||||
Write-Output "release build triggered from api"
|
||||
@@ -39,8 +36,8 @@ build_script:
|
||||
} else {
|
||||
"Build succeeded."
|
||||
}
|
||||
Push-AppveyorArtifact out
|
||||
}
|
||||
|
||||
test_script:
|
||||
- ps: >-
|
||||
if (Test-Path Env:\ELECTRON_RELEASE) {
|
||||
@@ -54,7 +51,6 @@ test_script:
|
||||
Write-Output "Tests succeeded."
|
||||
}
|
||||
python script\verify-ffmpeg.py
|
||||
|
||||
if ($LASTEXITCODE -ne '0') {
|
||||
throw "Verify ffmpeg failed with exit code $LASTEXITCODE"
|
||||
} else {
|
||||
@@ -73,22 +69,5 @@ deploy_script:
|
||||
} else {
|
||||
Write-Output "Uploading Electron release distribution to github releases"
|
||||
& python script\upload.py
|
||||
if (Test-Path Env:\AUTO_RELEASE) {
|
||||
node script\release.js --validateRelease --automaticRelease
|
||||
if ($? -eq 'True') {
|
||||
echo 'Release is ready to go; now running release'
|
||||
node script\release.js --automaticRelease
|
||||
if ($? -eq 'True') {
|
||||
echo 'Release successful, now publishing to npm'
|
||||
$npmfile = "$HOME\.npmrc"
|
||||
"//registry.npmjs.org/:_authToken=$env:ELECTRON_NPM_TOKEN" > $npmfile
|
||||
npm run publish-to-npm
|
||||
}
|
||||
} else {
|
||||
echo 'Release is not complete, skipping publish for now.'
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Write-Output "Skipping upload distribution because build is not for release"
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ BrowserWindow::BrowserWindow(v8::Isolate* isolate,
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
if (!window()->has_frame())
|
||||
OverrideNSWindowContentView();
|
||||
OverrideNSWindowContentView(web_contents->managed_web_contents());
|
||||
#endif
|
||||
|
||||
// Init window after everything has been setup.
|
||||
|
||||
@@ -81,7 +81,7 @@ class BrowserWindow : public TopLevelWindow,
|
||||
|
||||
private:
|
||||
#if defined(OS_MACOSX)
|
||||
void OverrideNSWindowContentView();
|
||||
void OverrideNSWindowContentView(brightray::InspectableWebContents* iwc);
|
||||
#endif
|
||||
|
||||
// Helpers.
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "atom/browser/native_window_mac.h"
|
||||
#include "atom/common/draggable_region.h"
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "brightray/browser/inspectable_web_contents_view.h"
|
||||
|
||||
@interface NSView (WebContentsView)
|
||||
- (void)setMouseDownCanMoveWindow:(BOOL)can_move;
|
||||
@@ -54,11 +55,12 @@ std::vector<gfx::Rect> CalculateNonDraggableRegions(
|
||||
|
||||
} // namespace
|
||||
|
||||
void BrowserWindow::OverrideNSWindowContentView() {
|
||||
void BrowserWindow::OverrideNSWindowContentView(
|
||||
brightray::InspectableWebContents* iwc) {
|
||||
// Make NativeWindow use a NSView as content view.
|
||||
static_cast<NativeWindowMac*>(window())->OverrideNSWindowContentView();
|
||||
// Add webview to contentView.
|
||||
NSView* webView = web_contents()->GetNativeView();
|
||||
NSView* webView = iwc->GetView()->GetNativeView();
|
||||
NSView* contentView = [window()->GetNativeWindow() contentView];
|
||||
[webView setFrame:[contentView bounds]];
|
||||
[contentView addSubview:webView];
|
||||
|
||||
@@ -69,7 +69,7 @@ void StartHandlingTask(bool capture_window,
|
||||
capture_screen ? content::desktop_capture::CreateScreenCapturer()
|
||||
: nullptr);
|
||||
std::unique_ptr<webrtc::DesktopCapturer> window_capturer(
|
||||
capture_window ? content::desktop_capture::CreateScreenCapturer()
|
||||
capture_window ? content::desktop_capture::CreateWindowCapturer()
|
||||
: nullptr);
|
||||
cap->media_list_.reset(new NativeDesktopMediaList(
|
||||
std::move(screen_capturer), std::move(window_capturer)));
|
||||
|
||||
@@ -26,7 +26,8 @@ FrameSubscriber::FrameSubscriber(v8::Isolate* isolate,
|
||||
: content::WebContentsObserver(web_contents),
|
||||
isolate_(isolate),
|
||||
callback_(callback),
|
||||
only_dirty_(only_dirty) {}
|
||||
only_dirty_(only_dirty),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
FrameSubscriber::~FrameSubscriber() = default;
|
||||
|
||||
@@ -64,7 +65,7 @@ void FrameSubscriber::DidReceiveCompositorFrame() {
|
||||
|
||||
view->CopyFromSurface(
|
||||
gfx::Rect(), view->GetViewBounds().size(),
|
||||
base::BindOnce(&FrameSubscriber::Done, base::Unretained(this),
|
||||
base::BindOnce(&FrameSubscriber::Done, weak_ptr_factory_.GetWeakPtr(),
|
||||
GetDamageRect()));
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "components/viz/common/frame_sinks/copy_output_result.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
@@ -39,6 +40,8 @@ class FrameSubscriber : public content::WebContentsObserver {
|
||||
FrameCaptureCallback callback_;
|
||||
bool only_dirty_;
|
||||
|
||||
base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameSubscriber);
|
||||
};
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 3,0,0,2
|
||||
PRODUCTVERSION 3,0,0,2
|
||||
FILEVERSION 3,0,0,3
|
||||
PRODUCTVERSION 3,0,0,3
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
|
||||
@@ -12,17 +12,27 @@
|
||||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/hash.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "url/origin.h"
|
||||
#include "v8/include/v8-profiler.h"
|
||||
|
||||
// This is defined in later versions of Chromium, remove this if you see
|
||||
// compiler complaining duplicate defines.
|
||||
#if defined(OS_WIN) || defined(OS_FUCHSIA)
|
||||
#define CrPRIdPid "ld"
|
||||
#else
|
||||
#define CrPRIdPid "d"
|
||||
#endif
|
||||
|
||||
namespace std {
|
||||
|
||||
// The hash function used by DoubleIDWeakMap.
|
||||
template <typename Type1, typename Type2>
|
||||
struct hash<std::pair<Type1, Type2>> {
|
||||
std::size_t operator()(std::pair<Type1, Type2> value) const {
|
||||
return base::HashInts<Type1, Type2>(value.first, value.second);
|
||||
return base::HashInts(base::Hash(value.first), value.second);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,6 +100,16 @@ int32_t GetObjectHash(v8::Local<v8::Object> object) {
|
||||
return object->GetIdentityHash();
|
||||
}
|
||||
|
||||
std::string GetContextID(v8::Isolate* isolate) {
|
||||
// When a page is reloaded, V8 and blink may have optimizations that do not
|
||||
// free blink::WebLocalFrame and v8::Context and reuse them for the new page,
|
||||
// while we always recreate node::Environment when a page is loaded.
|
||||
// So the only reliable way to return an identity for a page, is to return the
|
||||
// address of the node::Environment instance.
|
||||
node::Environment* env = node::Environment::GetCurrent(isolate);
|
||||
return base::StringPrintf("%" CrPRIdPid "-%p", base::GetCurrentProcId(), env);
|
||||
}
|
||||
|
||||
void TakeHeapSnapshot(v8::Isolate* isolate) {
|
||||
isolate->GetHeapProfiler()->TakeHeapSnapshot();
|
||||
}
|
||||
@@ -112,12 +132,14 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||
dict.SetMethod("setHiddenValue", &SetHiddenValue);
|
||||
dict.SetMethod("deleteHiddenValue", &DeleteHiddenValue);
|
||||
dict.SetMethod("getObjectHash", &GetObjectHash);
|
||||
dict.SetMethod("getContextId", &GetContextID);
|
||||
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
|
||||
dict.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo);
|
||||
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
|
||||
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
|
||||
dict.SetMethod("createDoubleIDWeakMap",
|
||||
&atom::api::KeyWeakMap<std::pair<int64_t, int32_t>>::Create);
|
||||
dict.SetMethod(
|
||||
"createDoubleIDWeakMap",
|
||||
&atom::api::KeyWeakMap<std::pair<std::string, int32_t>>::Create);
|
||||
dict.SetMethod("requestGarbageCollectionForTesting",
|
||||
&RequestGarbageCollectionForTesting);
|
||||
dict.SetMethod("isSameOrigin", &IsSameOrigin);
|
||||
|
||||
@@ -15,17 +15,20 @@ namespace atom {
|
||||
// static
|
||||
void RemoteCallbackFreer::BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_contents) {
|
||||
new RemoteCallbackFreer(isolate, target, object_id, web_contents);
|
||||
new RemoteCallbackFreer(isolate, target, context_id, object_id, web_contents);
|
||||
}
|
||||
|
||||
RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_contents)
|
||||
: ObjectLifeMonitor(isolate, target),
|
||||
content::WebContentsObserver(web_contents),
|
||||
context_id_(context_id),
|
||||
object_id_(object_id) {}
|
||||
|
||||
RemoteCallbackFreer::~RemoteCallbackFreer() {}
|
||||
@@ -34,6 +37,7 @@ void RemoteCallbackFreer::RunDestructor() {
|
||||
base::string16 channel =
|
||||
base::ASCIIToUTF16("ELECTRON_RENDERER_RELEASE_CALLBACK");
|
||||
base::ListValue args;
|
||||
args.AppendString(context_id_);
|
||||
args.AppendInteger(object_id_);
|
||||
auto* frame_host = web_contents()->GetMainFrame();
|
||||
if (frame_host) {
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#ifndef ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
||||
#define ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/api/object_life_monitor.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
|
||||
@@ -14,12 +17,14 @@ class RemoteCallbackFreer : public ObjectLifeMonitor,
|
||||
public:
|
||||
static void BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_conents);
|
||||
|
||||
protected:
|
||||
RemoteCallbackFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_conents);
|
||||
~RemoteCallbackFreer() override;
|
||||
@@ -30,6 +35,7 @@ class RemoteCallbackFreer : public ObjectLifeMonitor,
|
||||
void RenderViewDeleted(content::RenderViewHost*) override;
|
||||
|
||||
private:
|
||||
std::string context_id_;
|
||||
int object_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RemoteCallbackFreer);
|
||||
|
||||
@@ -29,14 +29,17 @@ content::RenderFrame* GetCurrentRenderFrame() {
|
||||
// static
|
||||
void RemoteObjectFreer::BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id) {
|
||||
new RemoteObjectFreer(isolate, target, object_id);
|
||||
new RemoteObjectFreer(isolate, target, context_id, object_id);
|
||||
}
|
||||
|
||||
RemoteObjectFreer::RemoteObjectFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id)
|
||||
: ObjectLifeMonitor(isolate, target),
|
||||
context_id_(context_id),
|
||||
object_id_(object_id),
|
||||
routing_id_(MSG_ROUTING_NONE) {
|
||||
content::RenderFrame* render_frame = GetCurrentRenderFrame();
|
||||
@@ -56,6 +59,7 @@ void RemoteObjectFreer::RunDestructor() {
|
||||
base::string16 channel = base::ASCIIToUTF16("ipc-message");
|
||||
base::ListValue args;
|
||||
args.AppendString("ELECTRON_BROWSER_DEREFERENCE");
|
||||
args.AppendString(context_id_);
|
||||
args.AppendInteger(object_id_);
|
||||
render_frame->Send(new AtomFrameHostMsg_Message(render_frame->GetRoutingID(),
|
||||
channel, args));
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifndef ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
||||
#define ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/api/object_life_monitor.h"
|
||||
|
||||
namespace atom {
|
||||
@@ -13,17 +15,20 @@ class RemoteObjectFreer : public ObjectLifeMonitor {
|
||||
public:
|
||||
static void BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id);
|
||||
|
||||
protected:
|
||||
RemoteObjectFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id);
|
||||
~RemoteObjectFreer() override;
|
||||
|
||||
void RunDestructor() override;
|
||||
|
||||
private:
|
||||
std::string context_id_;
|
||||
int object_id_;
|
||||
int routing_id_;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#define ATOM_MAJOR_VERSION 3
|
||||
#define ATOM_MINOR_VERSION 0
|
||||
#define ATOM_PATCH_VERSION 0
|
||||
#define ATOM_PRE_RELEASE_VERSION -beta.2
|
||||
#define ATOM_PRE_RELEASE_VERSION -beta.3
|
||||
|
||||
#ifndef ATOM_STRINGIFY
|
||||
#define ATOM_STRINGIFY(n) ATOM_STRINGIFY_HELPER(n)
|
||||
|
||||
@@ -115,6 +115,10 @@ void SpellCheckClient::RequestCheckingOfText(
|
||||
base::Owned(pending_request_param_.release())));
|
||||
}
|
||||
|
||||
bool SpellCheckClient::IsSpellCheckingEnabled() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpellCheckClient::ShowSpellingUI(bool show) {}
|
||||
|
||||
bool SpellCheckClient::IsShowingSpellingUI() {
|
||||
|
||||
@@ -46,6 +46,7 @@ class SpellCheckClient : public blink::WebSpellCheckPanelHostClient,
|
||||
void RequestCheckingOfText(
|
||||
const blink::WebString& textToCheck,
|
||||
blink::WebTextCheckingCompletion* completionCallback) override;
|
||||
bool IsSpellCheckingEnabled() const override;
|
||||
|
||||
// blink::WebSpellCheckPanelHostClient:
|
||||
void ShowSpellingUI(bool show) override;
|
||||
|
||||
@@ -92,6 +92,12 @@ void InitializeBindings(v8::Local<v8::Object> binding,
|
||||
b.SetMethod("getHeapStatistics", &AtomBindings::GetHeapStatistics);
|
||||
b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo);
|
||||
b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo);
|
||||
|
||||
// Pass in CLI flags needed to setup the renderer
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (command_line->HasSwitch(switches::kGuestInstanceID))
|
||||
b.Set(options::kGuestInstanceID,
|
||||
command_line->GetSwitchValueASCII(switches::kGuestInstanceID));
|
||||
}
|
||||
|
||||
class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver {
|
||||
|
||||
@@ -90,6 +90,7 @@ PlatformNotificationService::CheckPermissionOnIOThread(
|
||||
content::ResourceContext* resource_context,
|
||||
const GURL& origin,
|
||||
int render_process_id) {
|
||||
render_process_id_ = render_process_id;
|
||||
return blink::mojom::PermissionStatus::GRANTED;
|
||||
}
|
||||
|
||||
|
||||
@@ -386,6 +386,7 @@ app.on('session-created', (event, session) => {
|
||||
|
||||
Returns:
|
||||
|
||||
* `event` Event
|
||||
* `argv` String[] - An array of the second instance's command line arguments
|
||||
* `workingDirectory` String - The second instance's working directory
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Follow the guidelines below for building Electron on Windows.
|
||||
## Prerequisites
|
||||
|
||||
* Windows 7 / Server 2008 R2 or higher
|
||||
* Visual Studio 2017 - [download VS 2017 Community Edition for
|
||||
* Visual Studio 2017 15.7.2 or higher - [download VS 2017 Community Edition for
|
||||
free](https://www.visualstudio.com/vs/)
|
||||
* [Python 2.7](http://www.python.org/download/releases/2.7/)
|
||||
* [Node.js](https://nodejs.org/download/)
|
||||
|
||||
@@ -2,23 +2,6 @@
|
||||
|
||||
This document describes the process for releasing a new version of Electron.
|
||||
|
||||
## Determine which branch to release from
|
||||
|
||||
- **If releasing beta,** run the scripts below from `master`.
|
||||
- **If releasing a stable version,** run the scripts below from the branch
|
||||
you're stabilizing.
|
||||
|
||||
## Find out what version change is needed
|
||||
Run `npm run prepare-release -- --notesOnly` to view auto generated release
|
||||
notes. The notes generated should help you determine if this is a major, minor,
|
||||
patch, or beta version change. Read the
|
||||
[Version Change Rules](../tutorial/electron-versioning.md#semver) for more information.
|
||||
|
||||
**NB:** If releasing from a branch, e.g. 1-8-x, check out the branch with
|
||||
`git checkout 1-8-x` rather than `git checkout -b remotes/origin/1-8-x`.
|
||||
The scripts need `git rev-parse --abbrev-ref HEAD` to return a short name,
|
||||
e.g. no `remotes/origin/`
|
||||
|
||||
## Set your tokens and environment variables
|
||||
You'll need Electron S3 credentials in order to create and
|
||||
upload an Electron release. Contact a team member for more
|
||||
@@ -35,6 +18,26 @@ Create a token from https://windows-ci.electronjs.org/api-token
|
||||
If you don't have an account, ask a team member to add you.
|
||||
* `CIRCLE_TOKEN`:
|
||||
Create a token from "Personal API Tokens" at https://circleci.com/account/api
|
||||
* `VSTS_TOKEN`:
|
||||
Create a Personal Access Token at https://github.visualstudio.com/_usersSettings/tokens
|
||||
with the scope of `Build (read and execute)`.
|
||||
|
||||
## Determine which branch to release from
|
||||
|
||||
- **If releasing beta,** run the scripts below from `master`.
|
||||
- **If releasing a stable version,** run the scripts below from the branch
|
||||
you're stabilizing.
|
||||
|
||||
## Find out what version change is needed
|
||||
Run `npm run prepare-release -- --notesOnly` to view auto generated release
|
||||
notes. The notes generated should help you determine if this is a major, minor,
|
||||
patch, or beta version change. Read the
|
||||
[Version Change Rules](../tutorial/electron-versioning.md#semver) for more information.
|
||||
|
||||
**NB:** If releasing from a branch, e.g. 1-8-x, check out the branch with
|
||||
`git checkout 1-8-x` rather than `git checkout -b remotes/origin/1-8-x`.
|
||||
The scripts need `git rev-parse --abbrev-ref HEAD` to return a short name,
|
||||
e.g. no `remotes/origin/`
|
||||
|
||||
## Run the prepare-release script
|
||||
The prepare release script will do the following:
|
||||
@@ -84,8 +87,11 @@ $ ./script/bump-version.py --bump minor --dry-run
|
||||
The `prepare-release` script will trigger the builds via API calls.
|
||||
To monitor the build progress, see the following pages:
|
||||
|
||||
- [circleci.com/gh/electron/electron](https://circleci.com/gh/electron) for OS X and Linux
|
||||
- [windows-ci.electronjs.org/project/AppVeyor/electron](https://windows-ci.electronjs.org/project/AppVeyor/electron) for Windows
|
||||
- [electron-release-mas-x64](https://github.visualstudio.com/electron/_build/index?context=allDefinitions&path=%5C&definitionId=19&_a=completed) for MAS builds.
|
||||
- [electron-release-osx-x64](https://github.visualstudio.com/electron/_build/index?context=allDefinitions&path=%5C&definitionId=18&_a=completed) for OSX builds.
|
||||
- [circleci.com/gh/electron/electron](https://circleci.com/gh/electron) for Linux builds.
|
||||
- [windows-ci.electronjs.org/project/AppVeyor/electron-39ng6](https://windows-ci.electronjs.org/project/AppVeyor/electron-39ng6) for Windows 32-bit builds.
|
||||
- [windows-ci.electronjs.org/project/AppVeyor/electron](https://windows-ci.electronjs.org/project/AppVeyor/electron) for Windows 64-bit builds.
|
||||
|
||||
## Compile release notes
|
||||
|
||||
@@ -192,35 +198,13 @@ under the `beta` tag and can be installed via `npm install electron@beta`.
|
||||
1. Visit [the releases page] and you'll see a new draft release with placeholder
|
||||
release notes.
|
||||
2. Edit the release and add release notes.
|
||||
3. Uncheck the `prerelease` checkbox if you're publishing a stable release;
|
||||
leave it checked for beta releases.
|
||||
4. Click 'Save draft'. **Do not click 'Publish release'!**
|
||||
5. Wait for all builds to pass before proceeding.
|
||||
6. In the `release` branch, verify that the release's files have been created:
|
||||
3. Click 'Save draft'. **Do not click 'Publish release'!**
|
||||
4. Wait for all builds to pass before proceeding.
|
||||
5. In the branch, verify that the release's files have been created:
|
||||
```sh
|
||||
$ git rev-parse --abbrev-ref HEAD
|
||||
release
|
||||
$ npm run release -- --validateRelease
|
||||
```
|
||||
|
||||
## Merge temporary branch (pre-2-0-x branches only)
|
||||
Once the release builds have finished, merge the `release` branch back into
|
||||
the source release branch using the `merge-release` script.
|
||||
If the branch cannot be successfully merged back this script will automatically
|
||||
rebase the `release` branch and push the changes which will trigger the release
|
||||
builds again, which means you will need to wait for the release builds to run
|
||||
again before proceeding.
|
||||
|
||||
### Merging back into master
|
||||
```sh
|
||||
npm run merge-release -- master
|
||||
```
|
||||
|
||||
### Merging back into old release branch
|
||||
```sh
|
||||
npm run merge-release -- 1-7-x
|
||||
```
|
||||
|
||||
## Publish the release
|
||||
|
||||
Once the merge has finished successfully, run the `release` script
|
||||
@@ -234,7 +218,6 @@ on Windows by node-gyp to build native modules.
|
||||
5. Validate that all of the required files are present on GitHub and S3 and have
|
||||
the correct checksums as specified in the SHASUMS files.
|
||||
6. Publish the release on GitHub
|
||||
7. Delete the `release` branch.
|
||||
|
||||
## Publish to npm
|
||||
|
||||
@@ -264,16 +247,39 @@ electron
|
||||
$ npm run publish-to-npm
|
||||
```
|
||||
|
||||
Note: In general you should be using the latest Node during this
|
||||
process; however, older versions of the `publish-to-npm` script
|
||||
may have trouble with Node 7 or higher. If you have trouble with
|
||||
this in an older branch, try running with an older version of Node,
|
||||
e.g. a 6.x LTS.
|
||||
|
||||
[the releases page]: https://github.com/electron/electron/releases
|
||||
[this bump commit]: https://github.com/electron/electron/commit/78ec1b8f89b3886b856377a1756a51617bc33f5a
|
||||
[versioning]: /docs/tutorial/electron-versioning.md
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Rerun broken builds
|
||||
|
||||
If a release build fails for some reason, you can use `script/ci-release-build.js` to rerun a release build:
|
||||
|
||||
### Rerun all linux builds:
|
||||
```sh
|
||||
node script/ci-release-build.js --ci=CircleCI --ghRelease TARGET_BRANCH
|
||||
(TARGET_BRANCH) is the branch you are releasing from.
|
||||
```
|
||||
|
||||
### Rerun all macOS builds:
|
||||
```sh
|
||||
node script/ci-release-build.js --ci=VSTS --ghRelease TARGET_BRANCH
|
||||
(TARGET_BRANCH) is the branch you are releasing from.
|
||||
```
|
||||
|
||||
### Rerun all Windows builds:
|
||||
```sh
|
||||
node script/ci-release-build.js --ci=AppVeyor --ghRelease TARGET_BRANCH
|
||||
(TARGET_BRANCH) is the branch you are releasing from.
|
||||
```
|
||||
|
||||
Additionally you can pass a job name to the script to run an individual job, eg:
|
||||
````sh
|
||||
node script/ci-release-build.js --ci=AppVeyor --ghRelease --job=electron-x64 TARGET_BRANCH
|
||||
```
|
||||
|
||||
## Fix missing binaries of a release manually
|
||||
|
||||
In the case of a corrupted release with broken CI machines, we might have to
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '3.0.0-beta.2',
|
||||
'version%': '3.0.0-beta.3',
|
||||
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
||||
},
|
||||
'includes': [
|
||||
|
||||
@@ -184,7 +184,7 @@ WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callba
|
||||
return asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.once('did-finish-load', () => {
|
||||
this.once('did-stop-loading', () => {
|
||||
asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture).then(resolve).catch(reject)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -240,6 +240,23 @@ const attachGuest = function (event, elementInstanceId, guestInstanceId, params)
|
||||
webPreferences.disablePopups = true
|
||||
}
|
||||
|
||||
// Security options that guest will always inherit from embedder
|
||||
const inheritedWebPreferences = new Map([
|
||||
['contextIsolation', true],
|
||||
['javascript', false],
|
||||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['sandbox', true]
|
||||
])
|
||||
|
||||
// Inherit certain option values from embedder
|
||||
const lastWebPreferences = embedder.getLastWebPreferences()
|
||||
for (const [name, value] of inheritedWebPreferences) {
|
||||
if (lastWebPreferences[name] === value) {
|
||||
webPreferences[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
embedder.emit('will-attach-webview', event, webPreferences, params)
|
||||
if (event.defaultPrevented) {
|
||||
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId
|
||||
|
||||
@@ -17,16 +17,15 @@ class ObjectsRegistry {
|
||||
|
||||
// Register a new object and return its assigned ID. If the object is already
|
||||
// registered then the already assigned ID would be returned.
|
||||
add (webContents, obj) {
|
||||
add (webContents, contextId, obj) {
|
||||
// Get or assign an ID to the object.
|
||||
const id = this.saveToStorage(obj)
|
||||
|
||||
// Add object to the set of referenced objects.
|
||||
const webContentsId = webContents.getId()
|
||||
let owner = this.owners[webContentsId]
|
||||
let owner = this.owners[contextId]
|
||||
if (!owner) {
|
||||
owner = this.owners[webContentsId] = new Set()
|
||||
this.registerDeleteListener(webContents, webContentsId)
|
||||
owner = this.owners[contextId] = new Set()
|
||||
this.registerDeleteListener(webContents, contextId)
|
||||
}
|
||||
if (!owner.has(id)) {
|
||||
owner.add(id)
|
||||
@@ -43,25 +42,26 @@ class ObjectsRegistry {
|
||||
}
|
||||
|
||||
// Dereference an object according to its ID.
|
||||
remove (webContentsId, id) {
|
||||
// Dereference from the storage.
|
||||
this.dereference(id)
|
||||
|
||||
// Also remove the reference in owner.
|
||||
let owner = this.owners[webContentsId]
|
||||
// Note that an object may be double-freed (cleared when page is reloaded, and
|
||||
// then garbage collected in old page).
|
||||
remove (contextId, id) {
|
||||
let owner = this.owners[contextId]
|
||||
if (owner) {
|
||||
// Remove the reference in owner.
|
||||
owner.delete(id)
|
||||
// Dereference from the storage.
|
||||
this.dereference(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all references to objects refrenced by the WebContents.
|
||||
clear (webContentsId) {
|
||||
let owner = this.owners[webContentsId]
|
||||
clear (contextId) {
|
||||
let owner = this.owners[contextId]
|
||||
if (!owner) return
|
||||
|
||||
for (let id of owner) this.dereference(id)
|
||||
|
||||
delete this.owners[webContentsId]
|
||||
delete this.owners[contextId]
|
||||
}
|
||||
|
||||
// Private: Saves the object into storage and assigns an ID for it.
|
||||
@@ -92,12 +92,12 @@ class ObjectsRegistry {
|
||||
}
|
||||
|
||||
// Private: Clear the storage when webContents is reloaded/navigated.
|
||||
registerDeleteListener (webContents, webContentsId) {
|
||||
registerDeleteListener (webContents, contextId) {
|
||||
const processId = webContents.getProcessId()
|
||||
const listener = (event, deletedProcessId) => {
|
||||
if (deletedProcessId === processId) {
|
||||
webContents.removeListener('render-view-deleted', listener)
|
||||
this.clear(webContentsId)
|
||||
this.clear(contextId)
|
||||
}
|
||||
}
|
||||
webContents.on('render-view-deleted', listener)
|
||||
|
||||
@@ -56,7 +56,7 @@ let getObjectPrototype = function (object) {
|
||||
}
|
||||
|
||||
// Convert a real value into meta data.
|
||||
let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
let valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
|
||||
// Determine the type of value.
|
||||
const meta = { type: typeof value }
|
||||
if (meta.type === 'object') {
|
||||
@@ -84,14 +84,14 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
|
||||
// Fill the meta object according to value's type.
|
||||
if (meta.type === 'array') {
|
||||
meta.members = value.map((el) => valueToMeta(sender, el, optimizeSimpleObject))
|
||||
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||
meta.name = value.constructor ? value.constructor.name : ''
|
||||
|
||||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
meta.id = objectsRegistry.add(sender, value)
|
||||
meta.id = objectsRegistry.add(sender, contextId, value)
|
||||
meta.members = getObjectMembers(value)
|
||||
meta.proto = getObjectPrototype(value)
|
||||
} else if (meta.type === 'buffer') {
|
||||
@@ -101,7 +101,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
// Instead they should appear in the renderer process
|
||||
value.then(function () {}, function () {})
|
||||
|
||||
meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
|
||||
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
|
||||
value.then(onFulfilled, onRejected)
|
||||
})
|
||||
} else if (meta.type === 'error') {
|
||||
@@ -132,12 +132,12 @@ const plainObjectToMeta = function (obj) {
|
||||
}
|
||||
|
||||
// Convert Error into meta data.
|
||||
const exceptionToMeta = function (sender, error) {
|
||||
const exceptionToMeta = function (sender, contextId, error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
message: error.message,
|
||||
stack: error.stack || error,
|
||||
cause: valueToMeta(sender, error.cause)
|
||||
cause: valueToMeta(sender, contextId, error.cause)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ const removeRemoteListenersAndLogWarning = (sender, meta, callIntoRenderer) => {
|
||||
}
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
const unwrapArgs = function (sender, args) {
|
||||
const unwrapArgs = function (sender, contextId, args) {
|
||||
const metaToValue = function (meta) {
|
||||
let i, len, member, ref, returnValue
|
||||
switch (meta.type) {
|
||||
@@ -178,7 +178,7 @@ const unwrapArgs = function (sender, args) {
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id)
|
||||
case 'array':
|
||||
return unwrapArgs(sender, meta.value)
|
||||
return unwrapArgs(sender, contextId, meta.value)
|
||||
case 'buffer':
|
||||
return bufferUtils.metaToBuffer(meta.value)
|
||||
case 'date':
|
||||
@@ -204,26 +204,26 @@ const unwrapArgs = function (sender, args) {
|
||||
return returnValue
|
||||
}
|
||||
case 'function': {
|
||||
// Merge webContentsId and meta.id, since meta.id can be the same in
|
||||
// Merge contextId and meta.id, since meta.id can be the same in
|
||||
// different webContents.
|
||||
const webContentsId = sender.getId()
|
||||
const objectId = [webContentsId, meta.id]
|
||||
const objectId = [contextId, meta.id]
|
||||
|
||||
// Cache the callbacks in renderer.
|
||||
if (rendererFunctions.has(objectId)) {
|
||||
return rendererFunctions.get(objectId)
|
||||
}
|
||||
|
||||
const webContentsId = sender.getId()
|
||||
let callIntoRenderer = function (...args) {
|
||||
if (!sender.isDestroyed() && webContentsId === sender.getId()) {
|
||||
sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args))
|
||||
sender.send('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||
} else {
|
||||
removeRemoteListenersAndLogWarning(this, meta, callIntoRenderer)
|
||||
}
|
||||
}
|
||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
|
||||
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender)
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender)
|
||||
rendererFunctions.set(objectId, callIntoRenderer)
|
||||
return callIntoRenderer
|
||||
}
|
||||
@@ -236,19 +236,19 @@ const unwrapArgs = function (sender, args) {
|
||||
|
||||
// Call a function and send reply asynchronously if it's a an asynchronous
|
||||
// style function and the caller didn't pass a callback.
|
||||
const callFunction = function (event, func, caller, args) {
|
||||
const callFunction = function (event, contextId, func, caller, args) {
|
||||
let err, funcMarkedAsync, funcName, funcPassedCallback, ref, ret
|
||||
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
||||
funcPassedCallback = typeof args[args.length - 1] === 'function'
|
||||
try {
|
||||
if (funcMarkedAsync && !funcPassedCallback) {
|
||||
args.push(function (ret) {
|
||||
event.returnValue = valueToMeta(event.sender, ret, true)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, ret, true)
|
||||
})
|
||||
func.apply(caller, args)
|
||||
} else {
|
||||
ret = func.apply(caller, args)
|
||||
event.returnValue = valueToMeta(event.sender, ret, true)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, ret, true)
|
||||
}
|
||||
} catch (error) {
|
||||
// Catch functions thrown further down in function invocation and wrap
|
||||
@@ -261,45 +261,45 @@ const callFunction = function (event, func, caller, args) {
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
|
||||
ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, contextId, module) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
|
||||
event.returnValue = valueToMeta(event.sender, contextId, process.mainModule.require(module))
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
|
||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, module) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, electron[module])
|
||||
event.returnValue = valueToMeta(event.sender, contextId, electron[module])
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
|
||||
ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, contextId, name) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, global[name])
|
||||
event.returnValue = valueToMeta(event.sender, contextId, global[name])
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
|
||||
event.returnValue = valueToMeta(event.sender, contextId, event.sender.getOwnerBrowserWindow())
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) {
|
||||
event.returnValue = valueToMeta(event.sender, event.sender)
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
|
||||
event.returnValue = valueToMeta(event.sender, contextId, event.sender)
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let constructor = objectsRegistry.get(id)
|
||||
|
||||
if (constructor == null) {
|
||||
@@ -309,30 +309,30 @@ ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
|
||||
// Call new with array of arguments.
|
||||
// http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
|
||||
event.returnValue = valueToMeta(event.sender, obj)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, obj)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let func = objectsRegistry.get(id)
|
||||
|
||||
if (func == null) {
|
||||
throwRPCError(`Cannot call function on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
callFunction(event, func, global, args)
|
||||
callFunction(event, contextId, func, global, args)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
@@ -342,30 +342,30 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, a
|
||||
// Call new with array of arguments.
|
||||
let constructor = object[method]
|
||||
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
|
||||
event.returnValue = valueToMeta(event.sender, obj)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, obj)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
callFunction(event, obj[method], obj, args)
|
||||
callFunction(event, contextId, obj[method], obj, args)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
@@ -375,11 +375,11 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
||||
obj[name] = args[0]
|
||||
event.returnValue = null
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
|
||||
try {
|
||||
let obj = objectsRegistry.get(id)
|
||||
|
||||
@@ -387,14 +387,14 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
|
||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
event.returnValue = valueToMeta(event.sender, obj[name])
|
||||
event.returnValue = valueToMeta(event.sender, contextId, obj[name])
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) {
|
||||
objectsRegistry.remove(event.sender.getId(), id)
|
||||
ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id) {
|
||||
objectsRegistry.remove(contextId, id)
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
||||
@@ -402,16 +402,16 @@ ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
||||
e.returnValue = null
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) {
|
||||
ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
|
||||
try {
|
||||
let guestViewManager = require('./guest-view-manager')
|
||||
event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
|
||||
event.returnValue = valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, contextId, requestId, guestInstanceId, method, ...args) {
|
||||
try {
|
||||
let guestViewManager = require('./guest-view-manager')
|
||||
let guest = guestViewManager.getGuest(guestInstanceId)
|
||||
@@ -423,7 +423,7 @@ ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, request
|
||||
}
|
||||
guest[method].apply(guest, args)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -9,6 +9,18 @@ const bufferUtils = require('../../common/buffer-utils')
|
||||
const callbacksRegistry = new CallbacksRegistry()
|
||||
const remoteObjectCache = v8Util.createIDWeakMap()
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getContextId()
|
||||
|
||||
// Notify the main process when current context is going to be released.
|
||||
// Note that when the renderer process is destroyed, the message may not be
|
||||
// sent, we also listen to the "render-view-deleted" event in the main process
|
||||
// to guard that situation.
|
||||
process.on('exit', () => {
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||
ipcRenderer.sendSync(command, contextId)
|
||||
})
|
||||
|
||||
// Convert the arguments object into an array of meta data.
|
||||
function wrapArgs (args, visited = new Set()) {
|
||||
const valueToMeta = (value) => {
|
||||
@@ -107,7 +119,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||
} else {
|
||||
command = 'ELECTRON_BROWSER_MEMBER_CALL'
|
||||
}
|
||||
const ret = ipcRenderer.sendSync(command, metaId, member.name, wrapArgs(args))
|
||||
const ret = ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
|
||||
return metaToValue(ret)
|
||||
}
|
||||
|
||||
@@ -126,7 +138,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||
} else if (member.type === 'get') {
|
||||
descriptor.get = () => {
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||
const meta = ipcRenderer.sendSync(command, metaId, member.name)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
@@ -134,7 +146,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||
descriptor.set = (value) => {
|
||||
const args = wrapArgs([value])
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_SET'
|
||||
const meta = ipcRenderer.sendSync(command, metaId, member.name, args)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name, args)
|
||||
if (meta != null) metaToValue(meta)
|
||||
return value
|
||||
}
|
||||
@@ -164,7 +176,7 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
|
||||
if (loaded) return
|
||||
loaded = true
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||
const meta = ipcRenderer.sendSync(command, metaId, name)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, metaId, name)
|
||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
||||
}
|
||||
|
||||
@@ -224,7 +236,7 @@ function metaToValue (meta) {
|
||||
} else {
|
||||
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
|
||||
}
|
||||
const obj = ipcRenderer.sendSync(command, meta.id, wrapArgs(args))
|
||||
const obj = ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args))
|
||||
return metaToValue(obj)
|
||||
}
|
||||
ret = remoteFunction
|
||||
@@ -237,7 +249,7 @@ function metaToValue (meta) {
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||
|
||||
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
||||
v8Util.setRemoteObjectFreer(ret, meta.id)
|
||||
v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
|
||||
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
||||
remoteObjectCache.set(meta.id, ret)
|
||||
return ret
|
||||
@@ -264,60 +276,51 @@ function metaToException (meta) {
|
||||
}
|
||||
|
||||
// Browser calls a callback in renderer.
|
||||
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => {
|
||||
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, passedContextId, id, args) => {
|
||||
if (passedContextId !== contextId) {
|
||||
// The invoked callback belongs to an old page in this renderer.
|
||||
return
|
||||
}
|
||||
callbacksRegistry.apply(id, metaToValue(args))
|
||||
})
|
||||
|
||||
// A callback in browser is released.
|
||||
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, id) => {
|
||||
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, passedContextId, id) => {
|
||||
if (passedContextId !== contextId) {
|
||||
// The freed callback belongs to an old page in this renderer.
|
||||
return
|
||||
}
|
||||
callbacksRegistry.remove(id)
|
||||
})
|
||||
|
||||
process.on('exit', () => {
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||
ipcRenderer.sendSync(command, initialContext)
|
||||
})
|
||||
|
||||
exports.require = (module) => {
|
||||
const command = 'ELECTRON_BROWSER_REQUIRE'
|
||||
const meta = ipcRenderer.sendSync(command, module)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, module)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
// Alias to remote.require('electron').xxx.
|
||||
exports.getBuiltin = (module) => {
|
||||
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
|
||||
const meta = ipcRenderer.sendSync(command, module)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, module)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
exports.getCurrentWindow = () => {
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
|
||||
const meta = ipcRenderer.sendSync(command)
|
||||
const meta = ipcRenderer.sendSync(command, contextId)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
// Get current WebContents object.
|
||||
exports.getCurrentWebContents = () => {
|
||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
|
||||
}
|
||||
|
||||
const CONTEXT_ARG = '--context-id='
|
||||
let initialContext = process.argv.find(arg => arg.startsWith(CONTEXT_ARG))
|
||||
if (process.webContentsId) {
|
||||
// set by sandbox renderer init script
|
||||
initialContext = process.webContentsId
|
||||
} else if (initialContext) {
|
||||
initialContext = parseInt(initialContext.substr(CONTEXT_ARG.length), 10)
|
||||
} else {
|
||||
// if not available, pull from remote
|
||||
initialContext = exports.getCurrentWebContents().getId()
|
||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId))
|
||||
}
|
||||
|
||||
// Get a global object in browser.
|
||||
exports.getGlobal = (name) => {
|
||||
const command = 'ELECTRON_BROWSER_GLOBAL'
|
||||
const meta = ipcRenderer.sendSync(command, name)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, name)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
@@ -334,7 +337,7 @@ exports.createFunctionWithReturnValue = (returnValue) => {
|
||||
// Get the guest WebContents from guestInstanceId.
|
||||
exports.getGuestWebContents = (guestInstanceId) => {
|
||||
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
|
||||
const meta = ipcRenderer.sendSync(command, guestInstanceId)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, guestInstanceId)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ const webViewConstants = require('./web-view-constants')
|
||||
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getContextId()
|
||||
|
||||
// ID generator.
|
||||
let nextId = 0
|
||||
|
||||
@@ -396,7 +399,7 @@ const registerWebViewElement = function () {
|
||||
const createNonBlockHandler = function (m) {
|
||||
return function (...args) {
|
||||
const internal = v8Util.getHiddenValue(this, 'internal')
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', null, internal.guestInstanceId, m, ...args)
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', contextId, null, internal.guestInstanceId, m, ...args)
|
||||
}
|
||||
}
|
||||
for (const method of nonblockMethods) {
|
||||
@@ -410,7 +413,7 @@ const registerWebViewElement = function () {
|
||||
hasUserGesture = false
|
||||
}
|
||||
const requestId = getNextId()
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', contextId, requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
|
||||
ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, result) {
|
||||
if (callback) callback(result)
|
||||
})
|
||||
|
||||
@@ -78,6 +78,16 @@ if (window.location.protocol === 'chrome-devtools:') {
|
||||
require('../renderer/inspector')
|
||||
}
|
||||
|
||||
if (binding.guestInstanceId) {
|
||||
process.guestInstanceId = parseInt(binding.guestInstanceId)
|
||||
}
|
||||
|
||||
if (!process.guestInstanceId && preloadProcess.argv.indexOf('--webview-tag=true') !== -1) {
|
||||
// don't allow recursive `<webview>`
|
||||
require('../renderer/web-view/web-view')
|
||||
require('../renderer/web-view/web-view-attributes')
|
||||
}
|
||||
|
||||
// Wrap the script into a function executed in global scope. It won't have
|
||||
// access to the current scope, so we'll expose a few objects as arguments:
|
||||
//
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "3.0.0-beta.2",
|
||||
"version": "3.0.0-beta.3",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -120,7 +120,7 @@ async function callAppVeyor (targetBranch, job, options) {
|
||||
let appVeyorResponse = await makeRequest(requestOpts, true).catch(err => {
|
||||
console.log('Error calling AppVeyor:', err)
|
||||
})
|
||||
const buildUrl = `https://windows-ci.electronjs.org/project/AppVeyor/electron/build/${appVeyorResponse.version}`
|
||||
const buildUrl = `https://windows-ci.electronjs.org/project/AppVeyor/${appVeyorJobs[job]}/build/${appVeyorResponse.version}`
|
||||
console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ const os = require('os')
|
||||
const qs = require('querystring')
|
||||
const http = require('http')
|
||||
const {closeWindow} = require('./window-helpers')
|
||||
|
||||
const {emittedOnce} = require('./events-helpers')
|
||||
const {ipcRenderer, remote, screen} = require('electron')
|
||||
const {app, ipcMain, BrowserWindow, BrowserView, protocol, session, webContents} = remote
|
||||
|
||||
@@ -839,6 +839,14 @@ describe('BrowserWindow module', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('BrowserWindow.openDevTools()', () => {
|
||||
it('does not crash for frameless window', () => {
|
||||
w.destroy()
|
||||
w = new BrowserWindow({ show: false })
|
||||
w.openDevTools()
|
||||
})
|
||||
})
|
||||
|
||||
describe('BrowserWindow.fromBrowserView(browserView)', () => {
|
||||
let bv = null
|
||||
|
||||
@@ -1573,6 +1581,23 @@ describe('BrowserWindow module', () => {
|
||||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'preload.html'))
|
||||
})
|
||||
|
||||
it('webview in sandbox renderer', async () => {
|
||||
w.destroy()
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
sandbox: true,
|
||||
preload: preload,
|
||||
webviewTag: true
|
||||
}
|
||||
})
|
||||
w.loadURL(`file://${fixtures}/pages/webview-did-attach-event.html`)
|
||||
|
||||
const [, webContents] = await emittedOnce(w.webContents, 'did-attach-webview')
|
||||
const [, id] = await emittedOnce(ipcMain, 'webview-dom-ready')
|
||||
expect(webContents.id).to.equal(id)
|
||||
})
|
||||
})
|
||||
|
||||
describe('nativeWindowOpen option', () => {
|
||||
|
||||
@@ -69,7 +69,11 @@ describe('desktopCapturer', () => {
|
||||
return done()
|
||||
}
|
||||
|
||||
const { BrowserWindow } = remote
|
||||
const w = new BrowserWindow({ width: 200, height: 200 })
|
||||
|
||||
desktopCapturer.getSources({types: ['window']}, (error, sources) => {
|
||||
w.destroy()
|
||||
expect(error).to.be.null()
|
||||
expect(sources).to.be.an('array').that.is.not.empty()
|
||||
for (const {display_id: displayId} of sources) {
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
const assert = require('assert')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const path = require('path')
|
||||
const {closeWindow} = require('./window-helpers')
|
||||
const {remote, webFrame} = require('electron')
|
||||
const {BrowserWindow, protocol, ipcMain} = remote
|
||||
const {emittedOnce} = require('./events-helpers')
|
||||
|
||||
const {expect} = chai
|
||||
chai.use(dirtyChai)
|
||||
|
||||
/* Most of the APIs here don't use standard callbacks */
|
||||
/* eslint-disable standard/no-callback-literal */
|
||||
@@ -138,4 +144,18 @@ describe('webFrame module', function () {
|
||||
webFrame.setLayoutZoomLevelLimits(0, 25)
|
||||
})
|
||||
})
|
||||
|
||||
it('calls a spellcheck provider', async () => {
|
||||
w = new BrowserWindow({show: false})
|
||||
w.loadURL(`file://${fixtures}/pages/webframe-spell-check.html`)
|
||||
await emittedOnce(w.webContents, 'did-finish-load')
|
||||
|
||||
const spellCheckerFeedback = emittedOnce(ipcMain, 'spec-spell-check')
|
||||
const misspelledWord = 'spleling'
|
||||
for (const keyCode of [...misspelledWord, ' ']) {
|
||||
w.webContents.sendInputEvent({type: 'char', keyCode})
|
||||
}
|
||||
const [, text] = await spellCheckerFeedback
|
||||
expect(text).to.equal(misspelledWord)
|
||||
})
|
||||
})
|
||||
|
||||
13
spec/fixtures/pages/webframe-spell-check.html
vendored
Normal file
13
spec/fixtures/pages/webframe-spell-check.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const {ipcRenderer, webFrame} = require('electron')
|
||||
webFrame.setSpellCheckProvider('en-US', true, {
|
||||
spellCheck: text => {
|
||||
ipcRenderer.send('spec-spell-check', text)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<input autofocus />
|
||||
</body>
|
||||
</html>
|
||||
28
vsts.yml
28
vsts.yml
@@ -54,6 +54,7 @@ steps:
|
||||
script/test.py --ci --rebuild_native_modules
|
||||
fi
|
||||
name: Test
|
||||
condition: or(ne(variables['ELECTRON_RELEASE'], '1'), eq(variables['UPLOAD_TO_S3'], '1'))
|
||||
|
||||
- bash: |
|
||||
echo 'Verifying ffmpeg on build'
|
||||
@@ -63,6 +64,7 @@ steps:
|
||||
script/verify-ffmpeg.py
|
||||
fi
|
||||
name: Verify_FFmpeg
|
||||
condition: or(ne(variables['ELECTRON_RELEASE'], '1'), eq(variables['UPLOAD_TO_S3'], '1'))
|
||||
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish Test Results
|
||||
@@ -71,12 +73,26 @@ steps:
|
||||
searchFolder: junit
|
||||
condition: and(always(), ne(variables['ELECTRON_RELEASE'], '1'))
|
||||
|
||||
- task: kasunkodagoda.slack-notification.slack-notification-task.SlackNotification@3
|
||||
displayName: Post Slack Notification
|
||||
- bash: |
|
||||
export BUILD_URL="${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}${SYSTEM_TEAMPROJECT}/_build/results?buildId=${BUILD_BUILDID}"
|
||||
export MESSAGE="Build failed for *<$BUILD_URL|$BUILD_DEFINITIONNAME>* nightly build."
|
||||
curl -g -H "Content-Type: application/json" -X POST \
|
||||
-d "{\"text\": \"$MESSAGE\", \"attachments\": [{\"color\": \"#FC5C3C\",\"title\": \"$BUILD_DEFINITIONNAME nightly build results\",\"title_link\": \"$BUILD_URL\"}]}" $(slack_webhook)
|
||||
name: Post_Slack_Notification_on_Failure
|
||||
condition: failed()
|
||||
|
||||
- bash: |
|
||||
export BUILD_URL="${SYSTEM_TEAMFOUNDATIONCOLLECTIONURI}${SYSTEM_TEAMPROJECT}/_build/results?buildId=${BUILD_BUILDID}"
|
||||
export MESSAGE="Build succeeded for *<$BUILD_URL|$BUILD_DEFINITIONNAME>* nightly build."
|
||||
curl -g -H "Content-Type: application/json" -X POST \
|
||||
-d "{\"text\": \"$MESSAGE\", \"attachments\": [{\"color\": \"#FC5C3C\",\"title\": \"$BUILD_DEFINITIONNAME nightly build results\",\"title_link\": \"$BUILD_URL\"}]}" $(slack_webhook)
|
||||
name: Post_Slack_Notification_on_Success
|
||||
condition: succeeded()
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish Build Artifacts
|
||||
inputs:
|
||||
SlackApiToken: '$(slack_token)'
|
||||
Channel: '#bot-nightly-releases'
|
||||
Message: '$(Build.DefinitionName)-$(Build.BuildNumber) finished with a $(Agent.JobStatus) status.'
|
||||
condition: and(always(), eq(variables['Build.Reason'], 'Schedule'))
|
||||
PathtoPublish: '$(Build.SourcesDirectory)/out'
|
||||
ArtifactName: out
|
||||
|
||||
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
|
||||
|
||||
Reference in New Issue
Block a user