From e4a5dc81f33a48ba0bc57cbf0395c29361185b02 Mon Sep 17 00:00:00 2001 From: MrkMrk00 Date: Wed, 25 Mar 2026 14:20:05 +0100 Subject: [PATCH 1/3] feat: signed commit reusable workflow (without GPG keys) --- .github/workflows/commit.yaml | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 .github/workflows/commit.yaml diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml new file mode 100644 index 0000000..c8f83a4 --- /dev/null +++ b/.github/workflows/commit.yaml @@ -0,0 +1,163 @@ +name: Make a commit + +# This workflow allows to make a commit through the GitHub API. +# It exists for one simple reason: the commits made this way are +# automatically signed by GitHub without the need to provide custom +# managed GPG signing keys. +# +# Usage: +# +# my_commit_job: +# outputs: +# patch: ${{ steps.generate-git-patch.outputs.patch }} +# steps: +# ... +# - name: Commit and generate patch +# id: generate-git-patch +# run: | +# # You can put anything here, in the end the commit is going to be assigned +# # to the `github-token` owner. +# git config user.name "github-actions[bot]" +# git config user.email "41898282+github-actions[bot]@users.noreply.github.com" +# +# git add . +# git commit -m "chore(deploy): bump application version [ignore]" # or whatever +# +# patch=$(git format-patch -n1 --stdout HEAD | base64 -w0) +# echo "patch=$patch" >> "$GITHUB_OUTPUT" +# +# my_actually_does_commit_job: +# needs: +# - my_commit_job +# uses: apify/workflows/.github/workflows/commit.yaml@v... +# with: +# patch: ${{ needs.my_commit_job.outputs.patch }} +# branch: 'release/something-test-test-test' +# repository: ${{ github.repository }} +# create-branch: true +# secrets: +# github-token: ${{ needs.generate-app-token-or-whatever.outputs.token }} + +on: + workflow_call: + inputs: + patch: + description: Output of git-format-patch (use `git format-patch --stdout -1 HEAD` after commit). + type: string + required: true + branch: + type: string + required: true + repository: + description: The github.repository in the format /. + type: string + required: true + create-branch: + description: Create the branch from inputs.branch. Will be based on repository HEAD. Otherwise just commit into existing branch. + type: boolean + default: false + required: false + + secrets: + github-token: + description: Github token of the user/bot to commit under + required: true + +jobs: + commit_though_github_api: + runs-on: ubuntu-22.04-arm64 + steps: + - name: Shallow clone the repo + uses: actions/checkout@v6 + with: + repository: ${{ inputs.repository }} + token: ${{ secrets.github-token }} + ref: ${{ case(inputs.create-branch, null, inputs.branch) }} + + - name: Create and checkout target branch + if: ${{ inputs.create-branch }} + env: + TARGET_BRANCH: ${{ inputs.branch }} + run: | + git push origin "HEAD:refs/heads/$TARGET_BRANCH" + git fetch --depth=1 origin "$TARGET_BRANCH:refs/heads/$TARGET_BRANCH" + git checkout "$TARGET_BRANCH" + + - name: Get HEAD SHA + id: get-head-sha + run: echo "head_ref=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + - name: Apply commit from git patch + env: + PATCH: ${{ inputs.patch }} + run: base64 -d <<< "$PATCH" | git am + + - name: Commit through API + uses: actions/github-script@v8 + env: + REPO: ${{ inputs.repository }} + EXPECTED_HEAD_OID: ${{ steps.get-head-sha.outputs.head_ref }} + BRANCH: ${{ inputs.branch }} + with: + github-token: ${{ secrets.github-token }} + script: | + const { execSync } = require('node:child_process'); + const { readFileSync } = require('node:fs'); + + const STATUS = { ADDED: 'A', DELETED: 'D', MODIFIED: 'M' }; + + const commitMessage = execSync('git log -n1 --pretty=%B', { encoding: 'utf8' }).trim(); + const gitFileChanges = execSync('git diff-tree -r --no-commit-id --name-status --no-renames HEAD', { encoding: 'utf8' }) + .split('\n') + .map((line) => line.split(/\s+/).map((part) => part.trim())) + .filter((lineSplit) => lineSplit.length === 2); + + const fileChanges = { additions: [], deletions: [] }; + for (const [status, fileName] of gitFileChanges) { + switch (status) { + case STATUS.ADDED: + case STATUS.MODIFIED: + fileChanges.additions.push({ + path: fileName, + contents: readFileSync(fileName, { encoding: 'base64' }), + }); + break; + + case STATUS.DELETED: + fileChanges.deletions.push({ + path: fileName, + }); + break; + + default: + throw new Error(`unknown git file change status "${status}"`); + } + } + + const { BRANCH, EXPECTED_HEAD_OID, REPO } = process.env; + const messageLines = commitMessage.split('\n'); + + const response = await github.graphql(`\ + mutation Commit($input: CreateCommitOnBranchInput!) { + createCommitOnBranch(input: $input) { + commit { + oid + } + } + } + `, { + input: { + fileChanges, + branch: { + branchName: BRANCH, + repositoryNameWithOwner: REPO, + }, + expectedHeadOid: EXPECTED_HEAD_OID, + message: { + headline: messageLines[0], + body: messageLines.slice(1).join('\n'), + }, + }, + }); + + core.info(`sucessfully created commit "${response.createCommitOnBranch.commit.oid}" on branch ${BRANCH}`); From 976a11f3728a166852a6ceaf1d917ba9612011a0 Mon Sep 17 00:00:00 2001 From: MrkMrk00 Date: Wed, 25 Mar 2026 14:27:51 +0100 Subject: [PATCH 2/3] fix: remove case expression --- .github/workflows/commit.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml index c8f83a4..37cb48a 100644 --- a/.github/workflows/commit.yaml +++ b/.github/workflows/commit.yaml @@ -72,7 +72,7 @@ jobs: with: repository: ${{ inputs.repository }} token: ${{ secrets.github-token }} - ref: ${{ case(inputs.create-branch, null, inputs.branch) }} + ref: ${{ !inputs.create-branch && inputs.branch || '' }} - name: Create and checkout target branch if: ${{ inputs.create-branch }} From 94ada1ca38c846a856abeccc5e9404f43055d838 Mon Sep 17 00:00:00 2001 From: MrkMrk00 Date: Wed, 25 Mar 2026 14:52:01 +0100 Subject: [PATCH 3/3] fix typos --- .github/workflows/commit.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml index 37cb48a..694ce55 100644 --- a/.github/workflows/commit.yaml +++ b/.github/workflows/commit.yaml @@ -64,7 +64,7 @@ on: required: true jobs: - commit_though_github_api: + commit_through_github_api: runs-on: ubuntu-22.04-arm64 steps: - name: Shallow clone the repo @@ -160,4 +160,4 @@ jobs: }, }); - core.info(`sucessfully created commit "${response.createCommitOnBranch.commit.oid}" on branch ${BRANCH}`); + core.info(`successfully created commit "${response.createCommitOnBranch.commit.oid}" on branch ${BRANCH}`);