Scaffold EVE fullscreen OpenGL app
This commit is contained in:
commit
c6c73d60c0
15 changed files with 494 additions and 0 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.theos/
|
||||
packages/
|
||||
obj/
|
||||
*.deb
|
||||
*.ipa
|
||||
*.dSYM/
|
||||
22
Makefile
Normal file
22
Makefile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
ARCHS = arm64
|
||||
TARGET = iphone:clang:latest:14.0
|
||||
|
||||
include $(THEOS)/makefiles/common.mk
|
||||
|
||||
APPLICATION_NAME = EveCanvas
|
||||
|
||||
EveCanvas_FILES = \
|
||||
Sources/main.m \
|
||||
Sources/EVEAppDelegate.m \
|
||||
Sources/EVEViewController.mm \
|
||||
Sources/EVEGLView.mm
|
||||
|
||||
EveCanvas_FRAMEWORKS = UIKit QuartzCore OpenGLES CoreMotion
|
||||
EveCanvas_CFLAGS = -fobjc-arc -Wall -Wextra
|
||||
EveCanvas_CODESIGN_FLAGS = -Sentitlements.plist
|
||||
EveCanvas_INSTALL_PATH = /Applications
|
||||
|
||||
include $(THEOS_MAKE_PATH)/application.mk
|
||||
|
||||
after-install::
|
||||
install.exec "uicache -p /Applications/EveCanvas.app || uicache"
|
||||
77
README.md
Normal file
77
README.md
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# EveCanvas
|
||||
|
||||
Fullscreen native render shell for EVE, the jailbroken iPad 8th generation test
|
||||
device.
|
||||
|
||||
This is deliberately not a web app. UIKit owns the app/window lifecycle and the
|
||||
text overlay. OpenGL ES 3.0 owns the render surface.
|
||||
|
||||
## Objective
|
||||
|
||||
Put a real fullscreen native rendering surface on EVE without requiring macOS as
|
||||
the development machine.
|
||||
|
||||
## Current Mechanism
|
||||
|
||||
- `EVEAppDelegate` creates one fullscreen `UIWindow`.
|
||||
- `EVEViewController` installs:
|
||||
- `EVEGLView`, a `CAEAGLLayer` backed OpenGL ES 3.0 render target.
|
||||
- a native UIKit `UILabel` overlay for crisp Retina text.
|
||||
- `CADisplayLink` for frame ticking.
|
||||
- `CMMotionManager` for accelerometer and gyro telemetry.
|
||||
- `EVEGLView` clears the full drawable with a slow color pulse and presents the
|
||||
renderbuffer every frame.
|
||||
|
||||
## Invariants
|
||||
|
||||
- Browser/PWA layout is not part of this path.
|
||||
- UIKit is the text owner; OpenGL ES is the pixel owner.
|
||||
- The status bar stays hidden.
|
||||
- Sensor reads are display-only telemetry until a later input model owns them.
|
||||
- CultMesh networking is not smuggled into the render shell yet.
|
||||
|
||||
## Build Shape
|
||||
|
||||
The project is a Theos-style iOS application:
|
||||
|
||||
```sh
|
||||
make package
|
||||
make install
|
||||
```
|
||||
|
||||
EVE currently has SSH and jailbreak package management, but the native build
|
||||
toolchain still needs to be installed. In particular, a Theos install and an
|
||||
iPhoneOS SDK are required before `make package` can work.
|
||||
|
||||
Expected EVE target:
|
||||
|
||||
```text
|
||||
ssh alias: eve
|
||||
device: iPad11,6 / J171aAP
|
||||
iPadOS: 14.4
|
||||
install path: /Applications/EveCanvas.app
|
||||
```
|
||||
|
||||
## First Deployment Plan
|
||||
|
||||
1. Install or stage Theos on EVE.
|
||||
2. Stage a compatible iPhoneOS SDK under `$THEOS/sdks`.
|
||||
3. Copy this project to EVE, for example `/var/mobile/Projects/Eve`.
|
||||
4. Build on EVE:
|
||||
|
||||
```sh
|
||||
cd /var/mobile/Projects/Eve
|
||||
export THEOS=/var/theos
|
||||
make package
|
||||
make install
|
||||
uicache -p /Applications/EveCanvas.app
|
||||
```
|
||||
|
||||
5. Launch `EveCanvas` from SpringBoard.
|
||||
|
||||
## Next Cut
|
||||
|
||||
- Confirm actual fullscreen drawable size on EVE.
|
||||
- Add a simple triangle/quad shader so the renderer proves more than clear.
|
||||
- Add touch/Pencil visual markers.
|
||||
- Add a local telemetry bridge once the shell is stable.
|
||||
42
Resources/Info.plist
Normal file
42
Resources/Info.plist
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>EveCanvas</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>EveCanvas</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.gamecult.evecanvas</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>EveCanvas</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
7
Sources/EVEAppDelegate.h
Normal file
7
Sources/EVEAppDelegate.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface EVEAppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property(nonatomic, strong) UIWindow *window;
|
||||
|
||||
@end
|
||||
18
Sources/EVEAppDelegate.m
Normal file
18
Sources/EVEAppDelegate.m
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#import "EVEAppDelegate.h"
|
||||
|
||||
#import "EVEViewController.h"
|
||||
|
||||
@implementation EVEAppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
(void)application;
|
||||
(void)launchOptions;
|
||||
|
||||
self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
|
||||
self.window.rootViewController = [[EVEViewController alloc] init];
|
||||
self.window.backgroundColor = UIColor.blackColor;
|
||||
[self.window makeKeyAndVisible];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
8
Sources/EVEGLView.h
Normal file
8
Sources/EVEGLView.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface EVEGLView : UIView
|
||||
|
||||
- (void)resizeDrawableIfNeeded;
|
||||
- (void)renderAtTime:(NSTimeInterval)time;
|
||||
|
||||
@end
|
||||
95
Sources/EVEGLView.mm
Normal file
95
Sources/EVEGLView.mm
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
#import "EVEGLView.h"
|
||||
|
||||
#import <math.h>
|
||||
#import <OpenGLES/ES3/gl.h>
|
||||
#import <QuartzCore/CAEAGLLayer.h>
|
||||
|
||||
@interface EVEGLView ()
|
||||
|
||||
@property(nonatomic, strong) EAGLContext *context;
|
||||
@property(nonatomic, assign) GLuint framebuffer;
|
||||
@property(nonatomic, assign) GLuint colorRenderbuffer;
|
||||
@property(nonatomic, assign) GLint drawableWidth;
|
||||
@property(nonatomic, assign) GLint drawableHeight;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EVEGLView
|
||||
|
||||
+ (Class)layerClass {
|
||||
return CAEAGLLayer.class;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
if (!self) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
self.contentScaleFactor = UIScreen.mainScreen.scale;
|
||||
self.opaque = YES;
|
||||
self.multipleTouchEnabled = YES;
|
||||
|
||||
CAEAGLLayer *layer = (CAEAGLLayer *)self.layer;
|
||||
layer.opaque = YES;
|
||||
layer.drawableProperties = @{
|
||||
kEAGLDrawablePropertyRetainedBacking: @NO,
|
||||
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8,
|
||||
};
|
||||
|
||||
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
|
||||
NSAssert(self.context != nil, @"EVE requires OpenGL ES 3.0.");
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
|
||||
glGenFramebuffers(1, &_framebuffer);
|
||||
glGenRenderbuffers(1, &_colorRenderbuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self.framebuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self.colorRenderbuffer);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.colorRenderbuffer);
|
||||
|
||||
[self resizeDrawableIfNeeded];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
if (_colorRenderbuffer) {
|
||||
glDeleteRenderbuffers(1, &_colorRenderbuffer);
|
||||
}
|
||||
if (_framebuffer) {
|
||||
glDeleteFramebuffers(1, &_framebuffer);
|
||||
}
|
||||
[EAGLContext setCurrentContext:nil];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews {
|
||||
[super layoutSubviews];
|
||||
[self resizeDrawableIfNeeded];
|
||||
}
|
||||
|
||||
- (void)resizeDrawableIfNeeded {
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self.colorRenderbuffer);
|
||||
[self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_drawableWidth);
|
||||
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_drawableHeight);
|
||||
glViewport(0, 0, self.drawableWidth, self.drawableHeight);
|
||||
}
|
||||
|
||||
- (void)renderAtTime:(NSTimeInterval)time {
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self.framebuffer);
|
||||
glViewport(0, 0, self.drawableWidth, self.drawableHeight);
|
||||
|
||||
float pulse = (float)((sin(time * 0.8) + 1.0) * 0.5);
|
||||
float green = 0.08f + pulse * 0.12f;
|
||||
float blue = 0.13f + pulse * 0.18f;
|
||||
|
||||
glClearColor(0.02f, green, blue, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, self.colorRenderbuffer);
|
||||
[self.context presentRenderbuffer:GL_RENDERBUFFER];
|
||||
}
|
||||
|
||||
@end
|
||||
5
Sources/EVEViewController.h
Normal file
5
Sources/EVEViewController.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface EVEViewController : UIViewController
|
||||
|
||||
@end
|
||||
148
Sources/EVEViewController.mm
Normal file
148
Sources/EVEViewController.mm
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
#import "EVEViewController.h"
|
||||
|
||||
#import <CoreMotion/CoreMotion.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import "EVEGLView.h"
|
||||
|
||||
@interface EVEViewController ()
|
||||
|
||||
@property(nonatomic, strong) EVEGLView *glView;
|
||||
@property(nonatomic, strong) UILabel *overlayLabel;
|
||||
@property(nonatomic, strong) CADisplayLink *displayLink;
|
||||
@property(nonatomic, strong) CMMotionManager *motionManager;
|
||||
@property(nonatomic, assign) NSTimeInterval previousTimestamp;
|
||||
@property(nonatomic, assign) double filteredFPS;
|
||||
@property(nonatomic, assign) NSUInteger touchCount;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EVEViewController
|
||||
|
||||
- (void)loadView {
|
||||
UIView *root = [[UIView alloc] initWithFrame:UIScreen.mainScreen.bounds];
|
||||
root.backgroundColor = UIColor.blackColor;
|
||||
root.multipleTouchEnabled = YES;
|
||||
|
||||
self.glView = [[EVEGLView alloc] initWithFrame:root.bounds];
|
||||
self.glView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[root addSubview:self.glView];
|
||||
|
||||
self.overlayLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
self.overlayLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.overlayLabel.numberOfLines = 0;
|
||||
self.overlayLabel.textColor = UIColor.whiteColor;
|
||||
self.overlayLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.45];
|
||||
self.overlayLabel.font = [UIFont monospacedSystemFontOfSize:13.0 weight:UIFontWeightRegular];
|
||||
self.overlayLabel.layer.cornerRadius = 6.0;
|
||||
self.overlayLabel.layer.masksToBounds = YES;
|
||||
[root addSubview:self.overlayLabel];
|
||||
|
||||
UILayoutGuide *safe = root.safeAreaLayoutGuide;
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[self.overlayLabel.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor constant:12.0],
|
||||
[self.overlayLabel.topAnchor constraintEqualToAnchor:safe.topAnchor constant:12.0],
|
||||
[self.overlayLabel.widthAnchor constraintLessThanOrEqualToAnchor:safe.widthAnchor multiplier:0.92],
|
||||
]];
|
||||
|
||||
self.view = root;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.motionManager = [[CMMotionManager alloc] init];
|
||||
self.motionManager.accelerometerUpdateInterval = 1.0 / 30.0;
|
||||
self.motionManager.gyroUpdateInterval = 1.0 / 30.0;
|
||||
if (self.motionManager.accelerometerAvailable) {
|
||||
[self.motionManager startAccelerometerUpdates];
|
||||
}
|
||||
if (self.motionManager.gyroAvailable) {
|
||||
[self.motionManager startGyroUpdates];
|
||||
}
|
||||
|
||||
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(frameTick:)];
|
||||
[self.displayLink addToRunLoop:NSRunLoop.mainRunLoop forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self.displayLink invalidate];
|
||||
[self.motionManager stopAccelerometerUpdates];
|
||||
[self.motionManager stopGyroUpdates];
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
|
||||
return UIRectEdgeAll;
|
||||
}
|
||||
|
||||
- (BOOL)prefersHomeIndicatorAutoHidden {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
[self.glView resizeDrawableIfNeeded];
|
||||
}
|
||||
|
||||
- (void)frameTick:(CADisplayLink *)link {
|
||||
if (self.previousTimestamp > 0) {
|
||||
NSTimeInterval delta = link.timestamp - self.previousTimestamp;
|
||||
if (delta > 0) {
|
||||
double instantFPS = 1.0 / delta;
|
||||
self.filteredFPS = self.filteredFPS == 0 ? instantFPS : (self.filteredFPS * 0.9 + instantFPS * 0.1);
|
||||
}
|
||||
}
|
||||
self.previousTimestamp = link.timestamp;
|
||||
|
||||
[self.glView renderAtTime:link.timestamp];
|
||||
[self updateOverlay];
|
||||
}
|
||||
|
||||
- (void)updateOverlay {
|
||||
UIScreen *screen = UIScreen.mainScreen;
|
||||
CGSize points = screen.bounds.size;
|
||||
CGFloat scale = screen.scale;
|
||||
CGSize pixels = CGSizeMake(points.width * scale, points.height * scale);
|
||||
|
||||
CMAcceleration acceleration = self.motionManager.accelerometerData.acceleration;
|
||||
CMRotationRate rotation = self.motionManager.gyroData.rotationRate;
|
||||
|
||||
self.overlayLabel.text = [NSString stringWithFormat:
|
||||
@"EVE Canvas\n"
|
||||
"OpenGL ES 3.0 + UIKit text\n"
|
||||
"points %.0fx%.0f pixels %.0fx%.0f @ %.1fx\n"
|
||||
"fps %.1f touches %lu\n"
|
||||
"accel %+0.2f %+0.2f %+0.2f\n"
|
||||
"gyro %+0.2f %+0.2f %+0.2f",
|
||||
points.width, points.height, pixels.width, pixels.height, scale,
|
||||
self.filteredFPS, (unsigned long)self.touchCount,
|
||||
acceleration.x, acceleration.y, acceleration.z,
|
||||
rotation.x, rotation.y, rotation.z];
|
||||
[self.overlayLabel sizeToFit];
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
(void)touches;
|
||||
self.touchCount = event.allTouches.count;
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
(void)touches;
|
||||
self.touchCount = event.allTouches.count;
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
(void)touches;
|
||||
self.touchCount = event.allTouches.count;
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||||
(void)touches;
|
||||
self.touchCount = event.allTouches.count;
|
||||
}
|
||||
|
||||
@end
|
||||
9
Sources/main.m
Normal file
9
Sources/main.m
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "EVEAppDelegate.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([EVEAppDelegate class]));
|
||||
}
|
||||
}
|
||||
8
control
Normal file
8
control
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Package: org.gamecult.evecanvas
|
||||
Name: EveCanvas
|
||||
Version: 0.1.0
|
||||
Architecture: iphoneos-arm
|
||||
Description: Fullscreen OpenGL ES 3.0 render shell for EVE.
|
||||
Maintainer: GameCult <meta@gamecult.org>
|
||||
Author: GameCult
|
||||
Section: Applications
|
||||
11
entitlements.plist
Normal file
11
entitlements.plist
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>platform-application</key>
|
||||
<true/>
|
||||
<key>com.apple.private.security.no-container</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
18
scripts/check-eve-toolchain.ps1
Normal file
18
scripts/check-eve-toolchain.ps1
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
param(
|
||||
[string] $Target = "eve"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$remoteScript = "echo '== identity =='; " +
|
||||
"uname -a; " +
|
||||
"sysctl -n hw.machine kern.osversion 2>/dev/null || true; " +
|
||||
"echo '== command availability =='; " +
|
||||
"for c in make clang ldid git uicache dpkg apt; do " +
|
||||
"if command -v `$c >/dev/null 2>&1; then printf '%-8s %s\n' `$c `$(command -v `$c); else printf '%-8s missing\n' `$c; fi; " +
|
||||
"done; " +
|
||||
"echo '== theos =='; " +
|
||||
"printf 'THEOS=%s\n' `"`${THEOS:-}`"; " +
|
||||
"for d in /var/theos /opt/theos `"`$HOME/theos`"; do test -d `"`$d`" && find `"`$d`" -maxdepth 2 -type d | head -20; done"
|
||||
|
||||
ssh $Target $remoteScript
|
||||
20
scripts/stage-to-eve.ps1
Normal file
20
scripts/stage-to-eve.ps1
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
param(
|
||||
[string] $Target = "eve",
|
||||
[string] $RemotePath = "/var/mobile/Projects/Eve"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
||||
$archive = Join-Path $env:TEMP "eve-canvas-source.tar"
|
||||
|
||||
if (Test-Path $archive) {
|
||||
Remove-Item -LiteralPath $archive -Force
|
||||
}
|
||||
|
||||
tar -cf $archive -C $projectRoot .
|
||||
ssh $Target "mkdir -p '$RemotePath'"
|
||||
scp $archive "${Target}:/tmp/eve-canvas-source.tar"
|
||||
ssh $Target "tar -xf /tmp/eve-canvas-source.tar -C '$RemotePath' && rm /tmp/eve-canvas-source.tar && find '$RemotePath' -maxdepth 2 -type f | sort"
|
||||
|
||||
Write-Host "Staged EveCanvas to ${Target}:$RemotePath"
|
||||
Loading…
Reference in a new issue