Please enable Javascript to view the contents

Devops经验-git版本控制能否记录文件权限信息

 ·  ☕ 4 分钟

问题

JenkinsFile中,从Git仓库拉取的脚本,有时不具备可执行权限。

认知

  1. Git版本控制,可追踪文件内容、目录结构、文件格式、文件权限、文件元数据(文件最后修改时间等)。

  2. Git仓库拉取脚本的权限,如果设置core.fileMode=false,则不追踪文件的权限,当git仓库检出代码是,文件权限会根据当前文件系统和OS的默认设置进行设置。

最佳实践

手动配置

  1. 通过以下命令,使git客户端识别文件权限
1
git config core.fileMode true
  1. 手动对脚本提权并推送主仓库
1
2
3
4
chmod +x path_to_script.sh

# git commit -m 'fix: change script.sh filemode' 
# git push
  1. GIT全局配置,设置忽略文件格式的修改
1
git config --global core.fileMode false
  1. 当需要再归档特殊权限的文件时,再执行步骤1,2

Git-Hook钩子

使用pre-commitpre-push 钩子来检查和修改文件权限。
pre-commit: 在本地提交之前执行,可以用来检查文件内容或权限,并阻止提交如果它们不符合预期的标准。
pre-push: 在推送到远程仓库之前执行,可以用来检查文件权限,并阻止推送如果它们不符合预期的标准。

使用 pre-commit 钩子

在Git仓库的 .git/hooks 目录下创建或编辑 pre-commit 文件:

1
2
cd .git/hooks
touch pre-commit && chmod +x pre-commit

在 pre-commit 文件中添加以下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/sh

# 检查脚本文件的权限
for file in $(git diff --cached --name-only --diff-filter=ACMR | grep '\.sh$'); do
  if [ -x "$file" ]; then
    echo "Error: Script $file has execute permissions. Remove them before committing."
    exit 1
  fi
done

# 如果需要,也可以在这里添加设置权限的命令
# 例如,确保所有脚本文件都没有执行权限
# git update-index --chmod=+x 'path/to/script.sh'

使用 .gitattributes 文件

Git 允许你通过 .gitattributes 文件来设置文件的属性,包括权限。

  • -text: 不设置text

在 Git 仓库中创建或编辑 .gitattributes 文件:

1
2
path/to/your/script.sh eol=lf
path/to/your/script.sh -text

启用文件模式设置:

1
git config core.fileMode true

提交 .gitattributes 文件到仓库:

1
2
3
git add .gitattributes
git commit -m "Add .gitattributes to set file permissions"
git push

在Pipeline脚本中执行复杂的bash命令

默认,将运行系统默认shell。 在 Shell 步骤中,可以接受多行。 因此,可以声明解释器选择器,如 #!/bin/bash#!/usr/bin/perl
注意:#!/bin/bash必须放在第一行,且前面不允许存在空格,否则会被忽略。

正确写法如下:

1
2
3
4
5
6
7
stage('Setting the variables values') {
    steps {
         sh '''#!/bin/bash
                 echo "hello world" 
         '''
    }
}

错误写法举例:

1
2
3
4
5
6
7
8
stage('Setting the variables values') {
    steps {
         sh '''
            #!/bin/bash
            echo "hello world"
         '''
    }
}

流水线之间参数传递

注意:参数的赋值与取值,必须要在script{}块内才能生效。

父流水线

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
pipeline {
    agent {
        node { label 'linux' }
    }
    parameters {
        choice(
            name: 'choiceP',
            choices: ['A', 'B'],
            description: '<strong style="color:green;">A,B</strong>')
        listGitBranches(
            name: 'gitbranchP',
            defaultValue: 'master',
            credentialsId: 'gitlab-cred',
            remoteURL: 'https://github.com/example.git',
            description: '<strong style="color:green;">git分支选择,默认选择master分支</strong>',
            type: 'PT_BRANCH_TAG',
            tagFilter: '*',
            branchFilter: 'refs/heads/(.*)',
            quickFilterEnabled: true,
            selectedValue: 'NONE',
            sortMode: 'NONE')
    }
    environment {
        srcDir = 'example'
        timestamp = new Date().format('yyyyMMddHHmmss')
        TIMESTAMP_D = sh(script: "TZ=UTC-8 date +'%Y%m%d'", returnStdout: true).trim()
    }
    stages {
        stage('清理空间') {
            steps {
                cleanWs()
            }
        }
        stage('克隆代码') {
            steps {
                checkout([
                    $class: 'GitSCM',
                    branches: [[name: "$gitbranchP"]],
                    extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: "$srcDir"]],
                    userRemoteConfigs: [[credentialsId: 'gitlab-cred', url: 'https://github.com/example.git']]])
            }
        }
        stage('并发构建') {
            parallel {
                stage('打包-A') {
                    steps {
                        script {
                            jobA = build(
                                job: 'job-group/a-job',
                                parameters: [
                                    gitParameter(name: 'Branch', value: "${Branch}"),
                                    string(name: 'ParamsB', value: "${ParamsB}")]
                            )
                            a_fullpath = jobA.buildVariables.fullpath
                            a_filename = jobA.buildVariables.filename
                        }
                    }
                }
                stage('打包-B') {
                    steps {
                        script {
                            jobB = build(
                                job: 'job-group/b-job',
                                parameters: [
                                    gitParameter(name: 'Branch', value: "${Branch}"),
                                    string(name: 'ParamsB', value: "${ParamsB}")]
                            )
                            b_fullpath = jobB.buildVariables.fullpath
                            b_filename = jobB.buildVariables.filename
                        }
                    }
                }
            }
        }
    }
}
              

子流水线

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
pipeline {
    agent {
        kubernetes {
            cloud 'k8s'
            defaultContainer 'build'
            yaml '''\
        apiVersion: v1
        kind: Pod
        spec:
          containers:
          - name: build
            image: devops/job-a:1.0.0
            imagePullPolicy: "Always"
            tty: true
        nodeSelector:
          kubernetes.io/os: linux
        '''.stripIndent()
        }
    }

    options {
        timeout(time:20, unit:'MINUTES')
    }

    parameters {
        choice(
            name: 'ParamsB',
            choices: ['1', '2', '3'],
            description: '<strong style="color:green;">参数</strong>')
        listGitBranches(
            name: 'Branch',
            defaultValue: 'master',
            credentialsId: 'gitlab-cred',
            remoteURL: 'https://github.com/A.git',
            description: '<strong style="color:green;">git分支选择,默认选择master分支</strong>',
            type: 'PT_BRANCH_TAG',
            tagFilter: '*',
            branchFilter: 'refs/heads/(.*)',
            quickFilterEnabled: true,
            selectedValue: 'NONE',
            sortMode: 'NONE')
    }

    environment {
        srcDir = 'A'
    }
    stages {
      stage('克隆代码') {
            steps {
                script {
                    sh""" git clone -b "$Branch" https://github.com/A.git $srcDir """
                }
            }
        }
      stage('构建打包') {
            steps {
                script {
                    dir("$srcDir") {
                        env.commit_hash = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
                        env.commit_time = sh(script: 'git show --pretty=format:"%ct" | head -1', returnStdout: true).trim()
                        env.filename = "A_${env.commit_hash}.tar.gz"
                        env.file_path = '/s3/bucketA/'
                        env.fullpath = "${env.file_path}/${env.filename}"
                    }
                }
            }
      }
    }
}
分享

Hex
作者
Hex
CloudNative Developer

目录