{"id":16772585,"url":"https://github.com/jonlabelle/replace-tokens-action","last_synced_at":"2026-04-01T20:25:38.138Z","repository":{"id":240220333,"uuid":"801842596","full_name":"jonlabelle/replace-tokens-action","owner":"jonlabelle","description":"A GitHub action to replace tokens in a file, similar to envsubst","archived":false,"fork":false,"pushed_at":"2026-03-30T00:07:36.000Z","size":281,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-30T01:38:54.854Z","etag":null,"topics":["actions","envsubst","replace-tokens","tokens"],"latest_commit_sha":null,"homepage":"","language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jonlabelle.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-05-17T02:49:14.000Z","updated_at":"2026-03-30T00:07:12.000Z","dependencies_parsed_at":"2026-03-16T20:04:14.445Z","dependency_job_id":null,"html_url":"https://github.com/jonlabelle/replace-tokens-action","commit_stats":null,"previous_names":["jonlabelle/replace-tokens-action"],"tags_count":62,"template":false,"template_full_name":null,"purl":"pkg:github/jonlabelle/replace-tokens-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonlabelle%2Freplace-tokens-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonlabelle%2Freplace-tokens-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonlabelle%2Freplace-tokens-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonlabelle%2Freplace-tokens-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jonlabelle","download_url":"https://codeload.github.com/jonlabelle/replace-tokens-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jonlabelle%2Freplace-tokens-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291581,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions","envsubst","replace-tokens","tokens"],"created_at":"2024-10-13T06:43:17.001Z","updated_at":"2026-04-01T20:25:38.130Z","avatar_url":"https://github.com/jonlabelle.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Replace Tokens Action\n\n[![ci](https://github.com/jonlabelle/replace-tokens-action/actions/workflows/ci.yml/badge.svg)](https://github.com/jonlabelle/replace-tokens-action/actions/workflows/ci.yml)\n[![latest release](https://img.shields.io/github/v/tag/jonlabelle/replace-tokens-action.svg?label=version\u0026sort=semver)](https://github.com/jonlabelle/replace-tokens-action/releases)\n\n\u003e A GitHub Action that replaces tokens in files, similar to envsubst.\n\n## Table of contents\n\n- [Usage](#usage)\n- [Outputs](#outputs)\n- [Platform support](#platform-support)\n- [Examples](#examples)\n  - [Replace tokens in a file](#replace-tokens-in-a-file)\n  - [Using a path filter](#using-a-path-filter)\n  - [Search multiple paths](#search-multiple-paths)\n  - [Case-insensitive environment variable matching](#case-insensitive-environment-variable-matching)\n  - [Replace other token styles](#replace-other-token-styles)\n  - [Search paths recursively](#search-paths-recursively)\n  - [Replace an API key and URL in .env files](#replace-an-api-key-and-url-in-env-files)\n  - [Exclude items and patterns](#exclude-items-and-patterns)\n  - [Preview changes with dry-run](#preview-changes-with-dry-run)\n  - [Fail on no-op](#fail-on-no-op)\n  - [Fail on unresolved tokens](#fail-on-unresolved-tokens)\n  - [Custom file encoding](#custom-file-encoding)\n  - [No newline at EOF](#no-newline-at-eof)\n- [Token style](#token-style)\n- [File encoding](#file-encoding)\n- [License](#license)\n\n## Usage\n\nSee [action.yml](action.yml).\n\n| name               | description                      | type    | required | default    | example       |\n| ------------------ | -------------------------------- | ------- | -------- | ---------- | ------------- |\n| `paths`            | File paths to process [^1]       | string  | false    | `.`        | `./prod.json` |\n| `style`            | [Token style](#token-style)      | string  | false    | `mustache` | `envsubst`    |\n| `filter`           | Filename filter [^2]             | string  | false    | none       | `*.json`      |\n| `exclude`          | Exclude patterns [^3]            | string  | false    | none       | `*dev*.json`  |\n| `recurse`          | Search subdirectories            | boolean | false    | `false`    | `true`        |\n| `depth`            | Recursion depth (`0` = no limit) | number  | false    | `0`        | `2`           |\n| `dry-run`          | Preview without modifying files  | boolean | false    | `false`    | `true`        |\n| `fail`             | Fail if nothing changes [^4]     | boolean | false    | `false`    | `true`        |\n| `fail-on-skipped`  | Fail if any token is unresolved  | boolean | false    | `false`    | `true`        |\n| `case-insensitive` | Ignore env variable casing       | boolean | false    | `false`    | `true`        |\n| `encoding`         | [File encoding](#file-encoding)  | string  | false    | `auto`     | `unicode`     |\n| `no-newline`       | Skip the final newline           | boolean | false    | `false`    | `true`        |\n| `verbose`          | Enable verbose logging           | boolean | false    | `false`    | `true`        |\n\n\u003e [!Note]\n\u003e When `recurse` is enabled, symbolic links are followed automatically on PowerShell Core 6+. On Windows PowerShell 5.1, symbolic links are not followed because the underlying `Get-ChildItem -FollowSymlink` parameter is not available.\n\n## Outputs\n\n- `tokens-replaced`: Total number of tokens replaced, or that would be replaced in dry-run mode.\n- `tokens-skipped`: Total number of tokens skipped because no matching value was available.\n- `modified-files-count`: Number of files updated by the action.\n- `would-modify-files-count`: Number of files that would be updated in dry-run mode.\n\n## Platform support\n\n- GitHub-hosted runners execute the composite action with PowerShell Core (`pwsh`).\n- On self-hosted Windows runners, GitHub Actions falls back to Windows PowerShell when `pwsh` is not installed, so the action remains usable in both environments.\n- Environment variable name matching follows platform conventions by default: case-insensitive on Windows, case-sensitive on Linux and macOS.\n- Set `case-insensitive: true` to use case-insensitive environment variable name matching on any runner.\n- The `Expand-TemplateFile.ps1` script remains compatible with Windows PowerShell 5.1 and PowerShell Core 6+ for self-hosted fallback and direct script usage.\n\n## Examples\n\n### Replace tokens in a file\n\nReplace a **mustache** token such as `{{ NAME }}` in `./path/to/template.json`\nwith the value of the `NAME` environment variable.\n\n```yaml\nsteps:\n  - name: Replace tokens in the specified path\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/template.json\n    env:\n      NAME: jon\n```\n\n\u003e [!NOTE]  \n\u003e Environment variable names are matched case-insensitively on Windows, and case-sensitively on Linux and macOS by default. Set `case-insensitive: true` to opt into case-insensitive matching everywhere.\n\n### Case-insensitive environment variable matching\n\nUse `case-insensitive: true` when token casing does not need to match the environment variable name exactly.\n\n```yaml\nsteps:\n  - name: Replace tokens with case-insensitive environment variable matching\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/template.json\n      case-insensitive: true\n    env:\n      NAME: jon\n```\n\n### Using a path filter\n\nSearch `./path/to/search` and replace tokens in all `*.json` files.\n\n```yaml\nsteps:\n  - name: Replace tokens using a path filter\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n    env:\n      NAME: jon\n```\n\n### Search multiple paths\n\nSearch three paths and replace matching tokens in `*.json` files.\n\n```yaml\nsteps:\n  - name: Search multiple paths\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: |\n        ./first/path\n        ./second/path\n        ./third/path\n      filter: '*.json'\n    env:\n      NAME: jon\n```\n\n### Replace other token styles\n\nReplace tokens using the **handlebars** style, for example `{{VARIABLE}}`.\n\n```yaml\nsteps:\n  - name: Replace handlebars styled tokens\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      style: handlebars\n    env:\n      NAME: jon\n```\n\nReplace tokens using the **envsubst** style, for example `${VARIABLE}`.\n\n```yaml\nsteps:\n  - name: Replace envsubst styled tokens\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      style: envsubst\n    env:\n      NAME: jon\n```\n\nReplace tokens using the **brackets** style, for example `\u003cVARIABLE\u003e`.\n\n```yaml\nsteps:\n  - name: Replace brackets styled tokens\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      style: brackets\n    env:\n      NAME: jon\n```\n\n\u003e [!WARNING]  \n\u003e The `brackets` style (`\u003cVARIABLE\u003e`) can collide with HTML and XML tags. If an environment variable name matches a tag name (e.g. `div`, `span`), those tags will be replaced unintentionally. Avoid using this style on HTML/XML files, or use the `filter`/`exclude` inputs to restrict processing to non-markup files.\n\nReplace tokens using the **hashes** style, for example `##VARIABLE##`.\n\n```yaml\nsteps:\n  - name: Replace hashes styled tokens\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      style: hashes\n    env:\n      NAME: jon\n```\n\nReplace tokens using the **underscores** style, for example `__VARIABLE__`.\n\n```yaml\nsteps:\n  - name: Replace underscore styled tokens\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      style: underscores\n    env:\n      NAME: jon\n```\n\nReplace tokens using the **make** style, for example `$(VARIABLE)`.\n\n```yaml\nsteps:\n  - name: Replace make styled tokens\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      style: make\n    env:\n      NAME: jon\n```\n\n### Search paths recursively\n\nSearch the specified paths recursively, up to two directories deep, and replace matching tokens.\n\n```yaml\nsteps:\n  - name: Replace tokens using recursion, 2 directories deep\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: |\n        ./first/path/to/search\n        ./second/path/to/search\n      filter: '*.json'\n      recurse: true\n      depth: 2\n    env:\n      NAME: jon\n```\n\n### Replace an API key and URL in .env files\n\nReplace `API_URL` and `API_KEY` tokens in `.env` files.\n\n```yaml\nsteps:\n  - name: Replace an API key and URL in .env files\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      # matches: `./src/.env` and `./src/.env.production`\n      paths: ./src\n      filter: '*.env*'\n    env:\n      API_KEY: ${{ secrets.api-key }}\n      API_URL: https://example.net/api\n```\n\n### Exclude items and patterns\n\nExclude file or directory patterns from the search results.\n\n```yaml\nsteps:\n  - name: Exclude certain items or patterns from results\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      # matches: `./src/.env.local` and `./src/.env.production`,\n      # but not `./src/.env` or `./src/.env.development`\n      paths: ./src\n      filter: '*.env*'\n      exclude: |\n        .env\n        .env.development\n    env:\n      API_KEY: ${{ secrets.api-key }}\n      API_URL: https://example.net/api\n```\n\n### Preview changes with dry-run\n\nPreview the changes without modifying any files.\n\n```yaml\nsteps:\n  - name: Preview token replacement without modifying files\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      dry-run: true\n    env:\n      NAME: jon\n```\n\n\u003e [!TIP]\n\u003e Use `dry-run` to preview changes before applying them in production. This is especially useful when testing token configurations. When `dry-run: true` and `fail: true` are used together, the action fails only if no files would change.\n\n### Fail on no-op\n\nFail the step if no files were changed.\n\n```yaml\nsteps:\n  - name: Fail if no tokens were replaced\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      fail: true\n    env:\n      NAME: jon\n```\n\n\u003e [!NOTE]  \n\u003e A warning is written to the log when a token does not have a matching environment variable.\n\n### Fail on unresolved tokens\n\nFail the step if one or more tokens are skipped because a matching environment variable is missing or empty.\n\n```yaml\nsteps:\n  - name: Fail when a token cannot be resolved\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      fail-on-skipped: true\n    env:\n      NAME: jon\n```\n\n### Custom file encoding\n\nSpecify the encoding used for file reads and writes.\n\nBy default, the action uses `auto`, which sniffs a BOM with a minimal read, applies lightweight UTF-16/UTF-8 heuristics when no BOM is present, and then writes the file back using the detected encoding.\n\n```yaml\nsteps:\n  - name: Set a non-default encoding option for reading/writing files\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      encoding: unicode\n    env:\n      NAME: jon\n```\n\n### No newline at EOF\n\nDo not append a trailing newline after token replacement.\n\nBy default, the action preserves an existing trailing newline, avoids appending a duplicate newline when the file already ends with one, and reuses the file's detected line ending style when a trailing newline needs to be added. If no existing line ending can be inferred, it falls back to the runner environment newline.\n\n```yaml\nsteps:\n  - name: Don't insert a newline at the end of the file\n    uses: jonlabelle/replace-tokens-action@v1\n    with:\n      paths: ./path/to/search\n      filter: '*.json'\n      no-newline: true\n    env:\n      NAME: jon\n```\n\n## Token style\n\nUse one of the following token formats:\n\n| name                 | style            | examples                   |\n| -------------------- | ---------------- | -------------------------- |\n| `mustache` (default) | `{{ VARIABLE }}` | `{{TOKEN}}`, `{{ TOKEN }}` |\n| `handlebars`         | `{{ VARIABLE }}` | `{{TOKEN}}`, `{{ TOKEN }}` |\n| `brackets`           | `\u003c VARIABLE \u003e`   | `\u003cTOKEN\u003e`, `\u003c TOKEN \u003e`     |\n| `hashes`             | `## VARIABLE ##` | `##TOKEN##`, `## TOKEN ##` |\n| `underscores`        | `__ VARIABLE __` | `__TOKEN__`, `__ TOKEN __` |\n| `envsubst`           | `${VARIABLE}`    | `${TOKEN}`                 |\n| `make`               | `$(VARIABLE)`    | `$(TOKEN)`                 |\n\nIf an environment variable named `VARIABLE` exists, its value is used for replacement. This behavior is similar to [envsubst\\(1\\)](https://www.gnu.org/software/gettext/manual/html_node/envsubst-Invocation.html).\n\n## File encoding\n\nThe default encoding for file reads and writes is `auto`. In auto mode, the action first checks for a BOM, then uses lightweight heuristics for common no-BOM UTF-16 and UTF-8 files, and finally falls back to the Windows ANSI code page on Windows or UTF-8 without BOM on Linux and macOS. The following explicit `encoding` values are also supported.\n\n- `auto`: Detects the existing file encoding and writes the updated file back using that encoding when possible\n- `utf8`: Encodes as UTF-8 without a byte order mark (BOM)\n- `utf8BOM`: Encodes as UTF-8 with a byte order mark (BOM)\n- `ascii`: Uses the ASCII (7-bit) character set\n- `ansi`: Uses the current culture's ANSI code page\n- `bigendianunicode`: Encodes as UTF-16 using big-endian byte order\n- `bigendianutf32`: Encodes as UTF-32 using big-endian byte order\n- `oem`: Uses the default encoding for MS-DOS and console programs\n- `unicode`: Encodes as UTF-16 using little-endian byte order\n- `utf32`: Encodes as UTF-32\n\nOn Windows PowerShell 5.1, the action uses explicit .NET encodings internally so `auto`, `utf8`, and `utf8BOM` behave consistently with PowerShell Core while still preserving Windows ANSI fallback behavior when auto-detecting no-BOM files.\n\n## License\n\n[MIT](LICENSE)\n\n[^1]: One or more file or directory paths. Wildcards are supported. If omitted, the action defaults to the current directory (`.`). Specify multiple paths on separate lines using a multiline string `|`.\n\n[^2]: `filter` only supports `*` and `?` wildcards.\n\n[^3]: One or more names or patterns to exclude from the results. Wildcards are supported. Specify multiple exclusions on separate lines using a multiline string `|`. See Microsoft's [Get-ChildItem -Exclude](https://learn.microsoft.com/powershell/module/microsoft.powershell.management/get-childitem#-exclude) documentation for more information.\n\n[^4]: When `dry-run` is enabled, `fail` checks whether any files would change instead of whether any files were written.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonlabelle%2Freplace-tokens-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjonlabelle%2Freplace-tokens-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjonlabelle%2Freplace-tokens-action/lists"}