diff --git a/README.md b/README.md index afb29f2c..9918d141 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ Git child processes are also limited to the `https`, `http`, `ssh`, `file`, and Message-from-file flags (`-F`, `--file`, abbreviations such as `--fi`, and short-option clusters that include `F` such as `-aF`) are rejected: they can embed arbitrary runner filesystem contents into a tag or commit message and, with a push, into the repository history. Pathspec-from-file flags (`--pathspec-from-file`, `--pathspec-file-nul`, and abbreviations such as `--pathspec-fr` / `--pathspec-fi`) are rejected on `add`, `remove`, and `commit`: they can read an arbitrary runner file and leak its contents into the action log. Unmatched `'` / `"` quotes are also rejected: `string-argv` can otherwise split on an odd quote and turn part of a value into extra flags (for example a branch name like `fix'--force` becoming `fix` plus `--force`). +A quoted segment is accepted only when its closing quote is followed by whitespace or the end of the input. That is a conservative argument-boundary check, not a claim that every rejected form would become extra argv words: `'main'--force` is rejected (and would split into `main` plus `--force`), and so is `a'b'c` (which `string-argv` would keep as one token). `--message='hello'` is allowed because the closer is at the end of the word. Put a space after a wrapping closer (`origin 'main' --force`) or omit the quotes. Do not interpolate untrusted data (for example values from `github.event.*`, `github.head_ref`, or repository content that contributors can edit) into `fetch`, `pull`, `push`, `tag`, `tag_push`, or `commit` without sanitizing them first. When the branch name is dynamic, prefer the default `push: true` with [`new_branch`](#creating-a-new-branch) instead of embedding the ref in a custom `push` string. ### Allow unsafe git protocols diff --git a/lib/index.js b/lib/index.js index 1059264c..3f762a63 100644 --- a/lib/index.js +++ b/lib/index.js @@ -7,4 +7,4 @@ var uo=Symbol("NOT_RESOLVED");var go=Symbol("MERGE_KEY");function defineScalarTa /* v8 ignore next -- @preserve */ ()=>new ArrayBuffer(0))}}function isJSONResponse(e){return e.type==="application/json"||e.type==="application/scim+json"}function toErrorMessage(e){if(typeof e==="string"){return e}if(e instanceof ArrayBuffer){return"Unknown error"}if("message"in e){const t="documentation_url"in e?` - ${e.documentation_url}`:"";return Array.isArray(e.errors)?`${e.message}: ${e.errors.map(e=>JSON.stringify(e)).join(", ")}${t}`:`${e.message}${t}`}return`Unknown error: ${JSON.stringify(e)}`}function dist_bundle_withDefaults(e,t){const r=e.defaults(t);const newApi=function(e,t){const s=r.merge(e,t);if(!s.request||!s.request.hook){return fetchWrapper(r.parse(s))}const request2=(e,t)=>fetchWrapper(r.parse(r.merge(e,t)));Object.assign(request2,{endpoint:r,defaults:dist_bundle_withDefaults.bind(null,r)});return s.request.hook(request2,s)};return Object.assign(newApi,{endpoint:r,defaults:dist_bundle_withDefaults.bind(null,r)})}var jA=dist_bundle_withDefaults(JA,qA); /* v8 ignore next -- @preserve */ -/* v8 ignore else -- @preserve */var zA="0.0.0-development";function _buildMessageForResponseErrors(e){return`Request failed due to following response errors:\n`+e.errors.map(e=>` - ${e.message}`).join("\n")}var KA=class extends Error{constructor(e,t,r){super(_buildMessageForResponseErrors(r));this.request=e;this.headers=t;this.response=r;this.errors=r.errors;this.data=r.data;if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}}name="GraphqlResponseError";errors;data};var ZA=["method","baseUrl","url","headers","request","query","mediaType","operationName"];var $A=["query","method","url"];var XA=/\/api\/v3\/?$/;function graphql(e,t,r){if(r){if(typeof t==="string"&&"query"in r){return Promise.reject(new Error(`[@octokit/graphql] "query" cannot be used as variable name`))}for(const e in r){if(!$A.includes(e))continue;return Promise.reject(new Error(`[@octokit/graphql] "${e}" cannot be used as variable name`))}}const s=typeof t==="string"?Object.assign({query:t},r):t;const n=Object.keys(s).reduce((e,t)=>{if(ZA.includes(t)){e[t]=s[t];return e}if(!e.variables){e.variables={}}e.variables[t]=s[t];return e},{});const o=s.baseUrl||e.endpoint.DEFAULTS.baseUrl;if(XA.test(o)){n.url=o.replace(XA,"/api/graphql")}return e(n).then(e=>{if(e.data.errors){const t={};for(const r of Object.keys(e.headers)){t[r]=e.headers[r]}throw new KA(n,t,e.data)}return e.data.data})}function graphql_dist_bundle_withDefaults(e,t){const r=e.defaults(t);const newApi=(e,t)=>graphql(r,e,t);return Object.assign(newApi,{defaults:graphql_dist_bundle_withDefaults.bind(null,r),endpoint:r.endpoint})}var ea=graphql_dist_bundle_withDefaults(jA,{headers:{"user-agent":`octokit-graphql.js/${zA} ${getUserAgent()}`},method:"POST",url:"/graphql"});function withCustomRequest(e){return graphql_dist_bundle_withDefaults(e,{method:"POST",url:"/graphql"})}var ta="(?:[a-zA-Z0-9_-]+)";var ra="\\.";var sa=new RegExp(`^${ta}${ra}${ta}${ra}${ta}$`);var na=sa.test.bind(sa);async function auth(e){const t=na(e);const r=e.startsWith("v1.")||e.startsWith("ghs_");const s=e.startsWith("ghu_");const n=t?"app":r?"installation":s?"user-to-server":"oauth";return{type:"token",token:e,tokenType:n}}function withAuthorizationPrefix(e){if(e.split(/\./).length===3){return`bearer ${e}`}return`token ${e}`}async function hook(e,t,r,s){const n=t.endpoint.merge(r,s);n.headers.authorization=withAuthorizationPrefix(e);return t(n)}var oa=function createTokenAuth2(e){if(!e){throw new Error("[@octokit/auth-token] No token passed to createTokenAuth")}if(typeof e!=="string"){throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string")}e=e.replace(/^(token|bearer) +/i,"");return Object.assign(auth.bind(null,e),{hook:hook.bind(null,e)})};const ia="7.0.6";const dist_src_noop=()=>{};const Aa=console.warn.bind(console);const aa=console.error.bind(console);function dist_src_createLogger(e={}){if(typeof e.debug!=="function"){e.debug=dist_src_noop}if(typeof e.info!=="function"){e.info=dist_src_noop}if(typeof e.warn!=="function"){e.warn=Aa}if(typeof e.error!=="function"){e.error=aa}return e}const ca=`octokit-core.js/${ia} ${getUserAgent()}`;class Octokit{static VERSION=ia;static defaults(e){const t=class extends(this){constructor(...t){const r=t[0]||{};if(typeof e==="function"){super(e(r));return}super(Object.assign({},e,r,r.userAgent&&e.userAgent?{userAgent:`${r.userAgent} ${e.userAgent}`}:null))}};return t}static plugins=[];static plugin(...e){const t=this.plugins;const r=class extends(this){static plugins=t.concat(e.filter(e=>!t.includes(e)))};return r}constructor(e={}){const t=new OA.Collection;const r={baseUrl:jA.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},e.request,{hook:t.bind(null,"request")}),mediaType:{previews:[],format:""}};r.headers["user-agent"]=e.userAgent?`${e.userAgent} ${ca}`:ca;if(e.baseUrl){r.baseUrl=e.baseUrl}if(e.previews){r.mediaType.previews=e.previews}if(e.timeZone){r.headers["time-zone"]=e.timeZone}this.request=jA.defaults(r);this.graphql=withCustomRequest(this.request).defaults(r);this.log=dist_src_createLogger(e.log);this.hook=t;if(!e.authStrategy){if(!e.auth){this.auth=async()=>({type:"unauthenticated"})}else{const r=oa(e.auth);t.wrap("request",r.hook);this.auth=r}}else{const{authStrategy:r,...s}=e;const n=r(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:s},e.auth));t.wrap("request",n.hook);this.auth=n}const s=this.constructor;for(let t=0;t({async next(){if(!l)return{done:true};try{const e=await n({method:o,url:l,headers:a});const t=normalizePaginatedListResponse(e);l=((t.headers.link||"").match(/<([^<>]+)>;\s*rel="next"/)||[])[1];if(!l&&"total_commits"in t.data){const e=new URL(t.url);const r=e.searchParams;const s=parseInt(r.get("page")||"1",10);const n=parseInt(r.get("per_page")||"250",10);if(s*n{if(n.done){return t}let o=false;function done(){o=true}t=t.concat(s?s(n.value,done):n.value.data);if(o){return t}return gather(e,t,r,s)})}var Ea=Object.assign(paginate,{iterator:iterator});var fa=null&&["GET /advisories","GET /app/hook/deliveries","GET /app/installation-requests","GET /app/installations","GET /assignments/{assignment_id}/accepted_assignments","GET /classrooms","GET /classrooms/{classroom_id}/assignments","GET /enterprises/{enterprise}/code-security/configurations","GET /enterprises/{enterprise}/code-security/configurations/{configuration_id}/repositories","GET /enterprises/{enterprise}/dependabot/alerts","GET /enterprises/{enterprise}/teams","GET /enterprises/{enterprise}/teams/{enterprise-team}/memberships","GET /enterprises/{enterprise}/teams/{enterprise-team}/organizations","GET /events","GET /gists","GET /gists/public","GET /gists/starred","GET /gists/{gist_id}/comments","GET /gists/{gist_id}/commits","GET /gists/{gist_id}/forks","GET /installation/repositories","GET /issues","GET /licenses","GET /marketplace_listing/plans","GET /marketplace_listing/plans/{plan_id}/accounts","GET /marketplace_listing/stubbed/plans","GET /marketplace_listing/stubbed/plans/{plan_id}/accounts","GET /networks/{owner}/{repo}/events","GET /notifications","GET /organizations","GET /organizations/{org}/dependabot/repository-access","GET /orgs/{org}/actions/cache/usage-by-repository","GET /orgs/{org}/actions/hosted-runners","GET /orgs/{org}/actions/permissions/repositories","GET /orgs/{org}/actions/permissions/self-hosted-runners/repositories","GET /orgs/{org}/actions/runner-groups","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/hosted-runners","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners","GET /orgs/{org}/actions/runners","GET /orgs/{org}/actions/secrets","GET /orgs/{org}/actions/secrets/{secret_name}/repositories","GET /orgs/{org}/actions/variables","GET /orgs/{org}/actions/variables/{name}/repositories","GET /orgs/{org}/attestations/repositories","GET /orgs/{org}/attestations/{subject_digest}","GET /orgs/{org}/blocks","GET /orgs/{org}/campaigns","GET /orgs/{org}/code-scanning/alerts","GET /orgs/{org}/code-security/configurations","GET /orgs/{org}/code-security/configurations/{configuration_id}/repositories","GET /orgs/{org}/codespaces","GET /orgs/{org}/codespaces/secrets","GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories","GET /orgs/{org}/copilot/billing/seats","GET /orgs/{org}/copilot/metrics","GET /orgs/{org}/dependabot/alerts","GET /orgs/{org}/dependabot/secrets","GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories","GET /orgs/{org}/events","GET /orgs/{org}/failed_invitations","GET /orgs/{org}/hooks","GET /orgs/{org}/hooks/{hook_id}/deliveries","GET /orgs/{org}/insights/api/route-stats/{actor_type}/{actor_id}","GET /orgs/{org}/insights/api/subject-stats","GET /orgs/{org}/insights/api/user-stats/{user_id}","GET /orgs/{org}/installations","GET /orgs/{org}/invitations","GET /orgs/{org}/invitations/{invitation_id}/teams","GET /orgs/{org}/issues","GET /orgs/{org}/members","GET /orgs/{org}/members/{username}/codespaces","GET /orgs/{org}/migrations","GET /orgs/{org}/migrations/{migration_id}/repositories","GET /orgs/{org}/organization-roles/{role_id}/teams","GET /orgs/{org}/organization-roles/{role_id}/users","GET /orgs/{org}/outside_collaborators","GET /orgs/{org}/packages","GET /orgs/{org}/packages/{package_type}/{package_name}/versions","GET /orgs/{org}/personal-access-token-requests","GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories","GET /orgs/{org}/personal-access-tokens","GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories","GET /orgs/{org}/private-registries","GET /orgs/{org}/projects","GET /orgs/{org}/projectsV2","GET /orgs/{org}/projectsV2/{project_number}/fields","GET /orgs/{org}/projectsV2/{project_number}/items","GET /orgs/{org}/properties/values","GET /orgs/{org}/public_members","GET /orgs/{org}/repos","GET /orgs/{org}/rulesets","GET /orgs/{org}/rulesets/rule-suites","GET /orgs/{org}/rulesets/{ruleset_id}/history","GET /orgs/{org}/secret-scanning/alerts","GET /orgs/{org}/security-advisories","GET /orgs/{org}/settings/immutable-releases/repositories","GET /orgs/{org}/settings/network-configurations","GET /orgs/{org}/team/{team_slug}/copilot/metrics","GET /orgs/{org}/teams","GET /orgs/{org}/teams/{team_slug}/discussions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions","GET /orgs/{org}/teams/{team_slug}/invitations","GET /orgs/{org}/teams/{team_slug}/members","GET /orgs/{org}/teams/{team_slug}/projects","GET /orgs/{org}/teams/{team_slug}/repos","GET /orgs/{org}/teams/{team_slug}/teams","GET /projects/{project_id}/collaborators","GET /repos/{owner}/{repo}/actions/artifacts","GET /repos/{owner}/{repo}/actions/caches","GET /repos/{owner}/{repo}/actions/organization-secrets","GET /repos/{owner}/{repo}/actions/organization-variables","GET /repos/{owner}/{repo}/actions/runners","GET /repos/{owner}/{repo}/actions/runs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts","GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs","GET /repos/{owner}/{repo}/actions/secrets","GET /repos/{owner}/{repo}/actions/variables","GET /repos/{owner}/{repo}/actions/workflows","GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs","GET /repos/{owner}/{repo}/activity","GET /repos/{owner}/{repo}/assignees","GET /repos/{owner}/{repo}/attestations/{subject_digest}","GET /repos/{owner}/{repo}/branches","GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations","GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs","GET /repos/{owner}/{repo}/code-scanning/alerts","GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances","GET /repos/{owner}/{repo}/code-scanning/analyses","GET /repos/{owner}/{repo}/codespaces","GET /repos/{owner}/{repo}/codespaces/devcontainers","GET /repos/{owner}/{repo}/codespaces/secrets","GET /repos/{owner}/{repo}/collaborators","GET /repos/{owner}/{repo}/comments","GET /repos/{owner}/{repo}/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/commits","GET /repos/{owner}/{repo}/commits/{commit_sha}/comments","GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls","GET /repos/{owner}/{repo}/commits/{ref}/check-runs","GET /repos/{owner}/{repo}/commits/{ref}/check-suites","GET /repos/{owner}/{repo}/commits/{ref}/status","GET /repos/{owner}/{repo}/commits/{ref}/statuses","GET /repos/{owner}/{repo}/compare/{basehead}","GET /repos/{owner}/{repo}/compare/{base}...{head}","GET /repos/{owner}/{repo}/contributors","GET /repos/{owner}/{repo}/dependabot/alerts","GET /repos/{owner}/{repo}/dependabot/secrets","GET /repos/{owner}/{repo}/deployments","GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses","GET /repos/{owner}/{repo}/environments","GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies","GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps","GET /repos/{owner}/{repo}/environments/{environment_name}/secrets","GET /repos/{owner}/{repo}/environments/{environment_name}/variables","GET /repos/{owner}/{repo}/events","GET /repos/{owner}/{repo}/forks","GET /repos/{owner}/{repo}/hooks","GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries","GET /repos/{owner}/{repo}/invitations","GET /repos/{owner}/{repo}/issues","GET /repos/{owner}/{repo}/issues/comments","GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/issues/events","GET /repos/{owner}/{repo}/issues/{issue_number}/comments","GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by","GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking","GET /repos/{owner}/{repo}/issues/{issue_number}/events","GET /repos/{owner}/{repo}/issues/{issue_number}/labels","GET /repos/{owner}/{repo}/issues/{issue_number}/reactions","GET /repos/{owner}/{repo}/issues/{issue_number}/sub_issues","GET /repos/{owner}/{repo}/issues/{issue_number}/timeline","GET /repos/{owner}/{repo}/keys","GET /repos/{owner}/{repo}/labels","GET /repos/{owner}/{repo}/milestones","GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels","GET /repos/{owner}/{repo}/notifications","GET /repos/{owner}/{repo}/pages/builds","GET /repos/{owner}/{repo}/projects","GET /repos/{owner}/{repo}/pulls","GET /repos/{owner}/{repo}/pulls/comments","GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/pulls/{pull_number}/comments","GET /repos/{owner}/{repo}/pulls/{pull_number}/commits","GET /repos/{owner}/{repo}/pulls/{pull_number}/files","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments","GET /repos/{owner}/{repo}/releases","GET /repos/{owner}/{repo}/releases/{release_id}/assets","GET /repos/{owner}/{repo}/releases/{release_id}/reactions","GET /repos/{owner}/{repo}/rules/branches/{branch}","GET /repos/{owner}/{repo}/rulesets","GET /repos/{owner}/{repo}/rulesets/rule-suites","GET /repos/{owner}/{repo}/rulesets/{ruleset_id}/history","GET /repos/{owner}/{repo}/secret-scanning/alerts","GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations","GET /repos/{owner}/{repo}/security-advisories","GET /repos/{owner}/{repo}/stargazers","GET /repos/{owner}/{repo}/subscribers","GET /repos/{owner}/{repo}/tags","GET /repos/{owner}/{repo}/teams","GET /repos/{owner}/{repo}/topics","GET /repositories","GET /search/code","GET /search/commits","GET /search/issues","GET /search/labels","GET /search/repositories","GET /search/topics","GET /search/users","GET /teams/{team_id}/discussions","GET /teams/{team_id}/discussions/{discussion_number}/comments","GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /teams/{team_id}/discussions/{discussion_number}/reactions","GET /teams/{team_id}/invitations","GET /teams/{team_id}/members","GET /teams/{team_id}/projects","GET /teams/{team_id}/repos","GET /teams/{team_id}/teams","GET /user/blocks","GET /user/codespaces","GET /user/codespaces/secrets","GET /user/emails","GET /user/followers","GET /user/following","GET /user/gpg_keys","GET /user/installations","GET /user/installations/{installation_id}/repositories","GET /user/issues","GET /user/keys","GET /user/marketplace_purchases","GET /user/marketplace_purchases/stubbed","GET /user/memberships/orgs","GET /user/migrations","GET /user/migrations/{migration_id}/repositories","GET /user/orgs","GET /user/packages","GET /user/packages/{package_type}/{package_name}/versions","GET /user/public_emails","GET /user/repos","GET /user/repository_invitations","GET /user/social_accounts","GET /user/ssh_signing_keys","GET /user/starred","GET /user/subscriptions","GET /user/teams","GET /users","GET /users/{username}/attestations/{subject_digest}","GET /users/{username}/events","GET /users/{username}/events/orgs/{org}","GET /users/{username}/events/public","GET /users/{username}/followers","GET /users/{username}/following","GET /users/{username}/gists","GET /users/{username}/gpg_keys","GET /users/{username}/keys","GET /users/{username}/orgs","GET /users/{username}/packages","GET /users/{username}/projects","GET /users/{username}/projectsV2","GET /users/{username}/projectsV2/{project_number}/fields","GET /users/{username}/projectsV2/{project_number}/items","GET /users/{username}/received_events","GET /users/{username}/received_events/public","GET /users/{username}/repos","GET /users/{username}/social_accounts","GET /users/{username}/ssh_signing_keys","GET /users/{username}/starred","GET /users/{username}/subscriptions"];function isPaginatingEndpoint(e){if(typeof e==="string"){return fa.includes(e)}else{return false}}function paginateRest(e){return{paginate:Object.assign(paginate.bind(null,e),{iterator:iterator.bind(null,e)})}}paginateRest.VERSION=pa;const Ca=new Context;const Qa=getApiBaseUrl();const Ia={baseUrl:Qa,request:{agent:getProxyAgent(Qa),fetch:getProxyFetch(Qa)}};const Ba=Octokit.plugin(restEndpointMethods,paginateRest).defaults(Ia);function getOctokitOptions(e,t){const r=Object.assign({},t||{});const s=getAuthString(e,r);if(s){r.auth=s}const n=getUserAgentWithOrchestrationId(r.userAgent);if(n){r.userAgent=n}return r}const ma=new Context;function getOctokit(e,t,...r){const s=Ba.plugin(...r);return new s(getOctokitOptions(e,t))}function resolveBaseDir(e,t=process.cwd()){return u.resolve(t,e||".")}function assertWorkingDirectory(e,t){if(!l.existsSync(e)||!l.statSync(e).isDirectory()){throw new Error(`The cwd input '${neutralizeLogString(t||".")}' resolved to '${neutralizeLogString(e)}', which is not an existing directory. `+"Use a path relative to the runner workspace, or an absolute path that exists "+"(e.g. ${{ github.workspace }}/path — note that $GITHUB_WORKSPACE is not expanded in with:).")}}function getOctokitClient(){const e=io_getInput("github_token");if(!e){throw new Error("github_token is required")}return getOctokit(e)}async function getUserInfo(e){if(!e)return undefined;const t=getOctokitClient();const r=await t.rest.users.getByUsername({username:e});core_debug(`Fetched github actor from the API: ${JSON.stringify(r?.data,null,2)}`);return{name:r?.data?.name,email:r?.data?.email}}const wa=/[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u2028-\u202E\u2066-\u2069]/gu;function neutralizeLogString(e){return e.replace(wa,e=>{const t=e.codePointAt(0).toString(16).padStart(4,"0");return`\\u${t}`})}function safeInfo(e){info(neutralizeLogString(e))}const ya="[Circular]";function neutralizeForLog(e,t=new WeakSet){if(typeof e==="string")return neutralizeLogString(e);if(typeof e==="number"||typeof e==="boolean"||e===null||e===undefined){return e}if(e instanceof Error){const t=new Error(neutralizeLogString(e.message));t.name=neutralizeLogString(e.name);if(e.stack){t.stack=neutralizeLogString(e.stack)}return t}if(Array.isArray(e)){if(t.has(e))return ya;t.add(e);return e.map(e=>neutralizeForLog(e,t))}if(typeof e==="object"){if(t.has(e))return ya;t.add(e);const r={};for(const[s,n]of Object.entries(e)){r[neutralizeLogString(s)]=neutralizeForLog(n,t)}return r}return e}function log(e,t){if(t)console.log(neutralizeForLog(t));if(e){const t=neutralizeForLog(e);if(typeof t==="string"||t instanceof Error){error(t)}else{error(String(t))}}}const ba=["user.name","user.email","author.name","author.email","committer.name","committer.email"];function pickGitIdentityConfig(e){const t={};for(const r of ba){if(Object.prototype.hasOwnProperty.call(e,r)){t[r]=e[r]}}return t}function assertValidBranchName(e){if(!e||!e.trim()){throw new Error("The new_branch value is empty.")}if(e.startsWith("-")){throw new Error(`The new_branch value '${neutralizeLogString(e)}' cannot start with '-' (it would be interpreted as a git option).`)}for(const t of e){const r=t.codePointAt(0);if(r<=31||r===127||r>=128&&r<=159||/\s/u.test(t)){throw new Error(`The new_branch value '${neutralizeLogString(e)}' contains whitespace or control characters.`)}}try{(0,ue.execFileSync)("git",["check-ref-format","--branch",e],{stdio:"ignore"})}catch{throw new Error(`The new_branch value '${neutralizeLogString(e)}' is not a valid git branch name.`)}}const ka=[{canonical:"upload-pack",minPrefix:"upl"},{canonical:"receive-pack",minPrefix:"rece"},{canonical:"exec",minPrefix:"e"}];const Ta=[{canonical:"file",minPrefix:"fi"}];const Ra=[{canonical:"pathspec-from-file",minPrefix:"pathspec-fr"},{canonical:"pathspec-file-nul",minPrefix:"pathspec-fi"}];const Da=[{canonical:"message",minPrefix:"mes"},{canonical:"local-user",minPrefix:"local-"},{canonical:"cleanup",minPrefix:"cleanup"},{canonical:"file",minPrefix:"fi"},{canonical:"pathspec-from-file",minPrefix:"pathspec-fr"},{canonical:"upload-pack",minPrefix:"upl"},{canonical:"receive-pack",minPrefix:"rece"},{canonical:"exec",minPrefix:"e"}];const Sa=new Set(["m","F"]);function getLongOptionName(e){if(!e.startsWith("--")||e==="--")return undefined;const t=e.slice(2);const r=t.indexOf("=");return(r===-1?t:t.slice(0,r)).toLowerCase()}function longOptionHasInlineValue(e){if(!e.startsWith("--")||e==="--")return false;return e.slice(2).includes("=")}function matchesLongOptionPrefix(e,t){const r=getLongOptionName(e);if(!r)return false;return t.some(({canonical:e,minPrefix:t})=>r.length>=t.length&&e.startsWith(r))}function isDangerousRemoteHelperOption(e){return matchesLongOptionPrefix(e,ka)}function isDangerousMessageFileShortOption(e){if(!e.startsWith("-")||e.startsWith("--"))return false;const t=e.slice(1);for(let e=0;e` - ${neutralizeLogString(e)}`).join("\n");const r=e.map(e=>` git rm --cached -- ${neutralizeLogString(e)}`).join("\n");throw new Error(`Refusing to commit unexpected gitlink(s) (embedded git repository staged as mode 160000):\n${t}\n`+"Git records a nested .git directory as a gitlink, not as its files. "+`Remove the nested .git directory, or unstage the path(s) with:\n${r}`)}function parseInputArray(e){core_debug(`Parsing input array: ${e}`);try{const t=load(e);if(t&&Array.isArray(t)&&t.every(e=>typeof e==="string")){core_debug(`Input parsed as YAML array of length ${t.length}`);return t}}catch{}core_debug("Input parsed as single string");return[e]}function readJSON(e){let t;try{t=fs.readFileSync(e,{encoding:"utf8"})}catch{throw`Couldn't read file. File path: ${neutralizeLogString(e)}`}try{return JSON.parse(t)}catch{throw`Couldn't parse file to JSON. File path: ${neutralizeLogString(e)}`}}const va={committed:"false",commit_long_sha:undefined,commit_sha:undefined,pushed:"false",tagged:"false",tag_pushed:"false"};Object.entries(va).forEach(([e,t])=>setOutput(e,t));function io_getInput(e,t=false){if(t)return getBooleanInput(e);return getInput(e)}function io_setOutput(e,t){core_debug(`Setting output: ${e}=${t}`);va[e]=t;setOutput(e,t)}function parseBoolOrGitArgs(e){try{return io_getInput(e,true)}catch{return io_getInput(e)||""}}function parsePushAttempts(e){const t=e.trim();if(!/^\+?\d+$/.test(t)){throw new Error(`'${neutralizeLogString(e)}' is not a valid value for push_attempts. It must be a positive integer (≥ 1).`)}const r=Number.parseInt(t,10);if(!Number.isSafeInteger(r)||r<1){throw new Error(`'${neutralizeLogString(e)}' is not a valid value for push_attempts. It must be a positive integer (≥ 1).`)}return r}function logOutputs(){startGroup("Outputs");for(const e in va){safeInfo(`${e}: ${va[e]}`)}endGroup()}async function checkInputs(){function setInput(e,t){if(t)return process.env[`INPUT_${e.toUpperCase()}`]=t;else return delete process.env[`INPUT_${e.toUpperCase()}`]}function setDefault(e,t){if(!io_getInput(e))setInput(e,t);return io_getInput(e)}if(!io_getInput("add")&&!io_getInput("remove"))throw new Error("Both 'add' and 'remove' are empty, the action has nothing to do.");if(io_getInput("add")){const e=parseInputArray(io_getInput("add"));if(e.length===1)safeInfo("Add input parsed as single string, running 1 git add command.");else if(e.length>1)safeInfo(`Add input parsed as string array, running ${e.length} git add commands.`);else setFailed("Add input: array length < 1")}if(io_getInput("remove")){const e=parseInputArray(io_getInput("remove")||"");if(e.length===1)safeInfo("Remove input parsed as single string, running 1 git rm command.");else if(e.length>1)safeInfo(`Remove input parsed as string array, running ${e.length} git rm commands.`);else setFailed("Remove input: array length < 1")}const e=["github_actor","user_info","github_actions"];if(!e.includes(io_getInput("default_author")))throw new Error(`'${neutralizeLogString(io_getInput("default_author"))}' is not a valid value for default_author. Valid values: ${e.join(", ")}`);if(io_getInput("dry_run",true))safeInfo("> Dry run enabled: no mutating git operations will be performed.");if(io_getInput("allow_unsafe_git_protocols",true))warning("allow_unsafe_git_protocols is enabled: transport allowlist and scheme:: remote-helper URL checks are disabled. Only use this with fully trusted git argument inputs.");if(io_getInput("fetch")){let e;try{e=io_getInput("fetch",true)}catch{e=io_getInput("fetch")}core_debug(`Current fetch option: '${e}' (parsed as ${typeof e})`)}let t,r;switch(io_getInput("default_author")){case"github_actor":{t=process.env.GITHUB_ACTOR??"";r=`${process.env.GITHUB_ACTOR}@users.noreply.github.com`;break}case"user_info":{if(!io_getInput("author_name")||!io_getInput("author_email")){const e=await getUserInfo(process.env.GITHUB_ACTOR);if(!e?.name)warning("Couldn't fetch author name, filling with github_actor.");if(!e?.email)warning("Couldn't fetch author email, filling with github_actor.");e?.name&&(t=e?.name);e?.email&&(r=e.email);if(t&&r)break}!t&&(t=process.env.GITHUB_ACTOR??"");!r&&(r=`${process.env.GITHUB_ACTOR}@users.noreply.github.com`);break}case"github_actions":{t="github-actions";r="41898282+github-actions[bot]@users.noreply.github.com";break}default:throw new Error("This should not happen, please contact the author of this action. (checkInputs.author)")}setDefault("author_name",t);setDefault("author_email",r);safeInfo(`> Using '${io_getInput("author_name")} <${io_getInput("author_email")}>' as author.`);if(io_getInput("committer_name")||io_getInput("committer_email"))safeInfo(`> Using custom committer info: ${io_getInput("committer_name")||io_getInput("author_name")+" [from author info]"} <${io_getInput("committer_email")||io_getInput("author_email")+" [from author info]"}>`);setDefault("committer_name",io_getInput("author_name"));setDefault("committer_email",io_getInput("author_email"));core_debug(`Committer: ${io_getInput("committer_name")} <${io_getInput("committer_email")}>`);setDefault("message",`Commit from GitHub Actions (${process.env.GITHUB_WORKFLOW})`);safeInfo(`> Using "${io_getInput("message")}" as commit message.`);const s=io_getInput("new_branch");if(s)assertValidBranchName(s);const n=["ignore","exitImmediately","exitAtEnd"];if(!n.includes(io_getInput("pathspec_error_handling")))throw new Error(`"${neutralizeLogString(io_getInput("pathspec_error_handling"))}" is not a valid value for the 'pathspec_error_handling' input. Valid values are: ${n.join(", ")}`);if(io_getInput("pull")==="NO-PULL")warning("`NO-PULL` is a legacy option for the `pull` input. If you don't want the action to pull the repo, simply remove this input.");const o=parseBoolOrGitArgs("pull");if(io_getInput("pull")){core_debug(`Current pull option: '${o}' (parsed as ${typeof o})`)}if(io_getInput("push")){let e;try{e=io_getInput("push",true)}catch{e=io_getInput("push")}core_debug(`Current push option: '${e}' (parsed as ${typeof e})`)}const a=parsePushAttempts(io_getInput("push_attempts")||"1");core_debug(`Current push_attempts option: ${a}`);if(a>1&&!o){warning("push_attempts is greater than 1 but pull is not set. Retries will re-run push only; without pull (e.g. --rebase), concurrent remote updates are unlikely to recover.")}if(!io_getInput("github_token"))warning("No github_token has been detected, the action may fail if it needs to use the API")}const Ua=io_getInput("cwd")||"";const Na=resolveBaseDir(Ua);let Ma;function gitChildEnv(e={}){const t={...process.env,...e};if(!io_getInput("allow_unsafe_git_protocols",true)){t.GIT_ALLOW_PROTOCOL="https:http:ssh:file:git";t.GIT_PROTOCOL_FROM_USER="0"}return t}function parseGitArgs(e){return matchGitArgs(e,{allowUnsafeGitProtocols:io_getInput("allow_unsafe_git_protocols",true)})}const La=[];safeInfo(`Running in ${Na}`);(async()=>{assertWorkingDirectory(Na,Ua);Ma=lo({baseDir:Na});await checkInputs();Ma.env(gitChildEnv());const e=io_getInput("dry_run",true);startGroup("Internal logs");safeInfo(e?"> Staging files (dry run)...":"> Staging files...");const t=io_getInput("pathspec_error_handling")==="ignore"?"pathspec":"none";let r=false;if(io_getInput("add")){safeInfo(e?"> Adding files (dry run)...":"> Adding files...");const s=await add(t,e);if(e)r=r||s.some(e=>typeof e==="string"&&e.trim().length>0)}else safeInfo("> No files to add.");if(io_getInput("remove")){safeInfo(e?"> Removing files (dry run)...":"> Removing files...");const s=await main_remove(t,e);if(e)r=r||s.some(e=>{if(e===null||e===undefined)return false;const t=typeof e==="string"?e:String(e);return t.trim().length>0})}else safeInfo("> No files to remove.");safeInfo("> Checking for uncommitted changes in the git working tree...");const s=(await Ma.diffSummary(["--cached"])).files.length;const n=parseGitArgs(io_getInput("commit")||"").includes("--allow-empty");if(s>0||r||n){safeInfo(e?`> Dry run: would proceed (${s} already staged`+`${r?", staging probes reported changes":""}`+`${n?", --allow-empty":""}).`:`> Found ${s} changed files.`);core_debug(`--allow-empty argument detected: ${n}`);if(e){await logDryRunRemainingSteps();endGroup();safeInfo("> Dry run completed. No changes were made.");return}await Ma.addConfig("user.email",io_getInput("author_email"),undefined,log).addConfig("user.name",io_getInput("author_name"),undefined,log).addConfig("author.email",io_getInput("author_email"),undefined,log).addConfig("author.name",io_getInput("author_name"),undefined,log).addConfig("committer.email",io_getInput("committer_email"),undefined,log).addConfig("committer.name",io_getInput("committer_name"),undefined,log);if(isDebug()){const e=pickGitIdentityConfig((await Ma.listConfig()).all);core_debug(Object.keys(e).length?"> Current git identity config\n"+JSON.stringify(e,null,2):"> Git identity config set (no identity keys present in listConfig)")}let o;try{o=io_getInput("fetch",true)}catch{o=io_getInput("fetch")}if(o){safeInfo("> Fetching repo...");await Ma.fetch(parseGitArgs(o===true?"":o),log)}else safeInfo("> Not fetching repo.");const a=io_getInput("new_branch");if(a){safeInfo("> Checking-out branch...");if(!o)warning("Creating a new branch without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.");await Ma.checkout([a]).then(()=>{log(undefined,`'${a}' branch already existed.`)}).catch(()=>{log(undefined,`Creating '${a}' branch.`);return Ma.checkout(["-b",a],log)})}const l=parseBoolOrGitArgs("pull");if(l){await pullFromRemote(l,{restage:true,ignoreErrors:t})}else safeInfo("> Not pulling from repo.");safeInfo("> Creating commit...");const u=await Ma.commit(io_getInput("message"),parseGitArgs(io_getInput("commit")||""));log(undefined,u);if(!u.commit){throw new Error("Commit did not produce a SHA; refusing to report committed=true.")}io_setOutput("committed","true");io_setOutput("commit_long_sha",u.commit);io_setOutput("commit_sha",u.commit.substring(0,7));if(io_getInput("tag")){safeInfo("> Tagging commit...");if(!o)warning("Creating a tag without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.");await Ma.tag(parseGitArgs(io_getInput("tag")||""),(e,t)=>{if(t)io_setOutput("tagged","true");return log(e,t)}).then(e=>{io_setOutput("tagged","true");return log(null,e)}).catch(e=>setFailed(e))}else safeInfo("> No tag info provided.");let g;try{g=io_getInput("push",true)}catch{g=io_getInput("push")}if(g){const e=parsePushAttempts(io_getInput("push_attempts")||"1");for(let r=1;r<=e;r++){try{safeInfo(e>1?`> Pushing commit to repo (attempt ${r}/${e})...`:"> Pushing commit to repo...");await pushCommit(g);break}catch(s){if(r===e)throw s;const n=s instanceof Error?s.message:String(s);warning(`Push failed (attempt ${r}/${e}): ${neutralizeLogString(n)}`);if(l){await pullFromRemote(l,{restage:false,ignoreErrors:t});const e=(await Ma.revparse(["HEAD"])).trim();io_setOutput("commit_long_sha",e);io_setOutput("commit_sha",e.substring(0,7))}}}if(io_getInput("tag")){safeInfo("> Pushing tags to repo...");await Ma.pushTags("origin",parseGitArgs(io_getInput("tag_push")||"")).then(e=>{io_setOutput("tag_pushed","true");return log(null,e)}).catch(e=>setFailed(e))}else safeInfo("> No tags to push.")}else safeInfo("> Not pushing anything.");endGroup();safeInfo("> Task completed.")}else{endGroup();safeInfo(e?"> Dry run: working tree clean. Nothing would be committed.":"> Working tree clean. Nothing to commit.")}})().then(()=>{if(La.length===1)throw La[0];else if(La.length>1){La.forEach(e=>error(e));throw"There have been multiple runtime errors."}}).then(logOutputs).catch(e=>{endGroup();logOutputs();setFailed(e)});async function logDryRunRemainingSteps(){safeInfo(`> Would set git identity: ${io_getInput("author_name")} <${io_getInput("author_email")}> (committer: ${io_getInput("committer_name")} <${io_getInput("committer_email")}>)`);let e;try{e=io_getInput("fetch",true)}catch{e=io_getInput("fetch")}if(e){safeInfo(`> Would fetch repo${e===true?"":` with: ${e}`}.`)}else safeInfo("> Would not fetch repo.");const t=io_getInput("new_branch");if(t){safeInfo(`> Would check out branch '${t}'.`);if(!e)warning("Creating a new branch without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.")}const r=parseBoolOrGitArgs("pull");if(r){safeInfo(`> Would pull from remote${r===true?"":` with: ${r}`}.`)}else safeInfo("> Would not pull from repo.");safeInfo(`> Would create commit with message: "${io_getInput("message")}"${io_getInput("commit")?` (extra args: ${io_getInput("commit")})`:""}.`);if(io_getInput("tag")){safeInfo(`> Would tag commit with: ${io_getInput("tag")}.`);if(!e)warning("Creating a tag without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.")}else safeInfo("> No tag info provided.");let s;try{s=io_getInput("push",true)}catch{s=io_getInput("push")}if(s){const e=parsePushAttempts(io_getInput("push_attempts")||"1");if(s===true){const t=io_getInput("new_branch");safeInfo(t?`> Would push commit to repo (set upstream for '${t}')${e>1?` with up to ${e} attempts`:""}.`:`> Would push commit to repo${e>1?` with up to ${e} attempts`:""}.`)}else safeInfo(`> Would push commit to repo with: ${s}${e>1?` (up to ${e} attempts)`:""}.`);if(io_getInput("tag")){safeInfo(`> Would push tags to repo${io_getInput("tag_push")?` with: ${io_getInput("tag_push")}`:""}.`)}else safeInfo("> No tags to push.")}else safeInfo("> Would not push anything.")}async function pullFromRemote(e,t){safeInfo("> Pulling from remote...");const r=e===true?"":e;core_debug(`Current git pull arguments: ${r}`);await Ma.fetch(undefined,log).pull(undefined,undefined,parseGitArgs(r),log);safeInfo("> Checking for conflicts...");const s=await Ma.status(undefined,log);if(s.conflicted.length){throw new Error(`There are ${s.conflicted.length} conflicting files: ${s.conflicted.map(neutralizeLogString).join(", ")}`)}safeInfo("> No conflicts found.");if(t.restage){safeInfo("> Re-staging files...");if(io_getInput("add"))await add(t.ignoreErrors);if(io_getInput("remove"))await main_remove(t.ignoreErrors)}}async function pushCommit(e){if(e===true){const e=io_getInput("new_branch");if(e){core_debug(`Running: git push --set-upstream origin -- ${e}`);await Ma.raw(["push","--set-upstream","origin","--",e],(e,t)=>{if(t)io_setOutput("pushed","true");return log(e,t)})}else{core_debug("Running: git push origin --set-upstream");await Ma.push("origin",undefined,{"--set-upstream":null},(e,t)=>{if(t)io_setOutput("pushed","true");return log(e,t)})}}else{core_debug(`Running: git push ${e}`);await Ma.push(undefined,undefined,parseGitArgs(e),(e,t)=>{if(t)io_setOutput("pushed","true");return log(e,t)})}}async function add(e="none",t=false){const r=io_getInput("add");if(!r)return[];const s=parseInputArray(r);const n=[];for(const r of s){const s=t?["--dry-run",...parseGitArgs(r)]:parseGitArgs(r);n.push(await Ma.add(s,(t,r)=>log(e==="all"?null:t,r)).catch(t=>{if(e==="all")return;if(t.message.includes("fatal: pathspec")&&t.message.includes("did not match any files")){if(e==="pathspec")return;const t=io_getInput("pathspec_error_handling"),s=new Error(`Add command did not match any file: git add ${neutralizeLogString(r)}`);if(t==="exitImmediately")throw s;if(t==="exitAtEnd")La.push(s)}else throw t}))}if(t){await assertGitlinksWithTempIndex(s,e)}else{const e=await Ma.raw(["diff","--cached","--raw"]);assertNoUnexpectedGitlinks(findUnexpectedGitlinks(e))}return n}async function assertGitlinksWithTempIndex(e,t){const r=u.join(n.tmpdir(),`add-and-commit-${process.pid}-${Date.now()}.index`);try{const s=(await Ma.raw(["rev-parse","--git-path","index"])).trim();const n=u.isAbsolute(s)?s:u.join(Na,s);if(l.existsSync(n)){l.copyFileSync(n,r)}else{const e=lo({baseDir:Na}).env(gitChildEnv({GIT_INDEX_FILE:r}));const t=await Ma.raw(["rev-parse","--verify","HEAD"]).then(()=>true).catch(()=>false);if(t){await e.raw(["read-tree","HEAD"])}else{await e.raw(["read-tree","--empty"])}}const o=lo({baseDir:Na}).env(gitChildEnv({GIT_INDEX_FILE:r}));for(const r of e){await o.add(parseGitArgs(r),(e,r)=>log(t==="all"?null:e,r)).catch(e=>{if(t==="all")return;if(e.message.includes("fatal: pathspec")&&e.message.includes("did not match any files")){if(t==="pathspec")return;const e=io_getInput("pathspec_error_handling");if(e==="exitImmediately"){throw new Error(`Add command did not match any file: git add ${neutralizeLogString(r)}`)}return}throw e})}const a=await o.raw(["diff","--cached","--raw"]);assertNoUnexpectedGitlinks(findUnexpectedGitlinks(a))}finally{l.rmSync(r,{force:true});l.rmSync(`${r}.lock`,{force:true})}}async function main_remove(e="none",t=false){const r=io_getInput("remove");if(!r)return[];const s=parseInputArray(r);const n=[];for(const r of s){const s=t?["--dry-run",...parseGitArgs(r)]:parseGitArgs(r);n.push(await Ma.rm(s,(t,r)=>log(e==="all"?null:t,r)).catch(t=>{if(e==="all")return;if(t.message.includes("fatal: pathspec")&&t.message.includes("did not match any files")){if(e==="pathspec")return;const t=io_getInput("pathspec_error_handling"),s=new Error(`Remove command did not match any file:\n git rm ${neutralizeLogString(r)}`);if(t==="exitImmediately")throw s;if(t==="exitAtEnd")La.push(s)}else throw t}))}return n} \ No newline at end of file +/* v8 ignore else -- @preserve */var zA="0.0.0-development";function _buildMessageForResponseErrors(e){return`Request failed due to following response errors:\n`+e.errors.map(e=>` - ${e.message}`).join("\n")}var KA=class extends Error{constructor(e,t,r){super(_buildMessageForResponseErrors(r));this.request=e;this.headers=t;this.response=r;this.errors=r.errors;this.data=r.data;if(Error.captureStackTrace){Error.captureStackTrace(this,this.constructor)}}name="GraphqlResponseError";errors;data};var ZA=["method","baseUrl","url","headers","request","query","mediaType","operationName"];var $A=["query","method","url"];var XA=/\/api\/v3\/?$/;function graphql(e,t,r){if(r){if(typeof t==="string"&&"query"in r){return Promise.reject(new Error(`[@octokit/graphql] "query" cannot be used as variable name`))}for(const e in r){if(!$A.includes(e))continue;return Promise.reject(new Error(`[@octokit/graphql] "${e}" cannot be used as variable name`))}}const s=typeof t==="string"?Object.assign({query:t},r):t;const n=Object.keys(s).reduce((e,t)=>{if(ZA.includes(t)){e[t]=s[t];return e}if(!e.variables){e.variables={}}e.variables[t]=s[t];return e},{});const o=s.baseUrl||e.endpoint.DEFAULTS.baseUrl;if(XA.test(o)){n.url=o.replace(XA,"/api/graphql")}return e(n).then(e=>{if(e.data.errors){const t={};for(const r of Object.keys(e.headers)){t[r]=e.headers[r]}throw new KA(n,t,e.data)}return e.data.data})}function graphql_dist_bundle_withDefaults(e,t){const r=e.defaults(t);const newApi=(e,t)=>graphql(r,e,t);return Object.assign(newApi,{defaults:graphql_dist_bundle_withDefaults.bind(null,r),endpoint:r.endpoint})}var ea=graphql_dist_bundle_withDefaults(jA,{headers:{"user-agent":`octokit-graphql.js/${zA} ${getUserAgent()}`},method:"POST",url:"/graphql"});function withCustomRequest(e){return graphql_dist_bundle_withDefaults(e,{method:"POST",url:"/graphql"})}var ta="(?:[a-zA-Z0-9_-]+)";var ra="\\.";var sa=new RegExp(`^${ta}${ra}${ta}${ra}${ta}$`);var na=sa.test.bind(sa);async function auth(e){const t=na(e);const r=e.startsWith("v1.")||e.startsWith("ghs_");const s=e.startsWith("ghu_");const n=t?"app":r?"installation":s?"user-to-server":"oauth";return{type:"token",token:e,tokenType:n}}function withAuthorizationPrefix(e){if(e.split(/\./).length===3){return`bearer ${e}`}return`token ${e}`}async function hook(e,t,r,s){const n=t.endpoint.merge(r,s);n.headers.authorization=withAuthorizationPrefix(e);return t(n)}var oa=function createTokenAuth2(e){if(!e){throw new Error("[@octokit/auth-token] No token passed to createTokenAuth")}if(typeof e!=="string"){throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string")}e=e.replace(/^(token|bearer) +/i,"");return Object.assign(auth.bind(null,e),{hook:hook.bind(null,e)})};const ia="7.0.6";const dist_src_noop=()=>{};const Aa=console.warn.bind(console);const aa=console.error.bind(console);function dist_src_createLogger(e={}){if(typeof e.debug!=="function"){e.debug=dist_src_noop}if(typeof e.info!=="function"){e.info=dist_src_noop}if(typeof e.warn!=="function"){e.warn=Aa}if(typeof e.error!=="function"){e.error=aa}return e}const ca=`octokit-core.js/${ia} ${getUserAgent()}`;class Octokit{static VERSION=ia;static defaults(e){const t=class extends(this){constructor(...t){const r=t[0]||{};if(typeof e==="function"){super(e(r));return}super(Object.assign({},e,r,r.userAgent&&e.userAgent?{userAgent:`${r.userAgent} ${e.userAgent}`}:null))}};return t}static plugins=[];static plugin(...e){const t=this.plugins;const r=class extends(this){static plugins=t.concat(e.filter(e=>!t.includes(e)))};return r}constructor(e={}){const t=new OA.Collection;const r={baseUrl:jA.endpoint.DEFAULTS.baseUrl,headers:{},request:Object.assign({},e.request,{hook:t.bind(null,"request")}),mediaType:{previews:[],format:""}};r.headers["user-agent"]=e.userAgent?`${e.userAgent} ${ca}`:ca;if(e.baseUrl){r.baseUrl=e.baseUrl}if(e.previews){r.mediaType.previews=e.previews}if(e.timeZone){r.headers["time-zone"]=e.timeZone}this.request=jA.defaults(r);this.graphql=withCustomRequest(this.request).defaults(r);this.log=dist_src_createLogger(e.log);this.hook=t;if(!e.authStrategy){if(!e.auth){this.auth=async()=>({type:"unauthenticated"})}else{const r=oa(e.auth);t.wrap("request",r.hook);this.auth=r}}else{const{authStrategy:r,...s}=e;const n=r(Object.assign({request:this.request,log:this.log,octokit:this,octokitOptions:s},e.auth));t.wrap("request",n.hook);this.auth=n}const s=this.constructor;for(let t=0;t({async next(){if(!l)return{done:true};try{const e=await n({method:o,url:l,headers:a});const t=normalizePaginatedListResponse(e);l=((t.headers.link||"").match(/<([^<>]+)>;\s*rel="next"/)||[])[1];if(!l&&"total_commits"in t.data){const e=new URL(t.url);const r=e.searchParams;const s=parseInt(r.get("page")||"1",10);const n=parseInt(r.get("per_page")||"250",10);if(s*n{if(n.done){return t}let o=false;function done(){o=true}t=t.concat(s?s(n.value,done):n.value.data);if(o){return t}return gather(e,t,r,s)})}var Ea=Object.assign(paginate,{iterator:iterator});var fa=null&&["GET /advisories","GET /app/hook/deliveries","GET /app/installation-requests","GET /app/installations","GET /assignments/{assignment_id}/accepted_assignments","GET /classrooms","GET /classrooms/{classroom_id}/assignments","GET /enterprises/{enterprise}/code-security/configurations","GET /enterprises/{enterprise}/code-security/configurations/{configuration_id}/repositories","GET /enterprises/{enterprise}/dependabot/alerts","GET /enterprises/{enterprise}/teams","GET /enterprises/{enterprise}/teams/{enterprise-team}/memberships","GET /enterprises/{enterprise}/teams/{enterprise-team}/organizations","GET /events","GET /gists","GET /gists/public","GET /gists/starred","GET /gists/{gist_id}/comments","GET /gists/{gist_id}/commits","GET /gists/{gist_id}/forks","GET /installation/repositories","GET /issues","GET /licenses","GET /marketplace_listing/plans","GET /marketplace_listing/plans/{plan_id}/accounts","GET /marketplace_listing/stubbed/plans","GET /marketplace_listing/stubbed/plans/{plan_id}/accounts","GET /networks/{owner}/{repo}/events","GET /notifications","GET /organizations","GET /organizations/{org}/dependabot/repository-access","GET /orgs/{org}/actions/cache/usage-by-repository","GET /orgs/{org}/actions/hosted-runners","GET /orgs/{org}/actions/permissions/repositories","GET /orgs/{org}/actions/permissions/self-hosted-runners/repositories","GET /orgs/{org}/actions/runner-groups","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/hosted-runners","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/repositories","GET /orgs/{org}/actions/runner-groups/{runner_group_id}/runners","GET /orgs/{org}/actions/runners","GET /orgs/{org}/actions/secrets","GET /orgs/{org}/actions/secrets/{secret_name}/repositories","GET /orgs/{org}/actions/variables","GET /orgs/{org}/actions/variables/{name}/repositories","GET /orgs/{org}/attestations/repositories","GET /orgs/{org}/attestations/{subject_digest}","GET /orgs/{org}/blocks","GET /orgs/{org}/campaigns","GET /orgs/{org}/code-scanning/alerts","GET /orgs/{org}/code-security/configurations","GET /orgs/{org}/code-security/configurations/{configuration_id}/repositories","GET /orgs/{org}/codespaces","GET /orgs/{org}/codespaces/secrets","GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories","GET /orgs/{org}/copilot/billing/seats","GET /orgs/{org}/copilot/metrics","GET /orgs/{org}/dependabot/alerts","GET /orgs/{org}/dependabot/secrets","GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories","GET /orgs/{org}/events","GET /orgs/{org}/failed_invitations","GET /orgs/{org}/hooks","GET /orgs/{org}/hooks/{hook_id}/deliveries","GET /orgs/{org}/insights/api/route-stats/{actor_type}/{actor_id}","GET /orgs/{org}/insights/api/subject-stats","GET /orgs/{org}/insights/api/user-stats/{user_id}","GET /orgs/{org}/installations","GET /orgs/{org}/invitations","GET /orgs/{org}/invitations/{invitation_id}/teams","GET /orgs/{org}/issues","GET /orgs/{org}/members","GET /orgs/{org}/members/{username}/codespaces","GET /orgs/{org}/migrations","GET /orgs/{org}/migrations/{migration_id}/repositories","GET /orgs/{org}/organization-roles/{role_id}/teams","GET /orgs/{org}/organization-roles/{role_id}/users","GET /orgs/{org}/outside_collaborators","GET /orgs/{org}/packages","GET /orgs/{org}/packages/{package_type}/{package_name}/versions","GET /orgs/{org}/personal-access-token-requests","GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories","GET /orgs/{org}/personal-access-tokens","GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories","GET /orgs/{org}/private-registries","GET /orgs/{org}/projects","GET /orgs/{org}/projectsV2","GET /orgs/{org}/projectsV2/{project_number}/fields","GET /orgs/{org}/projectsV2/{project_number}/items","GET /orgs/{org}/properties/values","GET /orgs/{org}/public_members","GET /orgs/{org}/repos","GET /orgs/{org}/rulesets","GET /orgs/{org}/rulesets/rule-suites","GET /orgs/{org}/rulesets/{ruleset_id}/history","GET /orgs/{org}/secret-scanning/alerts","GET /orgs/{org}/security-advisories","GET /orgs/{org}/settings/immutable-releases/repositories","GET /orgs/{org}/settings/network-configurations","GET /orgs/{org}/team/{team_slug}/copilot/metrics","GET /orgs/{org}/teams","GET /orgs/{org}/teams/{team_slug}/discussions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions","GET /orgs/{org}/teams/{team_slug}/invitations","GET /orgs/{org}/teams/{team_slug}/members","GET /orgs/{org}/teams/{team_slug}/projects","GET /orgs/{org}/teams/{team_slug}/repos","GET /orgs/{org}/teams/{team_slug}/teams","GET /projects/{project_id}/collaborators","GET /repos/{owner}/{repo}/actions/artifacts","GET /repos/{owner}/{repo}/actions/caches","GET /repos/{owner}/{repo}/actions/organization-secrets","GET /repos/{owner}/{repo}/actions/organization-variables","GET /repos/{owner}/{repo}/actions/runners","GET /repos/{owner}/{repo}/actions/runs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts","GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs","GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs","GET /repos/{owner}/{repo}/actions/secrets","GET /repos/{owner}/{repo}/actions/variables","GET /repos/{owner}/{repo}/actions/workflows","GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs","GET /repos/{owner}/{repo}/activity","GET /repos/{owner}/{repo}/assignees","GET /repos/{owner}/{repo}/attestations/{subject_digest}","GET /repos/{owner}/{repo}/branches","GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations","GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs","GET /repos/{owner}/{repo}/code-scanning/alerts","GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances","GET /repos/{owner}/{repo}/code-scanning/analyses","GET /repos/{owner}/{repo}/codespaces","GET /repos/{owner}/{repo}/codespaces/devcontainers","GET /repos/{owner}/{repo}/codespaces/secrets","GET /repos/{owner}/{repo}/collaborators","GET /repos/{owner}/{repo}/comments","GET /repos/{owner}/{repo}/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/commits","GET /repos/{owner}/{repo}/commits/{commit_sha}/comments","GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls","GET /repos/{owner}/{repo}/commits/{ref}/check-runs","GET /repos/{owner}/{repo}/commits/{ref}/check-suites","GET /repos/{owner}/{repo}/commits/{ref}/status","GET /repos/{owner}/{repo}/commits/{ref}/statuses","GET /repos/{owner}/{repo}/compare/{basehead}","GET /repos/{owner}/{repo}/compare/{base}...{head}","GET /repos/{owner}/{repo}/contributors","GET /repos/{owner}/{repo}/dependabot/alerts","GET /repos/{owner}/{repo}/dependabot/secrets","GET /repos/{owner}/{repo}/deployments","GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses","GET /repos/{owner}/{repo}/environments","GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies","GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps","GET /repos/{owner}/{repo}/environments/{environment_name}/secrets","GET /repos/{owner}/{repo}/environments/{environment_name}/variables","GET /repos/{owner}/{repo}/events","GET /repos/{owner}/{repo}/forks","GET /repos/{owner}/{repo}/hooks","GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries","GET /repos/{owner}/{repo}/invitations","GET /repos/{owner}/{repo}/issues","GET /repos/{owner}/{repo}/issues/comments","GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/issues/events","GET /repos/{owner}/{repo}/issues/{issue_number}/comments","GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocked_by","GET /repos/{owner}/{repo}/issues/{issue_number}/dependencies/blocking","GET /repos/{owner}/{repo}/issues/{issue_number}/events","GET /repos/{owner}/{repo}/issues/{issue_number}/labels","GET /repos/{owner}/{repo}/issues/{issue_number}/reactions","GET /repos/{owner}/{repo}/issues/{issue_number}/sub_issues","GET /repos/{owner}/{repo}/issues/{issue_number}/timeline","GET /repos/{owner}/{repo}/keys","GET /repos/{owner}/{repo}/labels","GET /repos/{owner}/{repo}/milestones","GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels","GET /repos/{owner}/{repo}/notifications","GET /repos/{owner}/{repo}/pages/builds","GET /repos/{owner}/{repo}/projects","GET /repos/{owner}/{repo}/pulls","GET /repos/{owner}/{repo}/pulls/comments","GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions","GET /repos/{owner}/{repo}/pulls/{pull_number}/comments","GET /repos/{owner}/{repo}/pulls/{pull_number}/commits","GET /repos/{owner}/{repo}/pulls/{pull_number}/files","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews","GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments","GET /repos/{owner}/{repo}/releases","GET /repos/{owner}/{repo}/releases/{release_id}/assets","GET /repos/{owner}/{repo}/releases/{release_id}/reactions","GET /repos/{owner}/{repo}/rules/branches/{branch}","GET /repos/{owner}/{repo}/rulesets","GET /repos/{owner}/{repo}/rulesets/rule-suites","GET /repos/{owner}/{repo}/rulesets/{ruleset_id}/history","GET /repos/{owner}/{repo}/secret-scanning/alerts","GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations","GET /repos/{owner}/{repo}/security-advisories","GET /repos/{owner}/{repo}/stargazers","GET /repos/{owner}/{repo}/subscribers","GET /repos/{owner}/{repo}/tags","GET /repos/{owner}/{repo}/teams","GET /repos/{owner}/{repo}/topics","GET /repositories","GET /search/code","GET /search/commits","GET /search/issues","GET /search/labels","GET /search/repositories","GET /search/topics","GET /search/users","GET /teams/{team_id}/discussions","GET /teams/{team_id}/discussions/{discussion_number}/comments","GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions","GET /teams/{team_id}/discussions/{discussion_number}/reactions","GET /teams/{team_id}/invitations","GET /teams/{team_id}/members","GET /teams/{team_id}/projects","GET /teams/{team_id}/repos","GET /teams/{team_id}/teams","GET /user/blocks","GET /user/codespaces","GET /user/codespaces/secrets","GET /user/emails","GET /user/followers","GET /user/following","GET /user/gpg_keys","GET /user/installations","GET /user/installations/{installation_id}/repositories","GET /user/issues","GET /user/keys","GET /user/marketplace_purchases","GET /user/marketplace_purchases/stubbed","GET /user/memberships/orgs","GET /user/migrations","GET /user/migrations/{migration_id}/repositories","GET /user/orgs","GET /user/packages","GET /user/packages/{package_type}/{package_name}/versions","GET /user/public_emails","GET /user/repos","GET /user/repository_invitations","GET /user/social_accounts","GET /user/ssh_signing_keys","GET /user/starred","GET /user/subscriptions","GET /user/teams","GET /users","GET /users/{username}/attestations/{subject_digest}","GET /users/{username}/events","GET /users/{username}/events/orgs/{org}","GET /users/{username}/events/public","GET /users/{username}/followers","GET /users/{username}/following","GET /users/{username}/gists","GET /users/{username}/gpg_keys","GET /users/{username}/keys","GET /users/{username}/orgs","GET /users/{username}/packages","GET /users/{username}/projects","GET /users/{username}/projectsV2","GET /users/{username}/projectsV2/{project_number}/fields","GET /users/{username}/projectsV2/{project_number}/items","GET /users/{username}/received_events","GET /users/{username}/received_events/public","GET /users/{username}/repos","GET /users/{username}/social_accounts","GET /users/{username}/ssh_signing_keys","GET /users/{username}/starred","GET /users/{username}/subscriptions"];function isPaginatingEndpoint(e){if(typeof e==="string"){return fa.includes(e)}else{return false}}function paginateRest(e){return{paginate:Object.assign(paginate.bind(null,e),{iterator:iterator.bind(null,e)})}}paginateRest.VERSION=pa;const Ca=new Context;const Qa=getApiBaseUrl();const Ia={baseUrl:Qa,request:{agent:getProxyAgent(Qa),fetch:getProxyFetch(Qa)}};const Ba=Octokit.plugin(restEndpointMethods,paginateRest).defaults(Ia);function getOctokitOptions(e,t){const r=Object.assign({},t||{});const s=getAuthString(e,r);if(s){r.auth=s}const n=getUserAgentWithOrchestrationId(r.userAgent);if(n){r.userAgent=n}return r}const ma=new Context;function getOctokit(e,t,...r){const s=Ba.plugin(...r);return new s(getOctokitOptions(e,t))}function resolveBaseDir(e,t=process.cwd()){return u.resolve(t,e||".")}function assertWorkingDirectory(e,t){if(!l.existsSync(e)||!l.statSync(e).isDirectory()){throw new Error(`The cwd input '${neutralizeLogString(t||".")}' resolved to '${neutralizeLogString(e)}', which is not an existing directory. `+"Use a path relative to the runner workspace, or an absolute path that exists "+"(e.g. ${{ github.workspace }}/path — note that $GITHUB_WORKSPACE is not expanded in with:).")}}function getOctokitClient(){const e=io_getInput("github_token");if(!e){throw new Error("github_token is required")}return getOctokit(e)}async function getUserInfo(e){if(!e)return undefined;const t=getOctokitClient();const r=await t.rest.users.getByUsername({username:e});core_debug(`Fetched github actor from the API: ${JSON.stringify(r?.data,null,2)}`);return{name:r?.data?.name,email:r?.data?.email}}const wa=/[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u2028-\u202E\u2066-\u2069]/gu;function neutralizeLogString(e){return e.replace(wa,e=>{const t=e.codePointAt(0).toString(16).padStart(4,"0");return`\\u${t}`})}function safeInfo(e){info(neutralizeLogString(e))}const ya="[Circular]";function neutralizeForLog(e,t=new WeakSet){if(typeof e==="string")return neutralizeLogString(e);if(typeof e==="number"||typeof e==="boolean"||e===null||e===undefined){return e}if(e instanceof Error){const t=new Error(neutralizeLogString(e.message));t.name=neutralizeLogString(e.name);if(e.stack){t.stack=neutralizeLogString(e.stack)}return t}if(Array.isArray(e)){if(t.has(e))return ya;t.add(e);return e.map(e=>neutralizeForLog(e,t))}if(typeof e==="object"){if(t.has(e))return ya;t.add(e);const r={};for(const[s,n]of Object.entries(e)){r[neutralizeLogString(s)]=neutralizeForLog(n,t)}return r}return e}function log(e,t){if(t)console.log(neutralizeForLog(t));if(e){const t=neutralizeForLog(e);if(typeof t==="string"||t instanceof Error){error(t)}else{error(String(t))}}}const ba=["user.name","user.email","author.name","author.email","committer.name","committer.email"];function pickGitIdentityConfig(e){const t={};for(const r of ba){if(Object.prototype.hasOwnProperty.call(e,r)){t[r]=e[r]}}return t}function assertValidBranchName(e){if(!e||!e.trim()){throw new Error("The new_branch value is empty.")}if(e.startsWith("-")){throw new Error(`The new_branch value '${neutralizeLogString(e)}' cannot start with '-' (it would be interpreted as a git option).`)}for(const t of e){const r=t.codePointAt(0);if(r<=31||r===127||r>=128&&r<=159||/\s/u.test(t)){throw new Error(`The new_branch value '${neutralizeLogString(e)}' contains whitespace or control characters.`)}}try{(0,ue.execFileSync)("git",["check-ref-format","--branch",e],{stdio:"ignore"})}catch{throw new Error(`The new_branch value '${neutralizeLogString(e)}' is not a valid git branch name.`)}}const ka=[{canonical:"upload-pack",minPrefix:"upl"},{canonical:"receive-pack",minPrefix:"rece"},{canonical:"exec",minPrefix:"e"}];const Ta=[{canonical:"file",minPrefix:"fi"}];const Ra=[{canonical:"pathspec-from-file",minPrefix:"pathspec-fr"},{canonical:"pathspec-file-nul",minPrefix:"pathspec-fi"}];const Da=[{canonical:"message",minPrefix:"mes"},{canonical:"local-user",minPrefix:"local-"},{canonical:"cleanup",minPrefix:"cleanup"},{canonical:"file",minPrefix:"fi"},{canonical:"pathspec-from-file",minPrefix:"pathspec-fr"},{canonical:"upload-pack",minPrefix:"upl"},{canonical:"receive-pack",minPrefix:"rece"},{canonical:"exec",minPrefix:"e"}];const Sa=new Set(["m","F"]);function getLongOptionName(e){if(!e.startsWith("--")||e==="--")return undefined;const t=e.slice(2);const r=t.indexOf("=");return(r===-1?t:t.slice(0,r)).toLowerCase()}function longOptionHasInlineValue(e){if(!e.startsWith("--")||e==="--")return false;return e.slice(2).includes("=")}function matchesLongOptionPrefix(e,t){const r=getLongOptionName(e);if(!r)return false;return t.some(({canonical:e,minPrefix:t})=>r.length>=t.length&&e.startsWith(r))}function isDangerousRemoteHelperOption(e){return matchesLongOptionPrefix(e,ka)}function isDangerousMessageFileShortOption(e){if(!e.startsWith("-")||e.startsWith("--"))return false;const t=e.slice(1);for(let e=0;e` - ${neutralizeLogString(e)}`).join("\n");const r=e.map(e=>` git rm --cached -- ${neutralizeLogString(e)}`).join("\n");throw new Error(`Refusing to commit unexpected gitlink(s) (embedded git repository staged as mode 160000):\n${t}\n`+"Git records a nested .git directory as a gitlink, not as its files. "+`Remove the nested .git directory, or unstage the path(s) with:\n${r}`)}function parseInputArray(e){core_debug(`Parsing input array: ${e}`);try{const t=load(e);if(t&&Array.isArray(t)&&t.every(e=>typeof e==="string")){core_debug(`Input parsed as YAML array of length ${t.length}`);return t}}catch{}core_debug("Input parsed as single string");return[e]}function readJSON(e){let t;try{t=fs.readFileSync(e,{encoding:"utf8"})}catch{throw`Couldn't read file. File path: ${neutralizeLogString(e)}`}try{return JSON.parse(t)}catch{throw`Couldn't parse file to JSON. File path: ${neutralizeLogString(e)}`}}const va={committed:"false",commit_long_sha:undefined,commit_sha:undefined,pushed:"false",tagged:"false",tag_pushed:"false"};Object.entries(va).forEach(([e,t])=>setOutput(e,t));function io_getInput(e,t=false){if(t)return getBooleanInput(e);return getInput(e)}function io_setOutput(e,t){core_debug(`Setting output: ${e}=${t}`);va[e]=t;setOutput(e,t)}function parseBoolOrGitArgs(e){try{return io_getInput(e,true)}catch{return io_getInput(e)||""}}function parsePushAttempts(e){const t=e.trim();if(!/^\+?\d+$/.test(t)){throw new Error(`'${neutralizeLogString(e)}' is not a valid value for push_attempts. It must be a positive integer (≥ 1).`)}const r=Number.parseInt(t,10);if(!Number.isSafeInteger(r)||r<1){throw new Error(`'${neutralizeLogString(e)}' is not a valid value for push_attempts. It must be a positive integer (≥ 1).`)}return r}function logOutputs(){startGroup("Outputs");for(const e in va){safeInfo(`${e}: ${va[e]}`)}endGroup()}async function checkInputs(){function setInput(e,t){if(t)return process.env[`INPUT_${e.toUpperCase()}`]=t;else return delete process.env[`INPUT_${e.toUpperCase()}`]}function setDefault(e,t){if(!io_getInput(e))setInput(e,t);return io_getInput(e)}if(!io_getInput("add")&&!io_getInput("remove"))throw new Error("Both 'add' and 'remove' are empty, the action has nothing to do.");if(io_getInput("add")){const e=parseInputArray(io_getInput("add"));if(e.length===1)safeInfo("Add input parsed as single string, running 1 git add command.");else if(e.length>1)safeInfo(`Add input parsed as string array, running ${e.length} git add commands.`);else setFailed("Add input: array length < 1")}if(io_getInput("remove")){const e=parseInputArray(io_getInput("remove")||"");if(e.length===1)safeInfo("Remove input parsed as single string, running 1 git rm command.");else if(e.length>1)safeInfo(`Remove input parsed as string array, running ${e.length} git rm commands.`);else setFailed("Remove input: array length < 1")}const e=["github_actor","user_info","github_actions"];if(!e.includes(io_getInput("default_author")))throw new Error(`'${neutralizeLogString(io_getInput("default_author"))}' is not a valid value for default_author. Valid values: ${e.join(", ")}`);if(io_getInput("dry_run",true))safeInfo("> Dry run enabled: no mutating git operations will be performed.");if(io_getInput("allow_unsafe_git_protocols",true))warning("allow_unsafe_git_protocols is enabled: transport allowlist and scheme:: remote-helper URL checks are disabled. Only use this with fully trusted git argument inputs.");if(io_getInput("fetch")){let e;try{e=io_getInput("fetch",true)}catch{e=io_getInput("fetch")}core_debug(`Current fetch option: '${e}' (parsed as ${typeof e})`)}let t,r;switch(io_getInput("default_author")){case"github_actor":{t=process.env.GITHUB_ACTOR??"";r=`${process.env.GITHUB_ACTOR}@users.noreply.github.com`;break}case"user_info":{if(!io_getInput("author_name")||!io_getInput("author_email")){const e=await getUserInfo(process.env.GITHUB_ACTOR);if(!e?.name)warning("Couldn't fetch author name, filling with github_actor.");if(!e?.email)warning("Couldn't fetch author email, filling with github_actor.");e?.name&&(t=e?.name);e?.email&&(r=e.email);if(t&&r)break}!t&&(t=process.env.GITHUB_ACTOR??"");!r&&(r=`${process.env.GITHUB_ACTOR}@users.noreply.github.com`);break}case"github_actions":{t="github-actions";r="41898282+github-actions[bot]@users.noreply.github.com";break}default:throw new Error("This should not happen, please contact the author of this action. (checkInputs.author)")}setDefault("author_name",t);setDefault("author_email",r);safeInfo(`> Using '${io_getInput("author_name")} <${io_getInput("author_email")}>' as author.`);if(io_getInput("committer_name")||io_getInput("committer_email"))safeInfo(`> Using custom committer info: ${io_getInput("committer_name")||io_getInput("author_name")+" [from author info]"} <${io_getInput("committer_email")||io_getInput("author_email")+" [from author info]"}>`);setDefault("committer_name",io_getInput("author_name"));setDefault("committer_email",io_getInput("author_email"));core_debug(`Committer: ${io_getInput("committer_name")} <${io_getInput("committer_email")}>`);setDefault("message",`Commit from GitHub Actions (${process.env.GITHUB_WORKFLOW})`);safeInfo(`> Using "${io_getInput("message")}" as commit message.`);const s=io_getInput("new_branch");if(s)assertValidBranchName(s);const n=["ignore","exitImmediately","exitAtEnd"];if(!n.includes(io_getInput("pathspec_error_handling")))throw new Error(`"${neutralizeLogString(io_getInput("pathspec_error_handling"))}" is not a valid value for the 'pathspec_error_handling' input. Valid values are: ${n.join(", ")}`);if(io_getInput("pull")==="NO-PULL")warning("`NO-PULL` is a legacy option for the `pull` input. If you don't want the action to pull the repo, simply remove this input.");const o=parseBoolOrGitArgs("pull");if(io_getInput("pull")){core_debug(`Current pull option: '${o}' (parsed as ${typeof o})`)}if(io_getInput("push")){let e;try{e=io_getInput("push",true)}catch{e=io_getInput("push")}core_debug(`Current push option: '${e}' (parsed as ${typeof e})`)}const a=parsePushAttempts(io_getInput("push_attempts")||"1");core_debug(`Current push_attempts option: ${a}`);if(a>1&&!o){warning("push_attempts is greater than 1 but pull is not set. Retries will re-run push only; without pull (e.g. --rebase), concurrent remote updates are unlikely to recover.")}if(!io_getInput("github_token"))warning("No github_token has been detected, the action may fail if it needs to use the API")}const Ua=io_getInput("cwd")||"";const Na=resolveBaseDir(Ua);let Ma;function gitChildEnv(e={}){const t={...process.env,...e};if(!io_getInput("allow_unsafe_git_protocols",true)){t.GIT_ALLOW_PROTOCOL="https:http:ssh:file:git";t.GIT_PROTOCOL_FROM_USER="0"}return t}function parseGitArgs(e){return matchGitArgs(e,{allowUnsafeGitProtocols:io_getInput("allow_unsafe_git_protocols",true)})}const La=[];safeInfo(`Running in ${Na}`);(async()=>{assertWorkingDirectory(Na,Ua);Ma=lo({baseDir:Na});await checkInputs();Ma.env(gitChildEnv());const e=io_getInput("dry_run",true);startGroup("Internal logs");safeInfo(e?"> Staging files (dry run)...":"> Staging files...");const t=io_getInput("pathspec_error_handling")==="ignore"?"pathspec":"none";let r=false;if(io_getInput("add")){safeInfo(e?"> Adding files (dry run)...":"> Adding files...");const s=await add(t,e);if(e)r=r||s.some(e=>typeof e==="string"&&e.trim().length>0)}else safeInfo("> No files to add.");if(io_getInput("remove")){safeInfo(e?"> Removing files (dry run)...":"> Removing files...");const s=await main_remove(t,e);if(e)r=r||s.some(e=>{if(e===null||e===undefined)return false;const t=typeof e==="string"?e:String(e);return t.trim().length>0})}else safeInfo("> No files to remove.");safeInfo("> Checking for uncommitted changes in the git working tree...");const s=(await Ma.diffSummary(["--cached"])).files.length;const n=parseGitArgs(io_getInput("commit")||"").includes("--allow-empty");if(s>0||r||n){safeInfo(e?`> Dry run: would proceed (${s} already staged`+`${r?", staging probes reported changes":""}`+`${n?", --allow-empty":""}).`:`> Found ${s} changed files.`);core_debug(`--allow-empty argument detected: ${n}`);if(e){await logDryRunRemainingSteps();endGroup();safeInfo("> Dry run completed. No changes were made.");return}await Ma.addConfig("user.email",io_getInput("author_email"),undefined,log).addConfig("user.name",io_getInput("author_name"),undefined,log).addConfig("author.email",io_getInput("author_email"),undefined,log).addConfig("author.name",io_getInput("author_name"),undefined,log).addConfig("committer.email",io_getInput("committer_email"),undefined,log).addConfig("committer.name",io_getInput("committer_name"),undefined,log);if(isDebug()){const e=pickGitIdentityConfig((await Ma.listConfig()).all);core_debug(Object.keys(e).length?"> Current git identity config\n"+JSON.stringify(e,null,2):"> Git identity config set (no identity keys present in listConfig)")}let o;try{o=io_getInput("fetch",true)}catch{o=io_getInput("fetch")}if(o){safeInfo("> Fetching repo...");await Ma.fetch(parseGitArgs(o===true?"":o),log)}else safeInfo("> Not fetching repo.");const a=io_getInput("new_branch");if(a){safeInfo("> Checking-out branch...");if(!o)warning("Creating a new branch without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.");await Ma.checkout([a]).then(()=>{log(undefined,`'${a}' branch already existed.`)}).catch(()=>{log(undefined,`Creating '${a}' branch.`);return Ma.checkout(["-b",a],log)})}const l=parseBoolOrGitArgs("pull");if(l){await pullFromRemote(l,{restage:true,ignoreErrors:t})}else safeInfo("> Not pulling from repo.");safeInfo("> Creating commit...");const u=await Ma.commit(io_getInput("message"),parseGitArgs(io_getInput("commit")||""));log(undefined,u);if(!u.commit){throw new Error("Commit did not produce a SHA; refusing to report committed=true.")}io_setOutput("committed","true");io_setOutput("commit_long_sha",u.commit);io_setOutput("commit_sha",u.commit.substring(0,7));if(io_getInput("tag")){safeInfo("> Tagging commit...");if(!o)warning("Creating a tag without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.");await Ma.tag(parseGitArgs(io_getInput("tag")||""),(e,t)=>{if(t)io_setOutput("tagged","true");return log(e,t)}).then(e=>{io_setOutput("tagged","true");return log(null,e)}).catch(e=>setFailed(e))}else safeInfo("> No tag info provided.");let g;try{g=io_getInput("push",true)}catch{g=io_getInput("push")}if(g){const e=parsePushAttempts(io_getInput("push_attempts")||"1");for(let r=1;r<=e;r++){try{safeInfo(e>1?`> Pushing commit to repo (attempt ${r}/${e})...`:"> Pushing commit to repo...");await pushCommit(g);break}catch(s){if(r===e)throw s;const n=s instanceof Error?s.message:String(s);warning(`Push failed (attempt ${r}/${e}): ${neutralizeLogString(n)}`);if(l){await pullFromRemote(l,{restage:false,ignoreErrors:t});const e=(await Ma.revparse(["HEAD"])).trim();io_setOutput("commit_long_sha",e);io_setOutput("commit_sha",e.substring(0,7))}}}if(io_getInput("tag")){safeInfo("> Pushing tags to repo...");await Ma.pushTags("origin",parseGitArgs(io_getInput("tag_push")||"")).then(e=>{io_setOutput("tag_pushed","true");return log(null,e)}).catch(e=>setFailed(e))}else safeInfo("> No tags to push.")}else safeInfo("> Not pushing anything.");endGroup();safeInfo("> Task completed.")}else{endGroup();safeInfo(e?"> Dry run: working tree clean. Nothing would be committed.":"> Working tree clean. Nothing to commit.")}})().then(()=>{if(La.length===1)throw La[0];else if(La.length>1){La.forEach(e=>error(e));throw"There have been multiple runtime errors."}}).then(logOutputs).catch(e=>{endGroup();logOutputs();setFailed(e)});async function logDryRunRemainingSteps(){safeInfo(`> Would set git identity: ${io_getInput("author_name")} <${io_getInput("author_email")}> (committer: ${io_getInput("committer_name")} <${io_getInput("committer_email")}>)`);let e;try{e=io_getInput("fetch",true)}catch{e=io_getInput("fetch")}if(e){safeInfo(`> Would fetch repo${e===true?"":` with: ${e}`}.`)}else safeInfo("> Would not fetch repo.");const t=io_getInput("new_branch");if(t){safeInfo(`> Would check out branch '${t}'.`);if(!e)warning("Creating a new branch without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.")}const r=parseBoolOrGitArgs("pull");if(r){safeInfo(`> Would pull from remote${r===true?"":` with: ${r}`}.`)}else safeInfo("> Would not pull from repo.");safeInfo(`> Would create commit with message: "${io_getInput("message")}"${io_getInput("commit")?` (extra args: ${io_getInput("commit")})`:""}.`);if(io_getInput("tag")){safeInfo(`> Would tag commit with: ${io_getInput("tag")}.`);if(!e)warning("Creating a tag without fetching the repo first could result in an error when pushing to GitHub. Refer to the action README for more info about this topic.")}else safeInfo("> No tag info provided.");let s;try{s=io_getInput("push",true)}catch{s=io_getInput("push")}if(s){const e=parsePushAttempts(io_getInput("push_attempts")||"1");if(s===true){const t=io_getInput("new_branch");safeInfo(t?`> Would push commit to repo (set upstream for '${t}')${e>1?` with up to ${e} attempts`:""}.`:`> Would push commit to repo${e>1?` with up to ${e} attempts`:""}.`)}else safeInfo(`> Would push commit to repo with: ${s}${e>1?` (up to ${e} attempts)`:""}.`);if(io_getInput("tag")){safeInfo(`> Would push tags to repo${io_getInput("tag_push")?` with: ${io_getInput("tag_push")}`:""}.`)}else safeInfo("> No tags to push.")}else safeInfo("> Would not push anything.")}async function pullFromRemote(e,t){safeInfo("> Pulling from remote...");const r=e===true?"":e;core_debug(`Current git pull arguments: ${r}`);await Ma.fetch(undefined,log).pull(undefined,undefined,parseGitArgs(r),log);safeInfo("> Checking for conflicts...");const s=await Ma.status(undefined,log);if(s.conflicted.length){throw new Error(`There are ${s.conflicted.length} conflicting files: ${s.conflicted.map(neutralizeLogString).join(", ")}`)}safeInfo("> No conflicts found.");if(t.restage){safeInfo("> Re-staging files...");if(io_getInput("add"))await add(t.ignoreErrors);if(io_getInput("remove"))await main_remove(t.ignoreErrors)}}async function pushCommit(e){if(e===true){const e=io_getInput("new_branch");if(e){core_debug(`Running: git push --set-upstream origin -- ${e}`);await Ma.raw(["push","--set-upstream","origin","--",e],(e,t)=>{if(t)io_setOutput("pushed","true");return log(e,t)})}else{core_debug("Running: git push origin --set-upstream");await Ma.push("origin",undefined,{"--set-upstream":null},(e,t)=>{if(t)io_setOutput("pushed","true");return log(e,t)})}}else{core_debug(`Running: git push ${e}`);await Ma.push(undefined,undefined,parseGitArgs(e),(e,t)=>{if(t)io_setOutput("pushed","true");return log(e,t)})}}async function add(e="none",t=false){const r=io_getInput("add");if(!r)return[];const s=parseInputArray(r);const n=[];for(const r of s){const s=t?["--dry-run",...parseGitArgs(r)]:parseGitArgs(r);n.push(await Ma.add(s,(t,r)=>log(e==="all"?null:t,r)).catch(t=>{if(e==="all")return;if(t.message.includes("fatal: pathspec")&&t.message.includes("did not match any files")){if(e==="pathspec")return;const t=io_getInput("pathspec_error_handling"),s=new Error(`Add command did not match any file: git add ${neutralizeLogString(r)}`);if(t==="exitImmediately")throw s;if(t==="exitAtEnd")La.push(s)}else throw t}))}if(t){await assertGitlinksWithTempIndex(s,e)}else{const e=await Ma.raw(["diff","--cached","--raw"]);assertNoUnexpectedGitlinks(findUnexpectedGitlinks(e))}return n}async function assertGitlinksWithTempIndex(e,t){const r=u.join(n.tmpdir(),`add-and-commit-${process.pid}-${Date.now()}.index`);try{const s=(await Ma.raw(["rev-parse","--git-path","index"])).trim();const n=u.isAbsolute(s)?s:u.join(Na,s);if(l.existsSync(n)){l.copyFileSync(n,r)}else{const e=lo({baseDir:Na}).env(gitChildEnv({GIT_INDEX_FILE:r}));const t=await Ma.raw(["rev-parse","--verify","HEAD"]).then(()=>true).catch(()=>false);if(t){await e.raw(["read-tree","HEAD"])}else{await e.raw(["read-tree","--empty"])}}const o=lo({baseDir:Na}).env(gitChildEnv({GIT_INDEX_FILE:r}));for(const r of e){await o.add(parseGitArgs(r),(e,r)=>log(t==="all"?null:e,r)).catch(e=>{if(t==="all")return;if(e.message.includes("fatal: pathspec")&&e.message.includes("did not match any files")){if(t==="pathspec")return;const e=io_getInput("pathspec_error_handling");if(e==="exitImmediately"){throw new Error(`Add command did not match any file: git add ${neutralizeLogString(r)}`)}return}throw e})}const a=await o.raw(["diff","--cached","--raw"]);assertNoUnexpectedGitlinks(findUnexpectedGitlinks(a))}finally{l.rmSync(r,{force:true});l.rmSync(`${r}.lock`,{force:true})}}async function main_remove(e="none",t=false){const r=io_getInput("remove");if(!r)return[];const s=parseInputArray(r);const n=[];for(const r of s){const s=t?["--dry-run",...parseGitArgs(r)]:parseGitArgs(r);n.push(await Ma.rm(s,(t,r)=>log(e==="all"?null:t,r)).catch(t=>{if(e==="all")return;if(t.message.includes("fatal: pathspec")&&t.message.includes("did not match any files")){if(e==="pathspec")return;const t=io_getInput("pathspec_error_handling"),s=new Error(`Remove command did not match any file:\n git rm ${neutralizeLogString(r)}`);if(t==="exitImmediately")throw s;if(t==="exitAtEnd")La.push(s)}else throw t}))}return n} \ No newline at end of file diff --git a/src/util.ts b/src/util.ts index e34aaa47..16d1c84c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -365,18 +365,34 @@ function consumesFollowingArgument(arg: string): boolean { } /** - * Rejects unmatched `'` / `"` so `string-argv` cannot silently retokenize at an - * odd quote (e.g. `origin fix'--force` → `["origin","fix","--force"]`). - * Balanced quotes and the opposite quote type inside a quoted segment are allowed. + * Conservative argument-boundary check for quotes before `string-argv` runs. + * Not every rejected form would become extra argv words. + * + * Unmatched `'` / `"` are rejected (e.g. `origin fix'--force`). + * A quoted segment is accepted only when its closing quote is followed by + * whitespace or end of input (`origin 'main' --force`, `--message='hello'`). + * Interior glued quotes such as `a'b'c` are rejected even though `string-argv` + * would keep that as one token. A start-quoted token with text after the closer + * (`'main'--force`) is also rejected; that form would split into extra argv + * words. + * + * The opposite quote type inside a quoted segment is allowed. */ -function assertBalancedQuotes(input: string): void { +function assertSafeQuotes(input: string): void { let open: "'" | '"' | null = null; - for (const char of input) { + for (let i = 0; i < input.length; i++) { + const char = input[i]; if (char !== "'" && char !== '"') continue; if (open === null) { open = char; } else if (open === char) { open = null; + const next = input[i + 1]; + if (next !== undefined && !/\s/.test(next)) { + throw new Error( + 'Git arguments contain a quoted segment immediately followed by non-whitespace. string-argv would split that into extra arguments (for example a quoted name glued to --force).', + ); + } } } if (open !== null) { @@ -428,7 +444,7 @@ export type MatchGitArgsOptions = { * matchGitArgs(' ') => [ ] * ``` * @returns An array, if there's no match it'll be empty - * @throws If the args include unmatched quotes + * @throws If the args include unmatched quotes, or a closing quote glued to following text * @throws If the args include a blocked remote-helper override (`--upload-pack`, `--receive-pack`, `--exec`, or abbreviations) on any token, including values after `-u` / `-m` * @throws If the args include a blocked message-from-file flag (`-F`, `--file`, abbreviations, or short-option clusters containing `F`) * @throws If the args include a blocked pathspec-from-file flag (`--pathspec-from-file`, `--pathspec-file-nul`, or abbreviations) @@ -438,7 +454,7 @@ export function matchGitArgs( string: string, options: MatchGitArgsOptions = {}, ) { - assertBalancedQuotes(string); + assertSafeQuotes(string); const parsed = parseArgsStringToArgv(string); core.debug(`Git args parsed: diff --git a/test/integration/action.test.ts b/test/integration/action.test.ts index 199dc551..04ff4b41 100644 --- a/test/integration/action.test.ts +++ b/test/integration/action.test.ts @@ -140,6 +140,25 @@ describe('action integration', () => { expect(`${result.stdout}\n${result.stderr}`).toMatch(/not allowed/); }); + it('rejects glued quotes that would inject --force into push', () => { + const f = fixture!; + writeFile(f.local, 'glued-quotes.txt', 'changed\n'); + const beforeRemote = gitRevParse(f.remote, 'HEAD'); + + const result = runAction(f, { + message: 'Should not push with glued quotes', + fetch: 'false', + push: "origin 'main'--force --set-upstream", + }); + + expect(result.status).not.toBe(0); + expect(result.outputs.pushed).toBe('false'); + expect(gitRevParse(f.remote, 'HEAD')).toBe(beforeRemote); + expect(`${result.stdout}\n${result.stderr}`).toMatch( + /quoted segment immediately followed by non-whitespace/, + ); + }); + it('applies custom author and committer', () => { const f = fixture!; writeFile(f.local, 'id.txt', 'id\n'); diff --git a/test/util.test.ts b/test/util.test.ts index 4d7568cc..a79f7cf5 100644 --- a/test/util.test.ts +++ b/test/util.test.ts @@ -266,10 +266,33 @@ describe('matchGitArgs', () => { ); }); - it('parses balanced quotes without treating them as injection', () => { - expect(matchGitArgs("origin a'b'c --set-upstream")).toStrictEqual([ + it('rejects a closing quote glued to following text (string-argv would split it)', () => { + expect(() => matchGitArgs("origin 'main'--force --set-upstream")).toThrow( + /quoted segment immediately followed by non-whitespace/, + ); + expect(() => matchGitArgs('origin "main"--force --set-upstream')).toThrow( + /quoted segment immediately followed by non-whitespace/, + ); + expect(() => matchGitArgs("origin ''--force")).toThrow( + /quoted segment immediately followed by non-whitespace/, + ); + expect(() => matchGitArgs('origin \'foo\'"--force"')).toThrow( + /quoted segment immediately followed by non-whitespace/, + ); + expect(() => matchGitArgs("origin a'b'c --set-upstream")).toThrow( + /quoted segment immediately followed by non-whitespace/, + ); + }); + + it('parses balanced quotes that wrap a whole argument', () => { + expect(matchGitArgs("origin 'main' --set-upstream")).toStrictEqual([ + 'origin', + 'main', + '--set-upstream', + ]); + expect(matchGitArgs("origin 'a b c' --set-upstream")).toStrictEqual([ 'origin', - "a'b'c", + 'a b c', '--set-upstream', ]); expect(matchGitArgs("--longOption 'hello world'")).toStrictEqual([ @@ -279,6 +302,9 @@ describe('matchGitArgs', () => { expect( matchGitArgs('--longOption \'This uses the "other" quotes\''), ).toStrictEqual(['--longOption', 'This uses the "other" quotes']); + expect(matchGitArgs("--message='hello'")).toStrictEqual([ + "--message='hello'", + ]); }); it('rejects -F / --file message-from-file flags (PoC form)', () => {