├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── issue.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc ├── LICENSE ├── README.md ├── action.yml ├── dist └── index.js ├── package.json ├── renovate.json ├── src ├── index.ts └── utils.ts ├── test └── overwrite.file.md └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: jaywcjlove 2 | buy_me_a_coffee: jaywcjlove 3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 20 15 | 16 | - run: npm install 17 | - run: npm run build 18 | # - run: mkdir -p build 19 | 20 | - name: Generate Contributors Images 21 | uses: jaywcjlove/github-action-contributors@main 22 | id: contributors 23 | 24 | - name: Overwrite test/overwrite.file.md 25 | # uses: github-action-modify-file-content@main 26 | uses: ./ 27 | with: 28 | path: test/overwrite.file.md 29 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 30 | overwrite: 'true' 31 | 32 | - name: Modify test test/overwrite.file.md 33 | # uses: jaywcjlove/github-action-modify-file-content@main 34 | uses: ./ 35 | with: 36 | branch: test 37 | path: test/overwrite.file.md 38 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 39 | overwrite: 'true' 40 | 41 | - name: Modify README.md 42 | # uses: jaywcjlove/github-action-modify-file-content@main 43 | uses: ./ 44 | with: 45 | path: README.md 46 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 47 | 48 | - name: Modify README.md 49 | # uses: jaywcjlove/github-action-modify-file-content@main 50 | uses: ./ 51 | with: 52 | openDelimiter: '' 53 | closeDelimiter: '' 54 | path: README.md 55 | body: "different `GAMFC_TABEL` & `GAMFC_TABEL-END` (test)" 56 | 57 | 58 | - name: Modify CONTRIBUTING 59 | uses: ./ 60 | with: 61 | path: README.md 62 | trim_whitespace: false 63 | openDelimiter: '' 64 | closeDelimiter: '' 65 | message: 'chore: update contributors' 66 | body: | 67 | ${{steps.contributors.outputs.htmlList}} 68 | 69 | # - name: Converts Markdown to HTML 70 | # uses: jaywcjlove/markdown-to-html-cli@main 71 | # with: 72 | # output: build/index.html 73 | # github-corners: https://github.com/jaywcjlove/github-action-modify-file-content 74 | # favicon: data:image/svg+xml,🏷️ 75 | 76 | - name: Create idoc config 77 | run: | 78 | cat > idoc.yml << EOF 79 | site: "Modify File Content {{version}}" 80 | menus: 81 | Home: index.html 82 | Apps: 83 | url: https://jaywcjlove.github.io/#/app 84 | target: __blank 85 | Sponsor: 86 | url: https://jaywcjlove.github.io/#/sponsor 87 | target: __blank 88 | footer: | 89 | Sponsor • 90 | Create Tag • 91 | Contributors • 92 | Read File Content • 93 | Generated Badges 94 |
95 | Released under the MIT License. Copyright © {{idocYear}} Kenny Wong
96 | Generated by idoc v{{idocVersion}} 97 | 98 | EOF 99 | 100 | - run: npm install idoc@1 -g 101 | - run: idoc 102 | 103 | - name: Is a tag/release created auto? 104 | id: create_tag 105 | uses: jaywcjlove/create-tag-action@main 106 | with: 107 | # test: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 108 | package-path: ./package.json 109 | 110 | - name: get tag version 111 | id: tag_version 112 | uses: jaywcjlove/changelog-generator@main 113 | 114 | - name: gh-pages README.md 115 | working-directory: dist 116 | run: | 117 | cat > README.md << EOF 118 | Website: https://jaywcjlove.github.io/github-action-modify-file-content 119 | 120 | TEST: 2022-11-29 01:44:16 121 | EOF 122 | 123 | - name: Deploy 124 | uses: peaceiris/actions-gh-pages@v4 125 | with: 126 | commit_message: ${{steps.tag_version.outputs.tag}} ${{ github.event.head_commit.message }} 127 | github_token: ${{ secrets.GITHUB_TOKEN }} 128 | publish_dir: ./dist 129 | 130 | - name: Generate Changelog 131 | id: changelog 132 | uses: jaywcjlove/changelog-generator@main 133 | with: 134 | head-ref: ${{steps.create_tag.outputs.version}} 135 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 136 | 137 | - name: Create Release 138 | uses: jaywcjlove/create-tag-action@main 139 | with: 140 | # test: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 141 | package-path: ./package.json 142 | release: true 143 | body: | 144 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) 145 | 146 | Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/jaywcjlove/github-action-modify-file-content/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html 147 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }} 148 | 149 | ${{ steps.changelog.outputs.changelog }} 150 | 151 | ```yml 152 | - name: Modify README.md 153 | uses: jaywcjlove/github-action-modify-file-content@main 154 | with: 155 | path: README.md 156 | ``` 157 | 158 | `README.md` file content 159 | 160 | ```markdown 161 | update time 2022-10-26 14:39:35 162 | ``` 163 | 164 | Replace the content between `` and 165 | ``. -------------------------------------------------------------------------------- /.github/workflows/issue.yml: -------------------------------------------------------------------------------- 1 | name: fix issue 2 | on: 3 | push: 4 | branches: 5 | - issue 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 20 15 | 16 | - run: npm install 17 | - run: npm run build 18 | - run: mkdir -p build 19 | 20 | - name: Modify gh-pages README.md 21 | # uses: github-action-modify-file-content@main 22 | uses: ./ 23 | with: 24 | path: README.md 25 | branch: gh-pages 26 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 27 | 28 | 29 | - name: Overwrite test/overwrite.file.md 30 | # uses: github-action-modify-file-content@main 31 | uses: ./ 32 | with: 33 | path: test/overwrite.file.md 34 | branch: issue 35 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 36 | overwrite: 'true' 37 | 38 | - name: Overwrite test/overwrite.file.md 39 | # uses: github-action-modify-file-content@main 40 | uses: ./ 41 | with: 42 | path: test/overwrite.file2.md 43 | branch: test 44 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 45 | overwrite: 'true' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | lib 3 | node_modules 4 | 5 | npm-debug.log* 6 | lerna-debug.log 7 | yarn-error.log 8 | package-lock.json 9 | 10 | .DS_Store 11 | .cache 12 | .vscode 13 | .idea 14 | .env 15 | 16 | *.mpassword 17 | *.bak 18 | *.tem 19 | *.temp 20 | #.swp 21 | *.*~ 22 | ~*.* 23 | 24 | # IDEA 25 | *.iml 26 | *.ipr 27 | *.iws 28 | .idea/ -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no lint-staged 2 | git add dist/ -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.ts": [ 3 | "npm run build" 4 | ] 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 小弟调调™ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Modify File Content 2 | === 3 | 4 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) 5 | [![test](https://github.com/jaywcjlove/github-action-modify-file-content/actions/workflows/ci.yml/badge.svg)](https://github.com/jaywcjlove/github-action-modify-file-content/actions/workflows/ci.yml) 6 | 7 | Replace text content and submit content 8 | 9 | Here is the example: update time 2025-05-12 17:15:59 10 | 11 | Here is the different delimiter example: different `GAMFC_TABEL` & `GAMFC_TABEL-END` (test) 12 | 13 | ## Inputs 14 | 15 | | Name | Required | Default | Description | 16 | | -------- | -------- | -------- | -------- | 17 | | `token` | ✅ | `${{ github.token }}` | GitHub Token used to authenticate API requests. [Why?](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/authenticating-with-the-github_token#about-the-github_token-secret) | 18 | | `body` | ✅ | — | The content to insert between delimiters in the target file. | 19 | | `trim_whitespace` | ❌ | `true` | Trim leading and trailing whitespace in `body`. | 20 | | `path` | ✅ | — | File path to be modified. | 21 | | `branch` | ❌ | `${{ github.ref_name }}` | Branch to commit changes to. | 22 | | `ref` | ❌ | Default branch (usually `master`) | The target commit, branch, or tag. | 23 | | `overwrite` | ❌ | `false` | Whether to overwrite the entire file. | 24 | | `sync_local_file` | ❌ | `true` | Whether to sync the file from the local content. | 25 | | `message` | ❌ | `doc: update .` | Commit message. | 26 | | `committer_name` | ❌ | `github-actions[bot]` | Name used for the Git commit author. | 27 | | `committer_email` | ❌ | `github-actions[bot]@users.noreply.github.com` | Email used for the Git commit author. | 28 | | `openDelimiter` | ❌ | `2025-05-12 17:15:59` | End delimiter for content replacement. | 29 | 30 | ## Outputs 31 | 32 | - `content` text file content 33 | 34 | ## Example Usage 35 | 36 | ```yml 37 | - name: Modify README.md 38 | uses: jaywcjlove/github-action-modify-file-content@main 39 | with: 40 | path: README.md 41 | ``` 42 | 43 | `README.md` file content 44 | 45 | ```markdown 46 | update time 2025-05-12 17:15:59 47 | ``` 48 | 49 | Replace the content between `2025-05-12 17:15:59`. 50 | 51 | ### format date 52 | 53 | ```yml 54 | - name: Modify README.md 55 | uses: jaywcjlove/github-action-modify-file-content@main 56 | with: 57 | path: README.md 58 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 59 | ``` 60 | 61 | ### overwrite file 62 | 63 | ```yml 64 | - name: Modify README.md 65 | uses: jaywcjlove/github-action-modify-file-content@main 66 | with: 67 | path: README.md 68 | body: "overwrite file content {{date:YYYY-MM-DD HH:mm:ss}}", 69 | overwrite: 'true' 70 | ``` 71 | 72 | ### specify branch changes 73 | 74 | ```yml 75 | - name: Modify test test/overwrite.file.md 76 | uses: jaywcjlove/github-action-modify-file-content@main 77 | with: 78 | branch: test 79 | path: test/overwrite.file.md 80 | body: "{{date:YYYY-MM-DD HH:mm:ss}}" 81 | overwrite: 'true' 82 | ``` 83 | 84 | ## See Also 85 | 86 | - [Github Release Changelog Generator](https://github.com/jaywcjlove/changelog-generator) A GitHub Action that compares the commit differences between two branches 87 | - [Create Tags From](https://github.com/jaywcjlove/create-tag-action) Auto create tags from commit or package.json. 88 | - [Github Action Contributors](https://github.com/jaywcjlove/github-action-contributors) Github action generates dynamic image URL for contributor list to display it! 89 | - [Generated Badges](https://github.com/jaywcjlove/generated-badges) Create a badge using GitHub Actions and GitHub Workflow CPU time (no 3rd parties servers) 90 | - [Create Coverage Badges](https://github.com/jaywcjlove/coverage-badges-cli) Create coverage badges from coverage reports. (no 3rd parties servers) 91 | - [Github Action package](https://github.com/jaywcjlove/github-action-package) Read and modify the contents of `package.json`. 92 | - [Github Action EJS](https://github.com/jaywcjlove/github-action-package) A github action to render a ejs template using github context. 93 | - [Github Action Read File Content](https://github.com/jaywcjlove/github-action-read-file) 94 | Read file contents. You can also get the file content in the branch. 95 | 96 | ## Contributors 97 | 98 | As always, thanks to our amazing contributors! 99 | 100 | 101 | 小弟调调 102 | 103 | 104 | 105 | 106 | ## License 107 | 108 | Licensed under the MIT License. 109 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Modify File Content' 2 | author: 'Kenny Wong' 3 | description: 'Replace text content and submit content' 4 | inputs: 5 | token: 6 | description: 'Your GITHUB_TOKEN' 7 | default: ${{ github.token }} 8 | required: false 9 | body: 10 | description: 'what needs to be replaced' 11 | default: '' 12 | required: true 13 | trim_whitespace: 14 | description: 'Optional. Whether leading/trailing whitespace will be trimmed for the body input. Defaults to true' 15 | default: true 16 | required: false 17 | path: 18 | description: 'File to be replaced' 19 | default: '' 20 | required: false 21 | ref: 22 | description: 'The name of the commit/branch/tag. Default: the repository’s default branch (usually `master`)' 23 | default: '' 24 | required: false 25 | branch: 26 | description: 'The person that committed the file. Default: the authenticated user.' 27 | default: ${{ github.ref_name }} 28 | required: false 29 | message: 30 | description: 'The commit message.' 31 | default: '' 32 | required: false 33 | committer_name: 34 | description: 'The name of the author or committer of the commit.' 35 | default: 'github-actions[bot]' 36 | required: false 37 | committer_email: 38 | description: 'The email of the author or committer of the commit.' 39 | default: 'github-actions[bot]@users.noreply.github.com' 40 | required: false 41 | openDelimiter: 42 | description: 'Character to use for opening delimiter, by default ``' 43 | default: '' 44 | required: false 45 | closeDelimiter: 46 | description: 'Character to use for closing delimiter, by default ``' 47 | default: '' 48 | required: false 49 | overwrite: 50 | description: 'overwrite file' 51 | default: 'false' 52 | required: false 53 | sync_local_file: 54 | description: 'Sync local file content' 55 | default: 'true' 56 | required: false 57 | 58 | outputs: 59 | content: 60 | description: 'text file content' 61 | 62 | runs: 63 | using: 'node20' 64 | main: 'dist/index.js' 65 | 66 | branding: 67 | icon: 'list' 68 | color: 'blue' 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-action-modify-file-content", 3 | "version": "2.0.4", 4 | "description": "Replace text content and submit content", 5 | "homepage": "https://github.com/jaywcjlove/github-action-modify-file-content#readme", 6 | "main": "dist/index.js", 7 | "scripts": { 8 | "prepare": "husky", 9 | "build": "ncc build src/index.ts -o dist", 10 | "watch": "ncc watch src/index.ts -o dist" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/jaywcjlove/github-action-modify-file-content.git" 15 | }, 16 | "keywords": [], 17 | "author": "jaywcjlove", 18 | "license": "MIT", 19 | "engines": { 20 | "node": ">=v20.11.0", 21 | "npm": ">=10.2.4" 22 | }, 23 | "dependencies": { 24 | "@actions/core": "~1.10.0", 25 | "@actions/github": "~6.0.0", 26 | "@uiw/formatter": "~2.0.1", 27 | "fs-extra": "~11.2.0" 28 | }, 29 | "devDependencies": { 30 | "@kkt/ncc": "~1.1.1", 31 | "husky": "^9.0.11", 32 | "lint-staged": "^15.2.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ], 6 | "packageRules": [ 7 | { 8 | "matchPackagePatterns": ["*"], 9 | "rangeStrategy": "replace" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { getInput, getBooleanInput, startGroup, endGroup, setFailed, info, warning } from '@actions/core'; 2 | import formatter from '@uiw/formatter'; 3 | import { modifyPathContents } from './utils'; 4 | 5 | const REGEXP = /\{\{date:?(.*?)\}\}/ig 6 | 7 | ;(async () => { 8 | const filepath = getInput('path') || ''; 9 | try { 10 | let trimWhitespace = getBooleanInput('trim_whitespace'); 11 | let body = getInput('body', { trimWhitespace }) || ''; 12 | if (!body) { 13 | warning(`👉 [github-action-modify-file-content]: "body" input value does not exist.`) 14 | return 15 | } 16 | if (!filepath) { 17 | warning(`👉 [github-action-modify-file-content]: "path" input value does not exist.`) 18 | return 19 | } 20 | if (REGEXP.test(body)) { 21 | const result = body.replace(REGEXP, (match, str2) => { 22 | const format = match.replace(REGEXP, '$1'); 23 | const str = formatter(format || 'YYYY/MM/DD HH:mm:ss', new Date()); 24 | return str 25 | }); 26 | 27 | if (result) { 28 | body = result 29 | } 30 | } 31 | 32 | startGroup(`👉 Body input content:`); 33 | info(body) 34 | endGroup(); 35 | 36 | await modifyPathContents({ path: filepath }, body); 37 | } catch (error) { 38 | if (error instanceof Error) { 39 | setFailed(`${error.message} - ${filepath}`); 40 | } 41 | } 42 | })(); 43 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import FS from 'fs-extra'; 2 | import path from 'path'; 3 | import { context, getOctokit } from '@actions/github'; 4 | import { getInput, setOutput, startGroup, info, endGroup, warning } from '@actions/core'; 5 | import { paths } from '@octokit/openapi-types'; 6 | import { GetResponseTypeFromEndpointMethod } from '@octokit/types'; 7 | 8 | export type FilePutQuery = paths['/repos/{owner}/{repo}/contents/{path}']['put']['requestBody']['content']['application/json'] & paths['/repos/{owner}/{repo}/contents/{path}']['put']['parameters']['path']; 9 | // export type FilePutResult = paths['/repos/{owner}/{repo}/contents/{path}']['get']['responses']['200']['content']['application/vnd.github.v3.object'] 10 | // export type FilePutResultData = components['schemas']['content-file'] 11 | type GetContentResponseType = GetResponseTypeFromEndpointMethod['data']; 12 | 13 | 14 | export const myToken = getInput('token'); 15 | export const octokit = getOctokit(myToken); 16 | 17 | export const getInputs = () => { 18 | const body = getInput('body') || ''; 19 | const ref = getInput('ref') || context.ref; 20 | const branch = getInput('branch'); 21 | const sha = getInput('sha'); 22 | const overwrite = getInput('overwrite') || 'false'; 23 | const sync_local_file = getInput('sync_local_file') || 'true'; 24 | const filepath = getInput('path') || ''; 25 | const message = getInput('message') || ''; 26 | const committer_name = getInput('committer_name') || ''; 27 | const committer_email = getInput('committer_email') || ''; 28 | const openDelimiter = getInput('openDelimiter') || ''; 29 | const closeDelimiter = getInput('closeDelimiter') || ''; 30 | 31 | return { 32 | ...context.repo, 33 | body, filepath, ref, branch, sha, 34 | message, 35 | committer_name, 36 | committer_email, 37 | openDelimiter, 38 | closeDelimiter, 39 | overwrite, 40 | sync_local_file 41 | } 42 | } 43 | 44 | async function getBranch(): Promise { 45 | const { branch } = getInputs() 46 | if (branch !== null) { 47 | return Promise.resolve(branch); 48 | } 49 | const { data } = await octokit.rest.repos.get(context.repo); 50 | return data.default_branch; 51 | } 52 | 53 | async function getFileContents(branch: string): Promise { 54 | const {owner, repo, filepath} = getInputs() 55 | try { 56 | const { data } = await octokit.rest.repos.getContent({ 57 | owner, repo, ref: branch, path: filepath 58 | }); 59 | return data; 60 | } catch (err) { 61 | warning(`👉 [github-action-modify-file-content]: Get File Contents: ${err instanceof Error ? err.message : err}`); 62 | return; 63 | } 64 | } 65 | 66 | function getBodyContent(oldFileContent: string, content: string) { 67 | const {openDelimiter, closeDelimiter, overwrite} = getInputs() 68 | const REG = new RegExp(`${openDelimiter}([\\s\\S]*?)${closeDelimiter}`, 'ig') 69 | const match = oldFileContent.match(REG); 70 | startGroup(`👉 Current File content: ${match?.length}`); 71 | info(`👉 ${JSON.stringify(match, null, 2)}`); 72 | endGroup(); 73 | if (overwrite.toString() === 'true') { 74 | 75 | } 76 | return oldFileContent.replace(REG, `${openDelimiter}${content}${closeDelimiter}`) 77 | } 78 | 79 | export async function modifyPathContents(options: Partial = {}, content: string) { 80 | const { ...other} = options; 81 | const { owner, repo, message, committer_name, committer_email, overwrite, sync_local_file, ref} = getInputs(); 82 | const branch = await getBranch(); 83 | if (!options.path) { 84 | throw new Error(`modifyPathContents: file directory parameter does not exist`) 85 | } 86 | info(`👉 Modify Path (${options.path})`); 87 | info(`👉 Context.ref: (${context.ref})`); 88 | info(`👉 Context.sha: (${context.sha})`); 89 | info(`👉 branch: (${branch})`); 90 | 91 | let new_content = Buffer.from(content).toString("base64") 92 | let body: FilePutQuery = { 93 | owner, repo, 94 | path: options.path, 95 | branch, 96 | message: message || `doc: update ${options.path}.`, 97 | committer: { 98 | name: committer_name || 'github-actions[bot]', 99 | email: committer_email || 'github-actions[bot]@users.noreply.github.com' 100 | }, 101 | ...other, 102 | content: new_content, 103 | } 104 | startGroup(`👉 Init Body: (${branch})`) 105 | info(`👉 ${JSON.stringify(body, null, 2)}`) 106 | endGroup() 107 | const currentFile = await getFileContents(branch); 108 | if (currentFile && 'content' in currentFile) { 109 | const fileContent = currentFile.content || ''; 110 | const oldFileContent = Buffer.from(fileContent, 'base64').toString(); 111 | let reuslt = getBodyContent(oldFileContent, content) 112 | startGroup(`👉 Current File content: ${options.path}`); 113 | info(`👉 ${JSON.stringify(currentFile, null, 2)}`); 114 | endGroup(); 115 | if (overwrite.toString() === 'true') { 116 | body.content = new_content; 117 | reuslt = content; 118 | } else { 119 | body.content = Buffer.from(reuslt).toString("base64"); 120 | new_content = reuslt; 121 | } 122 | setOutput('content', Buffer.from(body.content, 'base64').toString()); 123 | startGroup(`👉 Text OLD content: ${oldFileContent == reuslt}`); 124 | info(`👉 ${oldFileContent}`); 125 | endGroup(); 126 | startGroup(`👉 Text NEW content: ${oldFileContent == reuslt}`); 127 | info(`👉 ${reuslt}`); 128 | endGroup(); 129 | if (oldFileContent == reuslt) { 130 | warning(`👉 [github-action-modify-file-content]: Content has not changed!!!!!`) 131 | return; 132 | } 133 | body = { ...body, ...currentFile, content: body.content, sha: currentFile.sha } 134 | const fullPath = path.resolve(options.path); 135 | const isExists = FS.existsSync(fullPath); 136 | if (isExists && sync_local_file.toString() === 'true' && ref === context.ref) { 137 | await FS.writeFile(fullPath, new_content); 138 | } 139 | startGroup(`modifyPathContents Body:`) 140 | info(`👉 ${JSON.stringify(body, null, 2)}`) 141 | endGroup() 142 | const result = await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', { 143 | ...body, 144 | sha: currentFile.sha 145 | }); 146 | startGroup(`file result:`) 147 | info(`👉 ${result.data.content?.path}`) 148 | info(`👉 ${result.data.content?.size}`) 149 | info(`👉 ${result.data.content?.sha}`) 150 | endGroup() 151 | } else { 152 | warning(`👉 [github-action-modify-file-content]: Not Found ::- ${options.path}`) 153 | const result = await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', { 154 | ...body, 155 | }); 156 | startGroup(`file result:`) 157 | info(`👉 ${result.data.content?.path}`) 158 | info(`👉 ${result.data.content?.size}`) 159 | info(`👉 ${result.data.content?.sha}`) 160 | endGroup() 161 | } 162 | } -------------------------------------------------------------------------------- /test/overwrite.file.md: -------------------------------------------------------------------------------- 1 | 2025-05-12 17:15:57 -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "commonjs", 5 | "outDir": "./lib", 6 | "rootDir": "./src", 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "declaration": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "moduleResolution": "node", 13 | }, 14 | "include": [ 15 | "src/**/*.ts", 16 | ], 17 | "exclude": [ 18 | "node_modules" 19 | ] 20 | } 21 | --------------------------------------------------------------------------------