Powershell 自动提交代码到 Github

Powershell 自动提交代码到 Github

前提

当前 Powershell 版本:

当前 Git 版本:

远程仓库:

监听特定的文件夹下的文件修改,并写入日志

参考博客:Monitoring Folders for File Changes - powershell.one

以下为我的脚本,替换 >>> 开头的注释所在的变量,然后保存为 .ps1 文件即可使用

# >>> 指定监控路径
$BlogRepoPath = 'path2monitoredFolder'

# >>> 指定日志文件夹
$basicLogPath = 'path2logFolder'

# >>> 指定监控路径对应的远程仓库名
$remoteReposity = 'remoteRepositoryName'

# specify which files you want to monitor
$FileFilter = '*'  

# specify whether you want to monitor subfolders as well:
$IncludeSubfolders = $true

# specify the file or folder properties you want to monitor:
$AttributeFilter = [IO.NotifyFilters]::FileName, [IO.NotifyFilters]::LastWrite 

try {
    $watcher = New-Object -TypeName System.IO.FileSystemWatcher -Property @{
        Path                  = $BlogRepoPath
        Filter                = $FileFilter
        IncludeSubdirectories = $IncludeSubfolders
        NotifyFilter          = $AttributeFilter
    }

    # define the code that should execute when a change occurs:
    $action = {
        # the code is receiving this to work with:

        # change type information:
        $details = $event.SourceEventArgs
        $Name = $details.Name
        $OldName = $details.OldName
        $FullPath = $details.FullPath
        $OldFullPath = $details.OldFullPath

        if ($FullPath.StartsWith("$BlogRepoPath\.git") -and -not($FullPath.Contains(".github")) ){
            <# Action to perform if the condition is true #>
            return
        }
        # node_modules/
        # if ($FullPath.StartsWith("$BlogRepoPath\node_modules")){
        #     <# Action to perform if the condition is true #>
        #     return
        # }
        # public/
        if ($FullPath.StartsWith("$BlogRepoPath\public")){
            <# Action to perform if the condition is true #>
            return
        }
        # db.json
        if ($FullPath.StartsWith("$BlogRepoPath\db.json")){
            <# Action to perform if the condition is true #>
            return
        }

        # 准备日志
        if (-not (Get-Item -Path $basicLogPath)) {
            return 
        }
        $year = (get-date).Year
        $mouth = (get-date).Month
        $day = (get-date).Day
        $filePath = "$basicLogPath\$year-$mouth"
        if (-not (Get-Item -Path $filePath)) {
            New-Item -ItemType Directory -Path $filePath 
        }
        $logFile = "$filePath\$year-$mouth-$day.log"
        if (-not (Get-Item -Path $logFile)) {
            New-Item -ItemType File -Path $logFile 
        }

        # type of change:
        $ChangeType = $details.ChangeType

        # when the change occured:
        $Timestamp = $event.TimeGenerated

        # save information to a global variable for testing purposes
        # so you can examine it later
        # MAKE SURE YOU REMOVE THIS IN PRODUCTION!
        $global:all = $details

        # now you can define some action to take based on the
        # details about the change event:

        # let's compose a message:
        $text = "{0} was {1} at {2}" -f $FullPath, $ChangeType, $Timestamp
        # Write-Host ""
        # Write-Host $text -ForegroundColor DarkYellow
        # Out-File -Append -FilePath  $logFile -InputObject ""
        Out-File -Append -FilePath  $logFile -InputObject $text

        # you can also execute code based on change type here:
        switch ($ChangeType) {
            'Changed' { 
                "CHANGE" 
                # 修改的时候提交也要等 5 秒
                Start-Sleep -Seconds 1    
            }
            'Created' { "CREATED" }
            'Deleted' {
                "DELETED"
                # to illustrate that ALL changes are picked up even if
                # handling an event takes a lot of time, we artifically
                # extend the time the handler needs whenever a file is deleted
                # Write-Host "Deletion Handler Start" -ForegroundColor Gray
                Out-File -Append -FilePath  $logFile -InputObject "Deletion Handler Start"
                Start-Sleep -Seconds 1    
                # Write-Host "Deletion Handler End" -ForegroundColor Gray
                Out-File -Append -FilePath  $logFile -InputObject "Deletion Handler End"
            }
            'Renamed' { 
                # this executes only when a file was renamed
                $text = "File {0} was renamed to {1}" -f $OldName, $Name
                # Write-Host $text -ForegroundColor Yellow
                Out-File -Append -FilePath  $logFile -InputObject $text
            }

            # any unhandled change types surface here:
            default { 
                # Write-Host $_ -ForegroundColor Red -BackgroundColor White
                Out-File -Append -FilePath  $logFile -InputObject $_
             }
        }

        #  git 先更新 再提交 
        #  日志添加时间,
        #  目录添加忽略条件。不监听 .git目录下的文件
        #  添加一个延时,不要提交太频繁
        git -C $BlogRepoPath pull $remoteReposity master
        git -C $BlogRepoPath add -A
        git -C $BlogRepoPath commit -m 'auto commit'
        git -C $BlogRepoPath push $remoteReposity
        Out-File -Append -FilePath  $logFile -InputObject "push to github $remoteReposity"

    }

    # subscribe your event handler to all event types that are
    # important to you. Do this as a scriptblock so all returned
    # event handlers can be easily stored in $handlers:
    $handlers = . {
        Register-ObjectEvent -InputObject $watcher -EventName Changed  -Action $action 
        Register-ObjectEvent -InputObject $watcher -EventName Created  -Action $action 
        Register-ObjectEvent -InputObject $watcher -EventName Deleted  -Action $action 
        Register-ObjectEvent -InputObject $watcher -EventName Renamed  -Action $action 
    }

    # monitoring starts now:
    $watcher.EnableRaisingEvents = $true

    # Write-Host "Watching for changes to $BlogRepoPath"
    # Out-File -Append -FilePath  $logFile -InputObject '------------------------------------------------------------------------------------------------------------------------'
    # Out-File -Append -FilePath  $logFile -InputObject "Watching for changes to $BlogRepoPath"


    # since the FileSystemWatcher is no longer blocking PowerShell
    # we need a way to pause PowerShell while being responsive to
    # incoming events. Use an endless loop to keep PowerShell busy:
    do {
        # Wait-Event waits for a second and stays responsive to events
        # Start-Sleep in contrast would NOT work and ignore incoming events
        Wait-Event -Timeout 60

        # write a dot to indicate we are still monitoring:
        # Write-Host "." -NoNewline
        # Out-File -Append -FilePath  $logFile -InputObject "."


    } while ($true)
}
finally {
    # this gets executed when user presses CTRL+C:

    # stop monitoring
    $watcher.EnableRaisingEvents = $false

    # remove the event handlers
    $handlers | ForEach-Object {
        Unregister-Event -SourceIdentifier $_.Name
    }

    # event handlers are technically implemented as a special kind
    # of background job, so remove the jobs now:
    $handlers | Remove-Job

    # properly dispose the FileSystemWatcher:
    $watcher.Dispose()

    # Write-Warning "Event Handler disabled, monitoring ends."
    Out-File -Append -FilePath  $logFile -InputObject "Event Handler disabled, monitoring ends."

}

对此,相比于参考博客中的源码,这里做出了几点改进:

将此 ps1 脚本设置为开机自启

设置 powershell 脚本开机启动有点麻烦,需要在外面套一层 .bat 文件,同时还会有闪屏的问题,即打开一个 cmd 窗口执行完语句之后又快速关闭。为了解决这个问题,在 .bat 文件的外面再套一层 .vbs 即可解决,最终文件如下:

  1. 创建 script.vbs

    Dim WinScriptHost
    Set WinScriptHost = CreateObject("WScript.Shell")
    WinScriptHost.Run Chr(34) & "path2batfile\script.bat" & Chr(34), 0
    Set WinScriptHost = Nothing
    
  2. 创建 script.bat

    pwsh.exe -file path2powershellScript\script.ps1
    
  3. 将上一小节的脚本保存为 script.ps1,放到 path2powershellScript 指定的位置即可。

  4. script.vbs 创建快捷方式,然后放到 C:\Users\<user_name>\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 文件夹,系统开机的时候,会自动执行此文件夹下的脚本或者软件。