{"id":23763821,"url":"https://github.com/tomashubelbauer/github-actions-push-api","last_synced_at":"2026-04-30T13:32:01.817Z","repository":{"id":107986128,"uuid":"479955037","full_name":"TomasHubelbauer/github-actions-push-api","owner":"TomasHubelbauer","description":"An example of using the GitHub API in a GitHub Actions workflow to create and modify repository files","archived":false,"fork":false,"pushed_at":"2022-04-14T20:20:44.000Z","size":218,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-01T16:42:09.065Z","etag":null,"topics":["github-actions","github-api"],"latest_commit_sha":null,"homepage":"","language":"Markdown","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TomasHubelbauer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-04-10T08:18:40.000Z","updated_at":"2022-04-16T12:48:53.000Z","dependencies_parsed_at":"2023-05-15T00:15:22.409Z","dependency_job_id":null,"html_url":"https://github.com/TomasHubelbauer/github-actions-push-api","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/TomasHubelbauer/github-actions-push-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomasHubelbauer%2Fgithub-actions-push-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomasHubelbauer%2Fgithub-actions-push-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomasHubelbauer%2Fgithub-actions-push-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomasHubelbauer%2Fgithub-actions-push-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TomasHubelbauer","download_url":"https://codeload.github.com/TomasHubelbauer/github-actions-push-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TomasHubelbauer%2Fgithub-actions-push-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32466333,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"ssl_error","status_checked_at":"2026-04-30T13:12:06.837Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["github-actions","github-api"],"created_at":"2024-12-31T22:13:37.726Z","updated_at":"2026-04-30T13:32:01.812Z","avatar_url":"https://github.com/TomasHubelbauer.png","language":"Markdown","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GitHub Actions Push API\n\n[\n  ![creation](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/creation.yml/badge.svg)\n](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/creation.yml)\n[\n  ![modification](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/modification.yml/badge.svg)\n](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/modification.yml)\n[\n  ![graphql](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/graphql.yml/badge.svg)\n](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/graphql.yml)\n\n## Preface\n\nI have come up with a way to push changes from a GitHub Actions workflow back to\nthe repository already here:\n\nhttps://github.com/tomasHubelbauer/github-actions#write-workflow\n\nIt relies on setting up one's own Git identity and then pushing from the GitHub\nActions workflow to the repository using Git. The integration PAT token is used\nfor authentication, which lets GitHub know the commit was automated and that it\nshould not run the workflow for it.\n\nIt is also possible to hit the GitHub API with the integration PAT. I have repos\nwhere I use it to automatically curate GitHub Issues, for example. An issue made\nusing the workflow PAT will have `github-actions` for its author and the GitHub\nhandle. The link of the handle leads to the GitHub Actions landing page, not a\nnormal GitHub user account page.\n\nOne downside of using one's own Git identity and pushing to GitHub using Git and\nthe integration PAT is that such commits count as one's contributions in the GH\ncontribution chart on the user profile page. One could set up any other kind of\nidentity for the commit as GitHub doesn't check the emails associated with the\ncommits, it just makes them into handles if it is an email known to GitHub. So,\nin order to avoid this, a new email could be used for the automated commits so\nthat they are attributed to the service account / fake email. However, I don't\nlike this option. I do not like programmatic accounts that look like personal\naccounts. GitHub doesn't have support for real service accounts, but it has one\nthrough this `github-actions` special account it assigns the automatically made\nissues to.\n\nSide note: the `ghost` account is also a special account, but I don't consider\nit to be a service account as it is just an account GitHub special-cased for a\nstand-in for deleted users. It even has its own user page. The `github-actions`\none on the other hand has no user page, like was already stated, it leads to the\nGitHub Actions landing page, clearly marking it as different from a normal user\naccount.\n\nCould I make GitHub make a commit appear as thought it was coming from the same\nservice account it assigns the issues to? I can't use that account for a Git\nidentity as it has no email, so there is no magical email I can author a commit\nwith to make it appear that way. I could call the Contents API with the PAT from\nthe workflow and not specify `committer` and `author` as those seem optional.\nWill that work? It says it will use the identity associated with the PAT used,\nbut if there's none since it is the integration PAT, what will happen?\n\n## Results\n\nI was able to use the GitHub API to make a file that appears as pushed by the\nGitHub Actions service account by using the integration PAT and omitting the\n`committer` and `author` fields in the request payload. These fields default to\nthe authenticated user which in the case of the integration PAT is the GitHub\nActions service account. See:\n\n- [`creation.yml`](https://github.com/TomasHubelbauer/github-actions-push-api/blob/main/.github/workflows/creation.yml)\n- [`modification.yml`](https://github.com/TomasHubelbauer/github-actions-push-api/blob/main/.github/workflows/modification.yml)\n\nEven multiple-file changes are possible, but need to be done using the GQL API:\n\n[`graphql.yml`](https://github.com/TomasHubelbauer/github-actions-push-api/blob/main/.github/workflows/graphql.yml)\n\n## Notes\n\n### API Response\n\nThe API response with the `author` and `committer` fields coerced to defaults,\nthe authenticated user - GitHub Actions service account in the case of the\nintegration PAT, looks like this:\n\n```json\n\"author\": {\n  \"name\": \"github-actions[bot]\",\n  \"email\": \"41898282+github-actions[bot]@users.noreply.github.com\"\n},\n\"committer\": {\n  \"name\": \"GitHub\",\n  \"email\": \"noreply@github.com\"\n}\n```\n\nI am not sure what the number `41898282` represents. I am guessing it might be\nmy GitHub user ID. It is constant across workflow runs and between workflows.\n\nThis `committer` and `author` objects appear like this even when I use the Basic\nauth option and pass in my GitHub handle as the user name:\n`curl -u ${{github.repository_owner}}:${{github.token}}`\n\nThe user name seems to be completely ignored and just the PAT in the password is\nused.\n\n### Authorization\n\nThe GitHub API supports Basic authentication where the password is replaced by a\nPAT as well as using the `Authorization` header with a PAT. I tried both in the\nworkflow and both work. The Basic auth option can even have no user name set and\nit still works:\n\n`curl -u :${{github.token}}`\n(or `curl -u ${{github.repository_owner}}:${{github.token}}`)\n\nhttps://docs.github.com/en/rest/overview/other-authentication-methods#via-oauth-and-personal-access-tokens\n\n`curl -H \"Authorization: token ${{github.token}}\"`\n\nhttps://docs.github.com/en/rest/overview/other-authentication-methods#authenticating-for-saml-sso\n\n### Modification\n\nTo modify a file, the same API call is used, but a new field, `sha` is provided.\nThis field is the SHA of the current version's of the file content. An easy way\nto get it without checking out the repository in the workflow is to call the API\nasking for it first. If the file doesn't exist, it will be undefined making the\nsubsequent call behave as a creation call, as desired.\n\nhttps://docs.github.com/en/rest/reference/repos#get-repository-content\n\nSee [`modification.yml`](https://github.com/TomasHubelbauer/github-actions-push-api/actions/workflows/modification.yml).\n\n### Multiple Files / GraphQL\n\nThe REST API doesn't support creating/modifying multiple files in one call. The\nGraphQL API provided by GitHub, however, does. This is implemented in the \n`graphql` workflow.\n\nIf the contents are changed to what's already on the branch, an empty commit\nwill be created. See https://github.com/TomasHubelbauer/github-actions-push-api/commit/20f476\n\nThe identity is like with REST taken from the integration PAT and even querying\nit from the response confirms this:\n\n```json\n{\n  \"data\": {\n    \"createCommitOnBranch\": {\n      \"commit\": {\n        \"commitUrl\": \"https://github.com/TomasHubelbauer/github-actions-push-api/commit/20f476\",\n        \"author\": {\n          \"email\": \"41898282+github-actions[bot]@users.noreply.github.com\",\n          \"name\": \"github-actions[bot]\"\n        },\n        \"committer\": {\n          \"email\": \"noreply@github.com\",\n          \"name\": \"GitHub\"\n        }\n      },\n      \"ref\": {\n        \"name\": \"main\",\n        \"prefix\": \"refs/heads/\"\n      }\n    }\n  }\n}\n```\n\nThe `41898282` number here is the same as in the REST response, but it is a\ndifferent workflow, so that number is very likely something tied to my GitHub\nidentity and not the workflow.\n\nMultiple changes can also be captured as if coming from the GitHub Actions\nservice account by configuring the Git client to use its email and name, see\n[Git](#git).\n\n### Git\n\nGit-based solution where the Git CLI is configured and used in the workflow to\npush the changes back to GitHub doesn't allow one to provide null identity. The\nemail can be left empty, but _a_ name needs to be provided.\n\nIn order to not associate those commits with me, I have switched the name and\nemail from mine to the ones used by the GitHub Actions service account:\n`41898282+github-actions[bot]@users.noreply.github.com` : `github-actions[bot]`.\nThis had the effect of associating the commits with the GitHub Actions service\naccount.\n\nIt's less ideal than using the API in my view as the identity needs to be set up\nexplicitly in the workflow like this, but it's possible.\n\n## To-Do\n\n### Make sure the workflows/scripts fail in case the API calls fail\n\nI need to make sure this is the case so that no errors go unnoticed. I know for\na fact the GraphQL workflow will pass even if the API call fails. I wonder if\nthat's GitHub returning a 200 on error? Is that GraphQL spec being responsible?\nMaybe it's the REST ones, too, but I think cURL would exit with a non-zero on a\nnon-OK HTTP code?\n\nSee [`--fail-with-body`](https://curl.se/docs/manpage.html#--fail-with-body).\n\n### Add JavaScript alternatives to the Bash scripts to include in Node projects\n\nWith no need to call the Git client anymore even for multi-file commits, it'd be\nuseful to have copy-paste friendly scripts to paste in my Node-based projects\nand just have the workflow call the Node script which will take care of the work\nitself as well as the push.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomashubelbauer%2Fgithub-actions-push-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftomashubelbauer%2Fgithub-actions-push-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftomashubelbauer%2Fgithub-actions-push-api/lists"}