我可以恢复已上传到 Google AppEngine 的源代码吗?

发布于 2024-08-26 01:07:22 字数 69 浏览 4 评论 0 原文

我最近硬盘崩溃并丢失了所有源代码。是否可以提取/签出我已经上传到 Google App Engine 的代码(如最新版本)?

I recently had a hard drive crashed and lost all of my source code. Is it possible to pull/checkout the code that I have already uploaded to Google App Engine (like the most recent version)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

聽兲甴掵 2024-09-02 01:07:23

截至 2020 年 10 月更新。

当前版本的 Google App Engine SDK 仍包含 appcfg.py 脚本,但是当尝试从您的网站下载文件时,脚本会尝试将它们下载到根文件夹中您的系统的。

示例:

/images/some_site_image.png

这可能与您的文件可能包含的 appengine 中的更改有关
以前位于相对目录中,但新版本中不再存在
系统的。

要解决此问题,您必须在以下位置编辑 appcfg.py 文件:

<path_to_cloud_install_dir>/google-cloud-sdk/platform/google_appengine/google/appengine/tools/appcfg.py

在第 1634 行左右,您会发现如下所示的内容:

full_path = os.path.join(out_dir, path)

问题出在 path 参数上,对于大多数文件来说,该参数是根目录。
这会导致 join 方法忽略 out_dir 参数。

要在 *NIX 和 MacOS 类型的系统上修复此问题,您需要在上述语句之前添加一行,如下所示:

path = re.sub(r'^/', '', path)

This returns the '/' prefix from the path and allowed the join method正确地
连接琴弦。

现在您应该能够运行:

google-cloud-sdk/platform/google_appengine/appcfg.py download_app -A <app> -V <version> 20200813t184800 <your_directory>

Update as of October 2020.

The current version of the Google App Engine SDK still includes the appcfg.py script however when trying to download the files from your site the script will attempt to download them into the root folder of your system.

Example:

/images/some_site_image.png

This is probably related to changes in appengine where your files might have
been in a relative directory before but they are no longer with the new versions
of the system.

To fix the problem you will have to edit the appcfg.py file in:

<path_to_cloud_install_dir>/google-cloud-sdk/platform/google_appengine/google/appengine/tools/appcfg.py

Around line 1634 you will find something that looks like:

full_path = os.path.join(out_dir, path)

The problem is with the path argument that for most files is a root directory.
This causes the join method to ignore the out_dir argument.

To fix this on a *NIX and MacOS type of system you will need to add a line before the above mentioned statement that looks like:

path = re.sub(r'^/', '', path)

This removes the '/' prefix from the path and allows the join method to properly
connect the strings.

Now you should be able to run:

google-cloud-sdk/platform/google_appengine/appcfg.py download_app -A <app> -V <version> 20200813t184800 <your_directory>
七月上 2024-09-02 01:07:22

由于我只是费了很大的劲才弄清楚如何做到这一点,所以我想我也可以将其作为答案,即使它不适用于你:

在继续之前,在你母亲的坟墓上发誓,下次你将备份您的代码,或者更好的是,使用源代码控制。我是认真的:跟我重复“下次我将使用源代码管理”。好的,完成后,让我们看看是否可以为您恢复您的代码...

如果您的应用程序是用 Java 编写的,恐怕您就不走运了 - 源代码甚至没有上传到 App Engine ,对于 Java 应用程序。

如果您的应用是用 Python 编写的,并且同时具有 remote_apideferred 处理程序后,可以通过这两个 API 的交互来恢复源代码。基本技巧如下:

  1. remote_api_shell
  2. 创建一个新的延迟任务,读取所有文件并将它们写入数据存储
  3. 等待该任务执行
  4. 使用remote_api 从数据存储中提取数据

按顺序查看它们:

启动 remote_api_shell

只需从命令行键入以下内容:

remote_api_shell.py your_app_id

如果 shell 不在您的路径中,请在命令前加上 App Engine SDK 目录的路径前缀。

将源代码写入数据存储

在这里,我们将利用以下事实:您已安装了延迟处理程序,您可以使用remote_api将任务排队以进行延迟,并且您可以延迟对Python内置函数的调用“评估”。

由于“eval”仅执行单个语句,而不是任意代码块,因此这有点棘手,因此我们需要将整个代码表示为单个语句。就是这样:

expr = """
[type(
    'CodeFile',
    (__import__('google.appengine.ext.db').appengine.ext.db.Expando,),
    {})(
        name=dp+'/'+fn,
        data=__import__('google.appengine.ext.db').appengine.ext.db.Text(
            open(dp + '/' + fn).read()
        )
    ).put()
 for dp, dns, fns in __import__('os').walk('.')
 for fn in fns]
"""

from google.appengine.ext.deferred import defer
defer(eval, expr)

完全是黑客。让我们一次看一下:

首先,我们使用“type”内置函数动态创建 db.Expandotype() 的三个参数是新类的名称、父类列表和类变量的字典。表达式的前 4 行都与此等效:

from google.appengine.ext import db
class CodeFile(db.Expando): pass

这里使用 'import' 是针对我们无法使用语句这一事实的另一种解决方法:表达式 __import__('google .appengine.ext.db') 导入引用的模块,并返回顶级模块 (google)。

由于 type() 返回新类,因此我们现在有了一个 Expando 子类,可用于将数据存储到数据存储区。接下来,我们调用它的构造函数,并向其传递两个参数:“名称”和“数据”。我们通过当前正在处理的目录和文件的串联构造名称,而数据是打开该文件名并读取其内容的结果,包装在 db.Text 对象中,因此它可以是任意长的。最后,我们对返回的实例调用 .put() 将其存储到数据存储区。

为了读取和存储所有源代码,而不是仅仅一个文件,整个表达式发生在列表理解中,它首先迭代 os.walk,它可以方便地返回基本目录下的所有目录和文件,然后返回每个目录中的每个文件。该表达式的返回值(写入数据存储区的键列表)将被延迟模块简单地丢弃。不过,这并不重要,因为我们关心的只是副作用。

最后,我们调用 defer 函数,推迟 eval 的调用,并将我们刚刚描述的表达式作为其参数。

读取数据

执行上述操作并等待其完成后,我们可以再次使用remote_api从数据存储中提取数据。首先,我们需要代码文件模型的本地版本:

import os
from google.appengine.ext import db
class CodeFile(db.Model):
  name = db.StringProperty(required=True)
  data = db.TextProperty(required=True)

现在,我们可以获取其所有实体,并将它们存储到磁盘:

for cf in CodeFile.all():
  os.makedirs(os.dirname(cf.name))
  fh = open(cf.name, "w")
  fh.write(cf.data)
  fh.close()

就是这样!您的本地文件系统现在应该包含您的源代码。

需要注意的是:下载的代码将仅包含您的代码和数据文件。不包括静态文件,但如果您还记得它们是什么,您应该能够通过 HTTP 简单地下载它们。配置文件(例如 app.yaml)同样不包含在内,并且无法恢复 - 您需要重写它们。不过,这比重写整个应用程序要好得多,对吧?

Since I just went to all the trouble of figuring out how to do this, I figure I may as well include it as an answer, even if it doesn't apply to you:

Before continuing, swear on your mother's grave that next time you will back your code up, or better, use source control. I mean it: Repeat after me "next time I will use source control". Okay, with that done, let's see if it's possible to recover your code for you...

If your app was written in Java, I'm afraid you're out of luck - the source code isn't even uploaded to App Engine, for Java apps.

If your app was written in Python, and had both the remote_api and deferred handlers defined, it's possible to recover your source code through the interaction of these two APIs. The basic trick goes like this:

  1. Start the remote_api_shell
  2. Create a new deferred task that reads in all your files and writes them to the datastore
  3. Wait for that task to execute
  4. Extract your data from the datastore, using remote_api

Looking at them in order:

Starting the remote_api_shell

Simply type the following from a command line:

remote_api_shell.py your_app_id

If the shell isn't in your path, prefix the command with the path to the App Engine SDK directory.

Writing your source to the datastore

Here we're going to take advantage of the fact that you have the deferred handler installed, that you can use remote_api to enqueue tasks for deferred, and that you can defer an invocation of the Python built-in function 'eval'.

This is made slightly trickier by the fact that 'eval' executes only a single statement, not an arbitrary block of code, so we need to formulate our entire code as a single statement. Here it is:

expr = """
[type(
    'CodeFile',
    (__import__('google.appengine.ext.db').appengine.ext.db.Expando,),
    {})(
        name=dp+'/'+fn,
        data=__import__('google.appengine.ext.db').appengine.ext.db.Text(
            open(dp + '/' + fn).read()
        )
    ).put()
 for dp, dns, fns in __import__('os').walk('.')
 for fn in fns]
"""

from google.appengine.ext.deferred import defer
defer(eval, expr)

Quite the hack. Let's look at it a bit at a time:

First, we use the 'type' builtin function to dynamically create a new subclass of db.Expando. The three arguments to type() are the name of the new class, the list of parent classes, and the dict of class variables. The entire first 4 lines of the expression are equivalent to this:

from google.appengine.ext import db
class CodeFile(db.Expando): pass

The use of 'import' here is another workaround for the fact that we can't use statements: The expression __import__('google.appengine.ext.db') imports the referenced module, and returns the top-level module (google).

Since type() returns the new class, we now have an Expando subclass we can use to store data to the datastore. Next, we call its constructor, passing it two arguments, 'name' and 'data'. The name we construct from the concatenation of the directory and file we're currently dealing with, while the data is the result of opening that filename and reading its content, wrapped in a db.Text object so it can be arbitrarily long. Finally, we call .put() on the returned instance to store it to the datastore.

In order to read and store all the source, instead of just one file, this whole expression takes place inside a list comprehension, which iterates first over the result of os.walk, which conveniently returns all the directories and files under a base directory, then over each file in each of those directories. The return value of this expression - a list of keys that were written to the datastore - is simply discarded by the deferred module. That doesn't matter, though, since it's only the side-effects we care about.

Finally, we call the defer function, deferring an invocation of eval, with the expression we just described as its argument.

Reading out the data

After executing the above, and waiting for it to complete, we can extract the data from the datastore, again using remote_api. First, we need a local version of the codefile model:

import os
from google.appengine.ext import db
class CodeFile(db.Model):
  name = db.StringProperty(required=True)
  data = db.TextProperty(required=True)

Now, we can fetch all its entities, storing them to disk:

for cf in CodeFile.all():
  os.makedirs(os.dirname(cf.name))
  fh = open(cf.name, "w")
  fh.write(cf.data)
  fh.close()

That's it! Your local filesystem should now contain your source code.

One caveat: The downloaded code will only contain your code and datafiles. Static files aren't included, though you should be able to simply download them over HTTP, if you remember what they all are. Configuration files, such as app.yaml, are similarly not included, and can't be recovered - you'll need to rewrite them. Still, a lot better than rewriting your whole app, right?

在巴黎塔顶看东京樱花 2024-09-02 01:07:22

更新:Google appengine 现在允许您下载代码(适用于 Python、Java、PHP 和 Go 应用)

工具文档位于此处

Update: Google appengine now allows you to download the code (for Python, Java, PHP and Go apps)

Tool documentation here.

尛丟丟 2024-09-02 01:07:22

不幸的是答案是否定的。这是 SO 和应用程序引擎板上的常见问题。
请参阅此处此处

我相信你会没事的,因为你确实将所有代码保存在源代码管理中,对吗? ;)

如果您希望将来成为一个选项,您可以上传您的 src 的 zip,并在您的 Web 应用程序中的某个位置提供指向它的链接,作为构建/部署过程的一部分。

还有一些项目,例如这个,可以为您自动执行该过程。

Unfortunately the answer is no. This is a common question on SO and the app engine boards.
See here and here for example.

I'm sure you'll be OK though, because you do keep all your code in source control, right? ;)

If you want this to be an option in the future, you can upload a zip of your src, with a link to it somewhere in your web app, as part of your build/deploy process.

There are also projects out there like this one that automate that process for you.

深海不蓝 2024-09-02 01:07:22

发现您可以在控制台(命令行/终端)中运行以下命令。只需确保 appcfg.py 可通过您的 $PATH 访问即可。

locate appcfg.py

默认情况下,下面的代码打印出每个文件和下载进度。

appcfg.py download_app -A APP_ID -V VERSION_ID ~/Downloads

Found that you can run the following in your console (command line / terminal). Just make sure that appcfg.py is accessible via your $PATH.

locate appcfg.py

By default the code below prints out each file and the download progress.

appcfg.py download_app -A APP_ID -V VERSION_ID ~/Downloads
偏爱自由 2024-09-02 01:07:22

您可以获得您的代码,甚至是 Java 代码。它只需要一些逆向工程。您可以按照以下说明使用 appengine SDK 下载 war 文件:https:// Developers.google.com/appengine/docs/java/tools/uploadinganapp

那么您至少拥有可以通过 JAD 运行以返回到源文件(至少接近源文件)的类文件。

You CAN get your code, even in Java. It just requires a bit of reverse engineering. You can download the war file using the appengine SDK by following these instructions: https://developers.google.com/appengine/docs/java/tools/uploadinganapp

Then you at least have the class files that you can run through JAD to get back to the source files (close to it, at least).

对你的占有欲 2024-09-02 01:07:22

如果您使用的是python...您也许可以编写一个脚本来打开当前目录和子目录中的所有文件并将它们添加到zip文件中供您下载

我不太了解应用程序引擎或权限,但似乎这是可能的

if you're using python... you might be able to write a script that opens all the files in it's current directory and child directories and adds them to a zipfile for you to download

I don't know much about app engine or the permissions, but it seems like that could be possible

我只土不豪 2024-09-02 01:07:22

你必须恢复到早期的sdk,appcfg.py不在最新的sdk中。有点痛苦,但它有效。它在文献中应该更加突出。花了我一整天的时间。

You have to revert to the earlier sdk, appcfg.py is not in the latest sdk. Kind of a pain, but it works. It should be far more prominent in the literature. Cost me an entire day.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文