From db9a326cbbd510e2510f9c6661a2184a136daa9e Mon Sep 17 00:00:00 2001 From: Dennis Ameling Date: Sat, 14 Sep 2024 11:46:38 +0200 Subject: [PATCH] ci: add ci-artifacts pipeline A similar pipeline already exists in git-sdk-64, so this commit is to port most of that code to git-sdk-arm64. We don't (yet) copy over the test-minimal-sdk job, because it spins up 16 parallel jobs, which is a bit too much for our self-hosted runner budget. We can add those once GitHub-hosted arm64 runners become available for OSS projects by the end of 2024. Ref: https://github.com/git-for-windows/git-for-windows-automation/issues/91 Ref: https://github.blog/news-insights/product-news/arm64-on-github-actions-powering-faster-more-efficient-build-systems/#get-started-using-arm-hosted-runners-today Signed-off-by: Dennis Ameling --- .github/workflows/ci-artifacts.yml | 160 +++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 .github/workflows/ci-artifacts.yml diff --git a/.github/workflows/ci-artifacts.yml b/.github/workflows/ci-artifacts.yml new file mode 100644 index 00000000000..36a3b02eeb6 --- /dev/null +++ b/.github/workflows/ci-artifacts.yml @@ -0,0 +1,160 @@ +name: ci-artifacts + +on: + push: + branches: + - main + pull_request: + +# For the continuous `ci-artifacts` release +permissions: + contents: write + +env: + LC_CTYPE: C.UTF-8 + +jobs: + minimal-sdk-artifact: + if: github.event.repository.fork == false + runs-on: [Windows, ARM64] + steps: + - name: clone git-sdk-arm64 + run: | + git init --bare git-sdk-arm64.git && + git --git-dir=git-sdk-arm64.git remote add origin https://github.com/${{github.repository}} && + git --git-dir=git-sdk-arm64.git config remote.origin.promisor true && + git --git-dir=git-sdk-arm64.git config remote.origin.partialCloneFilter blob:none && + git --git-dir=git-sdk-arm64.git fetch --depth=1 origin ${{github.sha}} && + git --git-dir=git-sdk-arm64.git update-ref --no-deref HEAD ${{github.sha}} + - name: clone build-extra + run: git clone --depth=1 --single-branch -b main https://github.com/git-for-windows/build-extra + - name: build git-sdk-arm64-minimal-sdk + shell: bash + run: | + sh -x ./build-extra/please.sh create-sdk-artifact --sdk=git-sdk-arm64.git minimal-sdk && + cygpath -aw minimal-sdk/usr/bin >>$GITHUB_PATH + - name: compress artifact + shell: bash + run: mkdir artifacts && (cd minimal-sdk && tar cvf - * .[0-9A-Za-z]*) | gzip -1 >artifacts/git-sdk-aarch64-minimal.tar.gz + - name: create zip and 7z SFX variants of the minimal SDK + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + shell: bash + run: | + for path in clangarm64/bin/7z.exe clangarm64/bin/7z.dll clangarm64/lib/7zip/7zCon.sfx clangarm64/bin/libc++.dll + do + git --git-dir=git-sdk-arm64.git show HEAD:$path >${path##*/} + done && + (cd minimal-sdk && ../7z.exe a -mmt=on -mx9 ../artifacts/git-sdk-aarch64-minimal.zip * .?*) && + (cd minimal-sdk && ../7z.exe a -t7z -mmt=on -m0=lzma -mqs -mlc=8 -mx=9 -md=256M -mfb=273 -ms=256M -sfx../7zCon.sfx \ + ../artifacts/git-sdk-aarch64-minimal.7z.exe * .?*) + - name: run some tests + shell: bash + run: | + set -x + . /etc/profile + + # cygpath works + test "$(cygpath -aw /)" = "${{github.workspace}}\minimal-sdk" || exit 1 + + # comes with Clang and can compile a DLL + test "$(type -p clang)" = "/clangarm64/bin/clang" || exit 1 + cat >dll.c <<-\EOF && + __attribute__((dllexport)) int increment(int i) + { + return i + 1; + } + EOF + + clang -Wall -g -O2 -shared -o sample.dll dll.c || exit 1 + ls -la + + # stat works + test "stat is /usr/bin/stat" = "$(type stat)" || exit 1 + stat /usr/bin/stat.exe || exit 1 + + # unzip works + test "unzip is /usr/bin/unzip" = "$(type unzip)" || exit 1 + git init unzip-test && + echo TEST >unzip-test/README && + git -C unzip-test add -A && + git -C unzip-test -c user.name=A -c user.email=b@c.d commit -m 'Testing, testing...' && + git --git-dir=unzip-test/.git archive -o test.zip HEAD && + unzip -v test.zip >unzip-test.out && + cat unzip-test.out && + test "grep is /usr/bin/grep" = "$(type grep)" || exit 1 + grep README unzip-test.out + - name: publish release assets + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: actions/github-script@v7 + with: + script: | + process.chdir('artifacts') + const req = { owner: context.repo.owner, repo: context.repo.repo } + // find or create the GitHub release named `ci-artifacts` + const release = await (async () => { + try { + return await github.rest.repos.getReleaseByTag({ ...req, tag: 'ci-artifacts' }); + } catch (e) { + if (e.status === 404) { + // create the `ci-artifacts` GitHub release based on the current revision + const workflowRunsURL = `${context.serverUrl}/${ + process.env.GITHUB_WORKFLOW_REF.replace(/\/\.github\/workflows\/([^@]+).*/, '/actions/workflows/$1') + }` + return await github.rest.repos.createRelease({ + ... req, + tag_name: 'ci-artifacts', + body: `Continuous release of \`ci-artifacts\` + + This release is automatically updated by the [ci-artifacts](${workflowRunsURL}) workflow. + + For technical reasons, allow up to a minute for release assets to be missing while they are updated.`, + }); + } + throw e; + } + })() + + const fs = require('fs') + for (const fileName of [ + 'git-sdk-aarch64-minimal.tar.gz', + 'git-sdk-aarch64-minimal.zip', + 'git-sdk-aarch64-minimal.7z.exe', + ]) { + console.log(`Uploading ${fileName}`) + const uploadReq = { + ...req, + release_id: release.data.id, + name: fileName, + headers: { + 'content-length': (await fs.promises.stat(fileName)).size, + }, + data: fs.createReadStream(fileName), + } + + // if the asset does not yet exist, simply upload it + const originalAsset = release.data.assets.filter(asset => asset.name === fileName).pop() + if (!originalAsset) { + const asset = await github.rest.repos.uploadReleaseAsset(uploadReq) + console.log(`Uploaded to ${asset.data.browser_download_url}`) + continue + } + + // otherwise upload it using a temporary file name, + // then delete the old asset + // and then rename the new asset; + // this way, the asset is not missing for a long time + const asset = await github.rest.repos.uploadReleaseAsset({ ...uploadReq, name: `tmp.${fileName}` }) + await github.rest.repos.deleteReleaseAsset({ ...req, asset_id: originalAsset.id }) + const updatedAsset = await github.rest.repos.updateReleaseAsset({...req, + asset_id: asset.data.id, + name: fileName, + label: fileName, + }) + console.log(`Updated ${updatedAsset.data.browser_download_url}`) + } + + await github.rest.git.updateRef({ + ...req, + ref: 'tags/ci-artifacts', + sha: process.env.GITHUB_SHA, + }) \ No newline at end of file