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

时间:2010-03-19 16:55:30

标签: google-app-engine version-control backup

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

8 个答案:

答案 0 :(得分:41)

由于我只是想弄清楚如何做到这一点,我想我也可以把它作为一个答案,即使它不适用于你:

在继续之前,发誓对你母亲的坟墓说,下次你将支持你的代码,或者更好,使用源代码控制。我的意思是:在我之后重复“下次我将使用源代码控制”。好的,完成后,让我们看看是否有可能为您恢复代码......

如果你的应用程序是用Java编写的,我担心你运气不好 - 源代码甚至没有上传到App Engine,也不适用于Java应用程序。

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

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

    按顺序查看它们:

    启动remote_api_shell

    只需在命令行中输入以下内容:

    remote_api_shell.py your_app_id
    

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

    将源写入数据存储区

    这里我们将利用你安装了延迟处理程序的事实,你可以使用remote_api为延迟排队任务,并且你可以推迟调用Python内置函数'eval'

    由于'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.Expando的新子类。 type()的三个参数是新类的名称,父类列表和类变量的字典。表达式的前4行与此相当:

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

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

    由于type()返回新类,我们现在有一个我们可以用来将数据存储到数据存储区的Expando子类。接下来,我们调用它的构造函数,传递两个参数,'name'和'data'。我们从目前正在处理的目录和文件的串联构造的名称,而数据是打开该文件名并读取其内容的结果,包装在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,同样不包括在内,无法恢复 - 您需要重写它们。还是,比重写整个应用程序要好得多,对吗?

答案 1 :(得分:32)

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

工具documentation here

答案 2 :(得分:4)

不幸的是答案是否定的。这是关于SO和应用引擎板的常见问题。 例如,请参阅herehere

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

如果您希望将来可以选择此选项,您可以上传src的zip,并在您的网络应用中的某个位置添加链接,作为构建/部署过程的一部分。

还有像this one这样的项目可以为您自动化该过程。

答案 3 :(得分:3)

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

locate appcfg.py

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

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

答案 4 :(得分:2)

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

然后你至少拥有可以通过JAD运行的类文件来回到源文件(至少接近它)。

答案 5 :(得分:0)

如果您正在使用python ...您可以编写一个脚本,打开其当前目录和子目录中的所有文件,并将它们添加到zipfile中供您下载

我对app引擎或权限知之甚少,但看起来似乎可能

答案 6 :(得分:0)

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

答案 7 :(得分:0)

截至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)

这将从路径中删除'/'前缀,并允许join方法正确 连接琴弦。

现在您应该可以运行:

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