diff --git a/.github/actions/check-run-action/action.yml b/.github/actions/check-run-action/action.yml index 139d52ad..93e1e00b 100644 --- a/.github/actions/check-run-action/action.yml +++ b/.github/actions/check-run-action/action.yml @@ -9,7 +9,6 @@ inputs: required: true owner: description: 'The owner of the target repository' - default: 'git-for-windows' repo: description: 'The name of the target repository' rev: diff --git a/.github/workflows/git-artifacts.yml b/.github/workflows/git-artifacts.yml index d93a99cf..cb3042b9 100644 --- a/.github/workflows/git-artifacts.yml +++ b/.github/workflows/git-artifacts.yml @@ -21,9 +21,10 @@ on: existing_git_tag: description: 'Existing tag to build from. Requires tag_git_workflow_run_id to be empty' required: false - build_extra_rev_for_existing_git_tag: - description: 'build-extra revision to use if building from an existing Git tag. Required if existing_git_tag is non-empty' + release_branch: + description: 'The branch name to use for this release (e.g. `git-2.44.x-releases`)' required: false + type: string env: GPG_OPTIONS: "--batch --yes --no-tty --list-options no-show-photos --verify-options no-show-photos --pinentry-mode loopback" @@ -31,11 +32,12 @@ env: USERPROFILE: "${{github.workspace}}\\home" ARTIFACTS_TO_BUILD: "${{github.event.inputs.artifacts}}" ARCHITECTURE: "${{github.event.inputs.architecture}}" - OWNER: git-for-windows + OWNER: ${{ github.repository_owner }} REPO: git TAG_GIT_WORKFLOW_RUN_ID: "${{github.event.inputs.tag_git_workflow_run_id}}" EXISTING_GIT_TAG: "${{github.event.inputs.existing_git_tag}}" - BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG: "${{github.event.inputs.build_extra_rev_for_existing_git_tag}}" + RELEASE_BRANCH: "${{github.event.inputs.release_branch}}" + GIT_OR_MINGIT: "Git" defaults: run: @@ -51,10 +53,59 @@ jobs: mingw_package_prefix: ${{steps.configure-environment.outputs.MINGW_PACKAGE_PREFIX}} sdk_repo_arch: ${{steps.configure-environment.outputs.SDK_REPO_ARCH}} check-run-state: ${{steps.check-run-state.outputs.check-run-state}} + release-branch: ${{steps.configure-environment.outputs.release-branch}} steps: - name: clone git-for-windows-automation uses: actions/checkout@v5 + - name: configure token + if: github.repository_visibility == 'private' + id: token + uses: actions/github-script@v7 + with: + result-encoding: string + script: | + const fs = require('fs') + if (!fs.existsSync(process.env.HOME)) fs.mkdirSync(process.env.HOME) + const { callGit, getPushAuthorizationHeader } = require('./repository-updates.js') + for (const repo of [ + 'build-extra', + 'git', + 'git-sdk-${{ env.ARCHITECTURE == 'x86_64' && '64' || (env.ARCHITECTURE == 'aarch64' && 'arm64' || '32') }}', + 'MINGW-packages' + ]) { + const header = await getPushAuthorizationHeader( + console, + core.setSecret, + ${{ secrets.GH_APP_ID }}, + ${{ toJSON(secrets.GH_APP_PRIVATE_KEY) }}, + process.env.OWNER, + repo + ) + console.log(callGit(['config', '--global', `http.https://github.com/${process.env.OWNER}/${repo}.extraHeader`, header])) + } + + // return an access token for use in the "wait if workflow run has not finished yet" step + if (process.env.EXISTING_GIT_TAG) return '' + const getAppInstallationId = require('./get-app-installation-id') + const installationId = await getAppInstallationId( + console, + ${{ secrets.GH_APP_ID }}, + ${{ toJSON(secrets.GH_APP_PRIVATE_KEY) }}, + context.repo.owner, + context.repo.repo + ) + + const getInstallationAccessToken = require('./get-installation-access-token') + const { token: accessToken } = await getInstallationAccessToken( + console, + ${{ secrets.GH_APP_ID }}, + ${{ toJSON(secrets.GH_APP_PRIVATE_KEY) }}, + installationId + ) + core.setSecret(accessToken) + return accessToken - name: Construct bundle-artifacts from existing tag + id: handle-existing-git-tag if: env.EXISTING_GIT_TAG != '' run: | die () { @@ -65,8 +116,26 @@ jobs: test -z "$TAG_GIT_WORKFLOW_RUN_ID" || die 'existing_git_tag cannot be used with tag_git_workflow_run_id!' - test -n "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" || - die 'existing_git_tag needs build_extra_rev_for_existing_git_tag!' + if test mingit = "$ARTIFACTS_TO_BUILD" || test "mingit mingit-busybox" = "$ARTIFACTS_TO_BUILD" + then + GIT_OR_MINGIT=MinGit + echo "GIT_OR_MINGIT=MinGit" >>$GITHUB_ENV + fi + + if test -n "$RELEASE_BRANCH" + then + if test MinGit = "$GIT_OR_MINGIT" + then + RELEASE_BRANCH=mingit-"$(expr "$EXISTING_GIT_TAG" : 'v\?\([0-9]\+\.[0-9]\+\)\.\{0,1\}[0-9]*\(\.windows\.[0-9]*\)\?$')".x-releases && + test mingit-.x-releases != "$RELEASE_BRANCH" + else + RELEASE_BRANCH="${EXISTING_GIT_TAG%%.windows.*}" && + RELEASE_BRANCH=git-"${RELEASE_BRANCH#v}" && + test git- != "$RELEASE_BRANCH" + fi || + die "Could not determine release branch from '$EXISTING_GIT_TAG'" + echo "RELEASE_BRANCH=$RELEASE_BRANCH" >>$GITHUB_ENV + fi set -o pipefail && @@ -87,7 +156,7 @@ jobs: const { waitForWorkflowRunToFinish } = require('./workflow-runs') await waitForWorkflowRunToFinish( console, - '${{ secrets.GITHUB_TOKEN }}', + '${{ steps.token.outputs.result || secrets.GITHUB_TOKEN }}', context.repo.owner, context.repo.repo, process.env.TAG_GIT_WORKFLOW_RUN_ID @@ -97,9 +166,10 @@ jobs: if: env.TAG_GIT_WORKFLOW_RUN_ID != '' id: get-bundle-artifacts-url with: + github-token: ${{ steps.token.outputs.result || secrets.GITHUB_TOKEN }} script: | - if (process.env.EXISTING_GIT_TAG || process.env.BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG) { - throw new Error('tag_git_workflow_run_id cannot be used with existing_git_tag or build_extra_rev_for_existing_git_tag!') + if (process.env.EXISTING_GIT_TAG) { + throw new Error('tag_git_workflow_run_id cannot be used with existing_git_tag!') } const getDownloadURL = require('./get-workflow-run-artifact') const workflowRunId = process.env.TAG_GIT_WORKFLOW_RUN_ID @@ -116,6 +186,7 @@ jobs: unzip bundle-artifacts.zip -d bundle-artifacts echo "GIT_VERSION=$(cat bundle-artifacts/next_version)" >> $GITHUB_ENV echo "GIT_REV=$(cat bundle-artifacts/git-commit-oid)" >>$GITHUB_ENV + test ! -f bundle-artifacts/mingit-only || echo "GIT_OR_MINGIT=MinGit" >>$GITHUB_ENV - name: Mirror Check Run to ${{ env.OWNER }}/${{ env.REPO }} uses: ./.github/actions/check-run-action with: @@ -125,8 +196,8 @@ jobs: repo: ${{ env.REPO }} rev: ${{ env.GIT_REV }} check-run-name: "git-artifacts-${{ env.ARCHITECTURE }}" - title: "Build Git ${{ env.GIT_VERSION }} artifacts" - summary: "Build Git ${{ env.GIT_VERSION }} artifacts from commit ${{ env.GIT_REV }}${{ env.TAG_GIT_WORKFLOW_RUN_ID && format(' (tag-git run #{0})', env.TAG_GIT_WORKFLOW_RUN_ID) || '' }}" + title: "Build ${{ env.GIT_OR_MINGIT }} ${{ env.GIT_VERSION }} artifacts" + summary: "Build ${{ env.GIT_OR_MINGIT }} ${{ env.GIT_VERSION }} artifacts from commit ${{ env.GIT_REV }}${{ env.TAG_GIT_WORKFLOW_RUN_ID && format(' (tag-git run #{0})', env.TAG_GIT_WORKFLOW_RUN_ID) || '' }}" text: "For details, see [this run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id}})." details-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id}}" - name: Re-publish bundle-artifacts so the next job can easily use it @@ -167,27 +238,59 @@ jobs: echo "MINGW_PREFIX=$MINGW_PREFIX" >> $GITHUB_OUTPUT echo "MINGW_PACKAGE_PREFIX=$MINGW_PACKAGE_PREFIX" >> $GITHUB_ENV echo "MINGW_PACKAGE_PREFIX=$MINGW_PACKAGE_PREFIX" >> $GITHUB_OUTPUT + echo "SDK_REPO_ARCH=$SDK_REPO_ARCH" >> $GITHUB_ENV echo "SDK_REPO_ARCH=$SDK_REPO_ARCH" >> $GITHUB_OUTPUT test -n "$ARTIFACTS_TO_BUILD" || { ARTIFACTS_TO_BUILD="mingit" - test "$ARCHITECTURE" = i686 || ARTIFACTS_TO_BUILD="installer portable archive $ARTIFACTS_TO_BUILD" test "$ARCHITECTURE" = aarch64 || ARTIFACTS_TO_BUILD="$ARTIFACTS_TO_BUILD mingit-busybox" - test "$ARCHITECTURE" != x86_64 || ARTIFACTS_TO_BUILD="$ARTIFACTS_TO_BUILD nuget" + test -f bundle-artifacts/mingit-only || { + case "$ARCHITECTURE,$GIT_VERSION" in + i686,v2.49*|i686,v2[5-9][0-9]*) ;; # only build MinGit for i686 Git 2.49+ + *) + ARTIFACTS_TO_BUILD="installer portable archive $ARTIFACTS_TO_BUILD" + ;; + esac + test "$ARCHITECTURE" != x86_64 || ARTIFACTS_TO_BUILD="$ARTIFACTS_TO_BUILD nuget" + } } echo "ARTIFACTS_TO_BUILD=$ARTIFACTS_TO_BUILD" >> $GITHUB_ENV echo "PKG_CACHE_KEY=pkg-$GIT_VERSION-$ARCHITECTURE-$TAG_GIT_WORKFLOW_RUN_ID" >> $GITHUB_ENV + + if test -z "$RELEASE_BRANCH" + then + RELEASE_BRANCH="$(cat bundle-artifacts/release-branch)" + if test -z "$RELEASE_BRANCH" + then + echo "No release branch found in bundle-artifacts/release-branch" + exit 1 + fi + echo "RELEASE_BRANCH=$RELEASE_BRANCH" >> $GITHUB_ENV + fi + echo "release-branch=$RELEASE_BRANCH" >> $GITHUB_OUTPUT - name: Configure user run: USER_NAME="${{github.actor}}" && USER_EMAIL="${{github.actor}}@users.noreply.github.com" && - mkdir "$HOME" && + mkdir -p "$HOME" && git config --global user.name "$USER_NAME" && git config --global user.email "$USER_EMAIL" && echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >> $GITHUB_ENV - uses: git-for-windows/setup-git-for-windows-sdk@v1 + if: env.OWNER == 'git-for-windows' && env.RELEASE_BRANCH == 'main' with: flavor: build-installers architecture: ${{env.architecture}} + - name: Set up Git for Windows SDK ${{ env.architecture }} (${{ env.RELEASE_BRANCH }}) + if: env.OWNER != 'git-for-windows' || env.RELEASE_BRANCH != 'main' + shell: bash + run: | + git -c checkout.workers=56 \ + clone --single-branch -b "$RELEASE_BRANCH" --depth 1 \ + https://github.com/$OWNER/git-sdk-$SDK_REPO_ARCH D:/git-sdk-$SDK_REPO_ARCH && + + cygpath -aw D:/git-sdk-$SDK_REPO_ARCH/usr/bin/core_perl >>$GITHUB_PATH && + cygpath -aw D:/git-sdk-$SDK_REPO_ARCH/usr/bin >>$GITHUB_PATH && + cygpath -aw D:/git-sdk-$SDK_REPO_ARCH/${MSYSTEM,,?}/bin >>$GITHUB_PATH - name: Create artifact build matrix uses: actions/github-script@v8 id: artifact-build-matrix @@ -196,7 +299,11 @@ jobs: core.info('Preparing artifact build matrix...') const createArtifactsMatrix = require('./create-artifacts-matrix') try { - const output = createArtifactsMatrix(process.env.ARTIFACTS_TO_BUILD, process.env.ARCHITECTURE) + const output = createArtifactsMatrix( + process.env.ARTIFACTS_TO_BUILD, + process.env.ARCHITECTURE, + ${{ github.repository_visibility == 'private' && 'true' || 'false' }} + ) core.info(`Will be using the following matrix: ${JSON.stringify(output)}`) return output } catch (e) { @@ -214,35 +321,33 @@ jobs: d=/usr/src/build-extra && if test ! -d $d/.git then - git clone --single-branch -b main https://github.com/git-for-windows/build-extra $d + git clone --single-branch -b "$RELEASE_BRANCH" https://github.com/$OWNER/build-extra $d else - git -C $d fetch https://github.com/git-for-windows/build-extra main && - git -C $d switch -C main FETCH_HEAD + git -C $d fetch https://github.com/$OWNER/build-extra "$RELEASE_BRANCH" && + git -C $d switch -C "$RELEASE_BRANCH" FETCH_HEAD fi && - if test -z "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" + if test -z "$EXISTING_GIT_TAG" then - git -C $d -c pull.rebase=false pull "$PWD"/bundle-artifacts/build-extra.bundle main - else - git -C $d fetch origin "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" && - git -C $d reset --hard "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" + git -C $d -c pull.rebase=false pull "$PWD"/bundle-artifacts/build-extra.bundle "$RELEASE_BRANCH" fi - - name: Prepare git-for-windows/git clone with the tag + - name: Prepare ${{ env.OWNER }}/git clone with the tag if: steps.restore-cached-git-pkg.outputs.cache-hit != 'true' run: | set -x if test ! -d /usr/src/MINGW-packages then - git clone --depth 1 --single-branch -b main \ - https://github.com/git-for-windows/MINGW-packages /usr/src/MINGW-packages + git clone --depth 1 --single-branch -b "$RELEASE_BRANCH" \ + https://github.com/$OWNER/MINGW-packages /usr/src/MINGW-packages fi && cd /usr/src/MINGW-packages/mingw-w64-git && if test ! -d git then + # Must be a clone of git-for-windows/git.git, otherwise `makepkg-mingw` will complain git clone --bare https://github.com/git-for-windows/git.git git fi && if test ! -d src/git then - git clone --reference git https://github.com/git-for-windows/git src/git && + git clone --reference git https://github.com/$OWNER/$REPO src/git && echo ../../../../git/objects >src/git/.git/objects/info/alternates fi && cd src/git && @@ -283,6 +388,7 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ env.OWNER }} append-text: 'About to build the `${{env.MINGW_PACKAGE_PREFIX}}-git` package' - name: Build ${{env.MINGW_PACKAGE_PREFIX}}-git timeout-minutes: 60 @@ -317,7 +423,17 @@ jobs: if test -z "$EXISTING_GIT_TAG" then git commit -s -m "mingw-w64-git: new version ($version)" PKGBUILD && - git bundle create "$b"/MINGW-packages.bundle origin/main..main + for seconds in 1 3 10 15 + do + GIT_TRACE2_EVENT=1 \ + git bundle create "$b"/MINGW-packages.bundle \ + "origin/$RELEASE_BRANCH..$RELEASE_BRANCH" && + break + # to ease debugging + git bundle verify "$b"/MINGW-packages.bundle ||: + sleep $seconds + done && + git bundle verify "$b"/MINGW-packages.bundle elif ! git update-index --ignore-submodules --refresh || ! git diff-files --ignore-submodules || ! git diff-index --cached --ignore-submodules HEAD @@ -325,7 +441,17 @@ jobs: echo "::warning::Uncommitted changes after build!" >&2 && git diff >&2 && git commit -s -m "mingw-w64-git: new version ($version)" PKGBUILD && - git bundle create "$b"/MINGW-packages.bundle main^..main + for seconds in 1 3 10 15 + do + GIT_TRACE2_EVENT=1 \ + git bundle create "$b"/MINGW-packages.bundle \ + "$RELEASE_BRANCH^..$RELEASE_BRANCH" && + break + # to ease debugging + git bundle verify "$b"/MINGW-packages.bundle ||: + sleep $seconds + done && + git bundle verify "$b"/MINGW-packages.bundle fi) - name: Cache ${{env.MINGW_PACKAGE_PREFIX}}-git if: steps.restore-cached-git-pkg.outputs.cache-hit != 'true' @@ -338,6 +464,7 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ env.OWNER }} append-text: 'The `${{env.MINGW_PACKAGE_PREFIX}}-git` package was built successfully' - name: Publish ${{env.MINGW_PACKAGE_PREFIX}}-git uses: actions/upload-artifact@v4 @@ -354,6 +481,7 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ env.OWNER }} append-text: "${{ format('Completed: {0}', job.status) }}." conclusion: ${{ job.status }} artifacts: @@ -364,6 +492,7 @@ jobs: MINGW_PREFIX: ${{ needs.pkg.outputs.mingw-prefix }} MINGW_PACKAGE_PREFIX: ${{ needs.pkg.outputs.mingw_package_prefix }} SDK_REPO_ARCH: ${{ needs.pkg.outputs.sdk_repo_arch }} + RELEASE_BRANCH: ${{ needs.pkg.outputs.release-branch }} strategy: fail-fast: false matrix: ${{ fromJSON(needs.pkg.outputs.artifact_matrix) }} @@ -389,14 +518,45 @@ jobs: name: bundle-artifacts path: bundle-artifacts - uses: git-for-windows/setup-git-for-windows-sdk@v1 + if: env.OWNER == 'git-for-windows' && env.RELEASE_BRANCH == 'main' with: flavor: build-installers architecture: ${{env.ARCHITECTURE}} + - name: configure token + if: github.repository_visibility == 'private' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs') + if (!fs.existsSync(process.env.HOME)) fs.mkdirSync(process.env.HOME) + const { callGit, getPushAuthorizationHeader } = require('./repository-updates.js') + for (const repo of ['build-extra', 'git-sdk-${{ env.ARCHITECTURE == 'x86_64' && '64' || (env.ARCHITECTURE == 'aarch64' && 'arm64' || '32') }}']) { + const header = await getPushAuthorizationHeader( + console, + core.setSecret, + ${{ secrets.GH_APP_ID }}, + ${{ toJSON(secrets.GH_APP_PRIVATE_KEY) }}, + process.env.OWNER, + repo + ) + console.log(callGit(['config', '--global', `http.https://github.com/${process.env.OWNER}/${repo}.extraHeader`, header])) + } + - name: Set up Git for Windows SDK ${{ env.architecture }} (${{ env.RELEASE_BRANCH }}) + if: env.OWNER != 'git-for-windows' || env.RELEASE_BRANCH != 'main' + shell: bash + run: | + git -c checkout.workers=56 \ + clone --single-branch -b "$RELEASE_BRANCH" --depth 1 \ + https://github.com/$OWNER/git-sdk-$SDK_REPO_ARCH D:/git-sdk-$SDK_REPO_ARCH && + + cygpath -aw D:/git-sdk-$SDK_REPO_ARCH/usr/bin/core_perl >>$GITHUB_PATH && + cygpath -aw D:/git-sdk-$SDK_REPO_ARCH/usr/bin >>$GITHUB_PATH && + cygpath -aw D:/git-sdk-$SDK_REPO_ARCH/${MSYSTEM,,?}/bin >>$GITHUB_PATH - name: Configure user run: USER_NAME="${{github.actor}}" && USER_EMAIL="${{github.actor}}@users.noreply.github.com" && - mkdir "$HOME" && + mkdir -p "$HOME" && git config --global user.name "$USER_NAME" && git config --global user.email "$USER_EMAIL" - name: Clone and update build-extra @@ -405,18 +565,15 @@ jobs: d=/usr/src/build-extra && if test ! -d $d/.git then - git clone --single-branch -b main https://github.com/git-for-windows/build-extra $d + git clone --single-branch -b "$RELEASE_BRANCH" https://github.com/$OWNER/build-extra $d else - git -C $d fetch https://github.com/git-for-windows/build-extra main && - git -C $d switch -C main FETCH_HEAD + git -C $d fetch https://github.com/$OWNER/build-extra "$RELEASE_BRANCH" && + git -C $d switch -C "$RELEASE_BRANCH" FETCH_HEAD fi && echo "result=$(cygpath -am "$d")" >> $GITHUB_OUTPUT && - if test -z "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" + if test -z "$EXISTING_GIT_TAG" then - git -C $d -c pull.rebase=false pull "$PWD"/bundle-artifacts/build-extra.bundle main - else - git -C $d fetch origin "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" && - git -C $d reset --hard "$BUILD_EXTRA_REV_FOR_EXISTING_GIT_TAG" + git -C $d -c pull.rebase=false pull "$PWD"/bundle-artifacts/build-extra.bundle "$RELEASE_BRANCH" fi - name: Prepare home directory for code-signing env: @@ -522,20 +679,26 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ env.OWNER }} append-text: 'Built ${{ matrix.artifact.name }}' - name: Run the installer if: matrix.artifact.name == 'installer' shell: pwsh run: | $exePath = Get-ChildItem -Path artifacts/*.exe | %{$_.FullName} - $installer = Start-Process -PassThru -Wait -FilePath "$exePath" -ArgumentList "/SILENT /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /ALLOWDOWNGRADE=1 /LOG=installer.log" + $installer = Start-Process -PassThru -Wait -FilePath "$exePath" -ArgumentList "/SILENT /VERYSILENT /NORESTART /SUPPRESSMSGBOXES /ALLOWDOWNGRADE=1 /ALLOWINSTALLING32ON64=1 /LOG=installer.log" $exitCode = $installer.ExitCode if ($exitCode -ne 0) { Write-Host "::error::Installer failed with exit code $exitCode!" exit 1 } - "$env:ProgramFiles\Git\usr\bin" | Out-File -Encoding ascii -Append $env:GITHUB_PATH - "$env:ProgramFiles\Git\${{env.MINGW_PREFIX}}\bin" | Out-File -Encoding ascii -Append $env:GITHUB_PATH + if ("${env:ARCHITECTURE}" -eq "i686") { + "${env:ProgramFiles(x86)}\Git\usr\bin" | Out-File -Encoding ascii -Append $env:GITHUB_PATH + "${env:ProgramFiles(x86)}\Git\${{env.MINGW_PREFIX}}\bin" | Out-File -Encoding ascii -Append $env:GITHUB_PATH + } else { + "$env:ProgramFiles\Git\usr\bin" | Out-File -Encoding ascii -Append $env:GITHUB_PATH + "$env:ProgramFiles\Git\${{env.MINGW_PREFIX}}\bin" | Out-File -Encoding ascii -Append $env:GITHUB_PATH + } - name: Publish installer log if: matrix.artifact.name == 'installer' && (failure() || success()) uses: actions/upload-artifact@v4 @@ -570,6 +733,7 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ env.OWNER }} append-text: "${{ format('Completed: {0}', job.status) }}." conclusion: ${{ job.status }} sha256sums: @@ -604,5 +768,6 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + owner: ${{ env.OWNER }} append-text: "${{ job.status == 'success' && 'Done!' || format('Completed: {0}', job.status) }}." conclusion: ${{ job.status }} diff --git a/.github/workflows/tag-git.yml b/.github/workflows/tag-git.yml index 69475047..4e859bcf 100644 --- a/.github/workflows/tag-git.yml +++ b/.github/workflows/tag-git.yml @@ -1,5 +1,5 @@ name: tag-git -run-name: "Tag Git ${{ inputs.snapshot == 'true' && 'snapshot ' || '' }}at ${{ inputs.owner }}/${{ inputs.repo }}@${{ inputs.rev }}" +run-name: "Tag ${{ inputs.mingit-only == 'true' && 'Min' || '' }}Git ${{ inputs.snapshot == 'true' && 'snapshot ' || '' }}at ${{ inputs.owner }}/${{ inputs.repo }}@${{ inputs.rev }}" on: workflow_dispatch: @@ -11,22 +11,36 @@ on: owner: description: 'Optionally override from where to fetch the specified rev' required: true - default: git-for-windows + default: repo: description: 'Optionally override from where to fetch the specified rev' required: true default: git + release-branch: + description: 'The branch name to use for this release' + required: false + default: main + release-date: + description: 'Override the release date (defaults to today)' + required: false snapshot: description: 'A flag indicating whether this is a snapshot or a full Git for Windows release' required: true default: "true" + mingit-only: + description: 'A flag indicating whether this is a MinGit-only release (AKA MinGit backport)' + required: false + default: "false" env: GPG_OPTIONS: "--batch --yes --no-tty --list-options no-show-photos --verify-options no-show-photos --pinentry-mode loopback" - OWNER: "${{github.event.inputs.owner}}" + OWNER: "${{github.event.inputs.owner || github.repository_owner }}" REPO: "${{github.event.inputs.repo}}" REV: "${{github.event.inputs.rev}}" + RELEASE_BRANCH: "${{github.event.inputs.release-branch}}" + RELEASE_DATE: "${{github.event.inputs.release-date}}" SNAPSHOT: "${{github.event.inputs.snapshot}}" + MINGIT_ONLY: "${{github.event.inputs.mingit-only}}" CREATE_CHECK_RUN: "true" NODEJS_VERSION: 16 @@ -59,19 +73,38 @@ jobs: repo: ${{ env.REPO }} rev: ${{ env.REV }} check-run-name: "tag-git" - title: "Tag Git @${{ env.REV }}" - summary: "Tag Git @${{ env.REV }}" + title: "Tag ${{ env.MINGIT_ONLY == 'true' && 'Min' || '' }}Git @${{ env.REV }}" + summary: "Tag ${{ env.MINGIT_ONLY == 'true' && 'Min' || '' }}Git @${{ env.REV }}" text: "For details, see [this run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id}})." details-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id}}" + - name: configure token + if: github.repository_visibility == 'private' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs') + if (!fs.existsSync(process.env.HOME)) fs.mkdirSync(process.env.HOME) + const { callGit, getPushAuthorizationHeader } = require('./repository-updates.js') + for (const repo of ['build-extra', process.env.REPO]) { + const header = await getPushAuthorizationHeader( + console, + core.setSecret, + ${{ secrets.GH_APP_ID }}, + ${{ toJSON(secrets.GH_APP_PRIVATE_KEY) }}, + process.env.OWNER, + repo + ) + console.log(callGit(['config', '--global', `http.https://github.com/${process.env.OWNER}/${repo}.extraHeader`, header])) + } - name: Configure user - run: + run: | USER_NAME="${{github.actor}}" && USER_EMAIL="${{github.actor}}@users.noreply.github.com" && git config --global user.name "$USER_NAME" && git config --global user.email "$USER_EMAIL" && echo "PACKAGER=$USER_NAME <$USER_EMAIL>" >> $GITHUB_ENV - name: Clone build-extra - run: git clone --single-branch -b main https://github.com/git-for-windows/build-extra $RUNNER_TEMP/build-extra + run: git clone --single-branch -b "$RELEASE_BRANCH" https://github.com/$OWNER/build-extra $RUNNER_TEMP/build-extra - name: Prepare home directory for GPG signing if: env.GPGKEY != '' run: | @@ -93,15 +126,28 @@ jobs: GPGKEY: ${{secrets.GPGKEY}} run: | git clone --bare --filter=blob:none https://github.com/git/git && - git -C git.git fetch --tags --filter=blob:none "https://github.com/$OWNER/$REPO" main:refs/remotes/origin/main "$REV" && + if test git-for-windows != "$OWNER" + then + git --git-dir=git.git fetch --tags --filter=blob:none https://github.com/git-for-windows/git + fi && + git -C git.git fetch --tags --filter=blob:none "https://github.com/$OWNER/$REPO" "$RELEASE_BRANCH:refs/remotes/origin/$RELEASE_BRANCH" "$REV" && sh -x "$GITHUB_WORKSPACE/update-scripts/tag-git.sh" \ ${{ env.SNAPSHOT != 'true' && '--no-snapshot-version' || ''}} \ --git-dir="git.git" \ --build-extra-dir="$RUNNER_TEMP/build-extra" \ --artifacts-dir="$GITHUB_WORKSPACE/bundle-artifacts" \ + --release-branch="$RELEASE_BRANCH" \ + --release-date="$RELEASE_DATE" \ + $(test "$MINGIT_ONLY" = 'true' && echo '--mingit') \ "$REV" && + echo "$RELEASE_BRANCH" >bundle-artifacts/release-branch && + if test true = "$MINGIT_ONLY" + then + >bundle-artifacts/mingit-only + fi && + echo "tag-name=$(cat bundle-artifacts/next_version)" >>$GITHUB_OUTPUT && echo "Tag name: \`$(cat bundle-artifacts/next_version)\`" >>$GITHUB_STEP_SUMMARY - name: update check-run @@ -110,9 +156,9 @@ jobs: with: app-id: ${{ secrets.GH_APP_ID }} private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - title: "Tag Git ${{ steps.generate.outputs.tag-name }} @${{ env.REV }}" - summary: "Tag Git ${{ steps.generate.outputs.tag-name }} @${{ env.REV }}" - append-text: 'Tagged Git ${{ steps.generate.outputs.tag-name }}' + title: "Tag ${{ env.MINGIT_ONLY == 'true' && 'Min' || '' }}Git ${{ steps.generate.outputs.tag-name }} @${{ env.REV }}" + summary: "Tag ${{ env.MINGIT_ONLY == 'true' && 'Min' || '' }}Git ${{ steps.generate.outputs.tag-name }} @${{ env.REV }}" + append-text: "Tagged ${{ env.MINGIT_ONLY == 'true' && 'Min' || '' }}Git ${{ steps.generate.outputs.tag-name }}" - name: 'Publish Pipeline Artifact: bundle-artifacts' uses: actions/upload-artifact@v4 with: diff --git a/create-artifacts-matrix.js b/create-artifacts-matrix.js index 27670dd7..6457355e 100644 --- a/create-artifacts-matrix.js +++ b/create-artifacts-matrix.js @@ -1,4 +1,4 @@ -module.exports = (artifactsString, architecture) => { +module.exports = (artifactsString, architecture, allowI686) => { const artifacts = artifactsString.split(' ') if (artifacts.length < 1) { @@ -13,7 +13,7 @@ module.exports = (artifactsString, architecture) => { } ] - if (architecture !== 'i686') validArtifacts.unshift({ + if (allowI686 || architecture !== 'i686') validArtifacts.unshift({ name: 'installer', filePrefix: 'Git', fileExtension: 'exe' diff --git a/github-release.js b/github-release.js index 31b6a77f..d5ea34f9 100644 --- a/github-release.js +++ b/github-release.js @@ -209,8 +209,8 @@ const getGitArtifacts = async ( const urls = await getWorkflowRunArtifactsURLs(context, token, owner, repo, workflowRunId) for (const artifact of artifacts) { if (architecture.name === 'aarch64' && artifact.name === 'mingit-busybox') continue - if (architecture.name === 'i686' && !artifact.name.startsWith('mingit')) continue const name = `${artifact.name}-${architecture.name}` + if (architecture.name === 'i686' && !artifact.name.startsWith('mingit') && !urls && !fs.existsSync(name)) continue context.log(`Downloading ${name}`) await downloadAndUnZip(token, urls[name], name) diff --git a/update-scripts/tag-git.sh b/update-scripts/tag-git.sh index 4d5de870..7b089fa4 100755 --- a/update-scripts/tag-git.sh +++ b/update-scripts/tag-git.sh @@ -8,29 +8,76 @@ die () { git_git_dir=/usr/src/git/.git && build_extra_dir=/usr/src/build-extra && artifacts_dir= && +release_branch=main && +git_or_mingit="Git for Windows" && snapshot_version=t && +release_date= && while case "$1" in --git-dir=*) git_git_dir="${1#*=}";; --build-extra-dir=*) build_extra_dir="${1#*=}";; --artifacts-dir=*) artifacts_dir="${1#*=}";; +--release-branch=*) release_branch="${1#*=}";; +--mingit) git_or_mingit="MinGit for Windows";; --full|--full-version|--no-snapshot|--no-snapshot-version) snapshot_version=;; +--release-date=*) release_date="${1#*=}";; *) break;; -esac; do shift; done +esac; do shift; done || +die "Could not parse command-line options: $*" test $# = 1 || die "Usage: $0 [--no-snapshot-version] [--git-dir=] [--build-extra-dir=] [--artifacts-dir=] " git_rev="$1" -test "refs/heads/main" = "$(git -C "$build_extra_dir" symbolic-ref HEAD)" || -die "Need the current branch in '$build_extra_dir' to be 'main'" +test "refs/heads/$release_branch" = "$(git -C "$build_extra_dir" symbolic-ref HEAD)" || +die "Need the current branch in '$build_extra_dir' to be '$release_branch'" + +# This function was copied from `git-update-git-for-windows` in +# the `mingw-w64-git-extra` package. +version_compare () { + a="$1" + b="$2" + + while true + do + test -n "$b" || { echo 1; return; } + test -n "$a" || { echo -1; return; } + + # Get the first numbers (if any) + a1="$(expr "$a" : '^\([0-9]*\)')"; a="${a#$a1}" + b1="$(expr "$b" : '^\([0-9]*\)')"; b="${b#$b1}" + + if test -z "$b1" + then + test -z "$a1" || { echo 1; return; } + a1=0 + b1=0 + fi + test -n "$a1" || { echo -1; return; } + test $a1 -le $b1 || { echo 1; return; } + test $b1 -le $a1 || { echo -1; return; } + + # Skip non-numeric prefixes + a1="$(expr "$a" : '^\([^0-9]\+\)')"; a="${a#$a1}" + b1="$(expr "$b" : '^\([^0-9]\+\)')"; b="${b#$b1}" + + case "$a1,$b1" in + [.-]rc,[.-]rc) ;; # both are -rc versions + [.-]rc,*) echo -1; return;; + *,[.-]rc) echo 1; return;; + esac + done +} mkdir -p "$artifacts_dir" && if test -n "$snapshot_version" then - tag_name="$(git -C "$git_git_dir" describe --match 'v[0-9]*' --exclude='*-[0-9]*' "$git_rev")-$(date +%Y%m%d%H%M%S)" && + test -z "$release_date" || + die 'Cannot specify --release-date with --no-snapshot-version' + + tag_name="$(git --git-dir "$git_git_dir" describe --match 'v[0-9]*' --exclude='*-[0-9]*' "$git_rev")-$(date +%Y%m%d%H%M%S)" && tag_message="Snapshot build" && - release_note="Snapshot of $(git -C "$git_git_dir" show -s --pretty='tformat:%h (%s, %ad)' --date=short "$git_rev")" && + release_note="Snapshot of $(git --git-dir "$git_git_dir" show -s --pretty='tformat:%h (%s, %ad)' --date=short "$git_rev")" && (cd "$build_extra_dir" && node ./add-release-note.js --commit feature "$release_note") && display_version=${tag_name#v} && ver=prerelease-${tag_name#v} @@ -40,8 +87,15 @@ else die "Need 'w3m' to render release notes" fi - desc="$(git -C "$git_git_dir" describe --match 'v[0-9]*[0-9]' --exclude='*-[0-9]*' --first-parent "$git_rev")" && + desc="$(git --git-dir "$git_git_dir" describe --match 'v[0-9]*[0-9]' --exclude='*-[0-9]*' --exclude='*.windows.*' "$git_rev")" && base_tag=${desc%%-[1-9]*} && + desc2="$(git --git-dir "$git_git_dir" describe --match 'v[0-9]*[0-9]' --exclude='*-[0-9]*' "$git_rev")" && + base_tag2=${desc2%%-[1-9]*} && + # If there is a candidate Git tag _and_ a candidate Git for Windows tag, compare versions + if test "$base_tag" != "$base_tag2" && test $(version_compare "$base_tag" "$base_tag2") -lt 0 + then + base_tag="$base_tag2" + fi && case "$base_tag" in "$desc") die "Revision '$git_rev' already tagged as $base_tag";; *.windows.*) @@ -55,7 +109,11 @@ else if ! grep -q "^\\* Comes with \\[Git $base_tag\\]" "$build_extra_dir"/ReleaseNotes.md then url=https://github.com/git/git/blob/$base_tag && - adoc="$(echo "${base_tag#v}" | sed 's/-rc[0-9]*$//').adoc" && + case "$base_tag" in + v2.43.7|v2.44.4|v2.45.4|v2.46.4|v2.47.3|v2.48.2|v2.49.1) ext=txt;; + *) ext=adoc;; + esac && + adoc="$(echo "${base_tag#v}" | sed 's/-rc[0-9]*$//').$ext" && url=$url/Documentation/RelNotes/$adoc && release_note="Comes with [Git $base_tag]($url)." && (cd "$build_extra_dir" && node ./add-release-note.js --commit feature "$release_note") @@ -65,11 +123,14 @@ else ver="$(echo "${tag_name#v}" | sed -n \ 's/^\([0-9]*\.[0-9]*\.[0-9]*\(-rc[0-9]*\)\?\)\.windows\(\.1\|\(\.[0-9]*\)\)$/\1\4/p')" && - release_date="$(LC_ALL=C date +"%B %-d %Y" | - sed -e 's/\( [2-9]\?[4-90]\| 1[0-9]\) /\1th /' \ - -e 's/1 /1st /' -e 's/2 /2nd /' -e 's/3 /3rd /' - )" && - sed -i -e "1s/.*/# Git for Windows v$display_version Release Notes/" \ + if test -z "$release_date" + then + release_date="$(LC_ALL=C date +"%B %-d %Y" | + sed -e 's/\( [2-9]\?[4-90]\| 1[0-9]\) /\1th /' \ + -e 's/1 /1st /' -e 's/2 /2nd /' -e 's/3 /3rd /' + )" + fi && + sed -i -e "1s/.*/# $git_or_mingit v$display_version Release Notes/" \ -e "2s/.*/Latest update: $release_date/" \ "$build_extra_dir"/ReleaseNotes.md && git -C "$build_extra_dir" commit -s \ @@ -78,10 +139,11 @@ else raw_notes="$(sed -n "/^## Changes since/,\${:1;p;n;/^## Changes/q;b1}" \ <"$build_extra_dir"/ReleaseNotes.md)" && notes="$(echo "$raw_notes" | + sed '1s/^## Changes since.*/&:/' | markdown | LC_CTYPE=C w3m -dump -cols 72 -T text/html)" && tag_message="$(printf "%s\n\n%s" \ - "$(sed -n '1s/.*\(Git for Windows v[^ ]*\).*/\1/p' \ + "$(sed -n '1s/.* \(\(Min\)\?Git for Windows v[^ ]*\).*/\1/p' \ <"$build_extra_dir"/ReleaseNotes.md)" "$notes")" && cat >"$artifacts_dir"/release-notes-$display_version <<-EOF && @@ -92,11 +154,11 @@ else @@CHECKSUMS@@ EOF - case "$display_version" in - prerelease-*) + case "$git_or_mingit,$display_version" in + *,prerelease-*) url=https://gitforwindows.org/git-snapshots/ ;; - *-rc*) + *,*-rc*|MinGit*) url=https://github.com/git-for-windows/git/releases/tag/$tag_name ;; *) @@ -108,7 +170,7 @@ else From: $(git var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/') Date: $(date -R) To: git@vger.kernel.org, git-packagers@googlegroups.com - Subject: [ANNOUNCE] Git for Windows $display_version + Subject: [ANNOUNCE] $git_or_mingit $display_version Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIME-Version: 1.0 @@ -116,7 +178,7 @@ else Dear Git users, - I hereby announce that Git for Windows $display_version is available from: + I hereby announce that $git_or_mingit $display_version is available from: $url @@ -133,9 +195,9 @@ echo "$ver" >"$artifacts_dir"/ver && echo "$display_version" >"$artifacts_dir"/display_version && echo "$tag_name" >"$artifacts_dir"/next_version && echo "$tag_message" >"$artifacts_dir"/tag-message && -git -C "$git_git_dir" rev-parse --verify "$git_rev"^0 >"$artifacts_dir"/git-commit-oid && +git --git-dir "$git_git_dir" rev-parse --verify "$git_rev"^0 >"$artifacts_dir"/git-commit-oid && -git -C "$git_git_dir" tag $(test -z "$GPGKEY" || echo " -s") -m "$tag_message" "$tag_name" "$git_rev" && -git -C "$git_git_dir" bundle create "$artifacts_dir"/git.bundle origin/main.."$tag_name" && +git --git-dir "$git_git_dir" tag $(test -z "$GPGKEY" || echo " -s") -m "$tag_message" "$tag_name" "$git_rev" && +git --git-dir "$git_git_dir" bundle create "$artifacts_dir"/git.bundle origin/$release_branch.."$tag_name" && -git -C "$build_extra_dir" bundle create "$artifacts_dir"/build-extra.bundle origin/main..main \ No newline at end of file +git -C "$build_extra_dir" bundle create "$artifacts_dir"/build-extra.bundle origin/$release_branch..$release_branch \ No newline at end of file