ythy / blog

Give everything a shot
6 stars 0 forks source link

Android 整合 JGIT 踩的坑 #536

Open ythy opened 6 months ago

ythy commented 6 months ago

试图整合Github

首先,官网是参考例子的 https://github.com/centic9/jgit-cookbook 例子超简单, 请看

  private const val REMOTE_URL_SSH = "ssh://git@github.com/xxx/xxxxx.git"
  private const val PRIVATE_KEY = "id_xxx.pem" 

  private fun cloneRepository(context:Context, directory:File):Git{
        val sshSessionFactory: SshSessionFactory = object : JschConfigSessionFactory() {
            override fun configure(host: OpenSshConfig.Host?, session: Session?) {
//              connection is closed by foreign host
//              at com.jcraft.jsch.Session.connect(Session.java:269)
                session?.port = 443
                super.configure(host, session)
            }

            @Throws(JSchException::class)
            override fun createDefaultJSch(fs: FS?): JSch{
                val defaultJSch: JSch = super.createDefaultJSch(fs)
                defaultJSch.addIdentity(getPriKey(context))
                return defaultJSch
            }
        }

        return Git.cloneRepository()
            .setURI(REMOTE_URL_SSH)
            .setDirectory(directory)
            .setTransportConfigCallback { transport: Transport ->
                val sshTransport = transport as SshTransport
                sshTransport.sshSessionFactory = sshSessionFactory
            }
            .call()
    }

   fun getPriKey(context: Context):String{
        val prikey = CommonUtil.generateFile(context, PRIVATE_KEY, MConfig.SD_KEY_PATH)
        if (prikey.length() == 0L)
            CommonUtil.copyBigDataToSD(context, PRIVATE_KEY, prikey.path)
        return prikey.path
    }

    private fun commitAndPush(context: Context, directory: File){
        val repoDir = File(directory, ".git")
        val builder = FileRepositoryBuilder()
        builder.setGitDir(repoDir)
            .readEnvironment() // scan environment GIT_* variables
            .findGitDir() // scan up the file system tree
            .build().use { repository ->
                Git(repository).use { git ->
                    git.add()
                        .addFilepattern(".")
                        .call();
                    git.commit()
                        .setMessage("Changed saving file by program")
                        .call()
                    Log.d(TAG, "Committed file to repository at " + repository.directory)

                    val results: Iterable<PushResult> = git.push()
                        .setCredentialsProvider(
                            UsernamePasswordCredentialsProvider(
                                USER,
                                PASSWORD
                            )
                        )
                        .call()
                    for (r: PushResult in results) {
                        for (update: RemoteRefUpdate in r.remoteUpdates) {
                            Log.d(TAG, "Push result: $update")
                            if (update.status != RemoteRefUpdate.Status.OK && update.status != RemoteRefUpdate.Status.UP_TO_DATE) {
                                val errorMessage = "Push failed: " + update.status
                                throw RuntimeException(errorMessage)
                            }
                        }
                    }
                    Log.d(TAG, "Pushed to remote repository at " + git.repository.directory)
                }
            }
    }

那么问题来了

.ssh目录下默认生成的私钥文件id_xxx 无法使用

报错:com.jcraft.jsch.JSchException: invalid privatekey 查询资料有讲:The Jsch seems not to support the above private key format, to solve it, we can use ssh-keygen to convert the private key format to the RSA or pem mode, and the above program works again.
推荐用ssh-keygen -p -f ~/.ssh/id_rsa -m pem 命令把当前私钥格式进行转换。 命令运行即报错Windows SSH: Permissions for 'private-key' are too open 意思是这个密钥文件仅限登录者用户自己有权限,处理方法如下: You locate the file in Windows Explorer, right-click on it then select "Properties". Navigate to the "Security" tab and click "Advanced".

  1. disable inheritance and delete all permissions.
  2. Change the owner to you,
  3. Then grant yourself "Full control" and save the permissions.

好了,命令不报错了,格式也没转! 只能重新生成一个pem格式的私钥,命令: ssh-keygen -m PEM -t ecdsa -b 521 -f id_xxx.pem 此文件上面程序会使用到。

JGIT这个包版本对Androd版本极度依赖

这是人干的事吗!编译可能不报错,一运行,只要测试机的OS Android API版本低于Gjit未声明的某个隐藏量,就报错!!!!比如:

java.lang.NoSuchMethodError: No virtual method readNBytes(I)[B in class Lorg/eclipse/jgit/util/io/SilentFileInputStream; or its super classes (declaration of 'org.eclipse.jgit.util.io.SilentFileInputStream' appears in /data/app/com.mx.emperor-2Md192Xxr6rvNhbbfASfNw==/base.apk!classes12.dex)

惹不起,咱要么降Jgit,其实也降不了多少,因为 org.eclipse.jgit:org.eclipse.jgit.ssh.jsch 这东西最低版本都是 5.8.0.202006091008-r,目标机器是Android9 肯定不行了。要么就用高版本模拟器或者真机运行。 5.8 可以在Android12上运行, 6.8 就不能在Android12上运行了

BARE 库

测试提交的时候,总提示org.eclipse.jgit.api.errors.WrongRepositoryStateException: Cannot commit on a repo with state: BARE 我一头雾水啊,我导仓库的时候没设bare啊, 折腾半天才发现,读取已存在仓库的时候val builder = FileRepositoryBuilder();builder.setGitDir(repoDir), 这里设的gitDir, 不是外层的directory,是val repoDir = File(directory, ".git") 里面的那个.git目录!!

跨不过去的22端口

当你自认为处理完一切问题,好了,22端口不通

13:get remote references: create git ls-remote: exit status 128, stderr: "ssh connect to host github.com port 22: Connection timed out\r\nfatal: Could not read from remote repository.\n\nPlease make sure you have the correct access rights\nand the repository exists.\n"

PC上可以通过设置 .ssh/config 文件解决, 手机上整不了啊!!!!

Host github.com
Hostname ssh.github.com
Port 443
User git

getSession的时候,session?.port = 443强制使用443端口报错Error: ssh://git@github.com:443/xxx/xxxxx.git: connection is closed by foreign host

换 org.eclipse.jgit:org.eclipse.jgit.ssh.apache 包

这里就简单概括,先上错误

Caused by: java.lang.ClassNotFoundException: Didn't find class "javax.management.ReflectionException" on path: DexPathList
Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/management/ReflectionException;

然后找到官方issue https://github.com/apache/mina-sshd/issues/322

I try to used mina sshd in Android but I have some problems:
in LazyDefaultUserHomeFolderHolder, the path is read from System.getProperty("user.home"). But in Android, System.getProperty("user.home") is null. Will this be a problem?
in ExceptionUtils, sshd uses class javax.management.MBeanException and javax.management.ReflectionException. But they don't exist on Android.
Can mina sshd be used in Android? Or is there any android version of sshd can be used in Android?

下面答复言简意赅,不!支!持!
散了吧

ythy commented 6 months ago

到这里其实改结束了,偶然间想到可能不应该在github一棵树上吊死,别的托管可能不强制要求SSH,于是换了gitea试试。

gitea

  1. 先注册
  2. 建仓库一气呵成
  3. 用https加用户名密码的方式测试,
  4. 一次成功,我人都懵了!

主要代码如下

        val repoDir = File(directory, ".git")
        // now open the resulting repository with a FileRepositoryBuilder
        val builder = FileRepositoryBuilder()
        val repository = builder.setGitDir(repoDir)
            .readEnvironment() // scan environment GIT_* variables
            .findGitDir() // scan up the file system tree
            .build()
        val git = Git(repository)
        git.add()
            .addFilepattern(".")
            .call();
        git.commit()
            .setMessage("Changed saving file by program at ${ DateFormat.format("yyyy-MM-dd HH:mm:ss", Date())}")
            .call()
       val results: Iterable<PushResult> = git.push()
            .setCredentialsProvider(
                UsernamePasswordCredentialsProvider(
                    USER,
                    PASSWORD
                )
            )
            .call()
ythy commented 6 months ago
  1. 由于JGit最近的版本不支持Android <=9, 把版本降到3.7.0.201502260915-r能正常运行。 注意这个旧版本默认分支名称还是master, 需要clone的时候.setBranch("main").
  2. Commit命令要设置一下提交人和提交邮箱,.setAuthor(USER, EMAIL), 否则提交人会显示成类似这样root root@localhost