我最近有一个硬盘崩溃并丢失了我的所有源代码。是否可以提取/签出我已上传到Google App Engine的代码(如最新版本)?
答案 0 :(得分:41)
由于我只是想弄清楚如何做到这一点,我想我也可以把它作为一个答案,即使它不适用于你:
在继续之前,发誓对你母亲的坟墓说,下次你将支持你的代码,或者更好,使用源代码控制。我的意思是:在我之后重复“下次我将使用源代码控制”。好的,完成后,让我们看看是否有可能为您恢复代码......
如果你的应用程序是用Java编写的,我担心你运气不好 - 源代码甚至没有上传到App Engine,也不适用于Java应用程序。
如果您的应用程序是用Python编写的,并且同时定义了remote_api和deferred处理程序,则可以通过这两个API的交互来恢复源代码。基本技巧是这样的:
按顺序查看它们:
只需在命令行中输入以下内容:
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应用)
答案 2 :(得分:4)
不幸的是答案是否定的。这是关于SO和应用引擎板的常见问题。 例如,请参阅here和here。
我相信你会好的,因为你确实将所有代码保存在源代码管理中,对吧? ;)
如果您希望将来可以选择此选项,您可以上传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>