diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1c5edf..50827c6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,6 +33,12 @@ jobs: a = yaml.safe_load(open('action.yml')) assert a['runs']['using'] == 'composite', 'action must be composite' assert a['inputs']['api-token']['required'] is True, 'api-token must be required' + # ci18-7277 — api-token is the ONLY required input: with nothing else + # set, the action runs `ptc init` itself and uses what it detects. + for optional in ('config-file', 'source-locale', 'patterns'): + assert a['inputs'][optional].get('required', False) is False, f'{optional} must stay optional' + run = a['runs']['steps'][0]['run'] + assert 'init --yes' in run, 'the action must be able to detect the layout itself' print('action.yml OK:', a['name']) PY diff --git a/README.md b/README.md index cf8f391..81769e0 100644 --- a/README.md +++ b/README.md @@ -69,9 +69,9 @@ The component only runs on a push to your default branch and skips translation c | Input | Required | Default | Description | |---|---|---|---| | `api-token` | ✅ | — | PTC project token. Passed via the `PTC_API_TOKEN` env var, never argv. | -| `config-file` | | `''` | Path to `.ptc-config.yml`. Takes precedence over `source-locale`/`patterns`. | -| `source-locale` | | `''` | Source language code (with `patterns`). | -| `patterns` | | `''` | Glob(s) with a `{{lang}}` slot. | +| `config-file` | | `''` | Path to `.ptc-config.yml`. Optional: a `.ptc-config.yml` committed at the repo root is used on its own, and with no config at all the action detects the layout itself. Takes precedence over `source-locale`/`patterns`. | +| `source-locale` | | `''` | Source language code. Only to override detection (with `patterns`). | +| `patterns` | | `''` | Glob(s) with a `{{lang}}` slot. Only to override detection. | | `file-tag-name` | | auto | PTC file tag (defaults to the git branch). | | `api-url` | | `https://app.ptc.wpml.org/api/v1/` | Override for staging / self-hosted. | | `project-dir` | | `.` | Directory treated as project root. | diff --git a/action.yml b/action.yml index 27e4265..c064df1 100644 --- a/action.yml +++ b/action.yml @@ -10,15 +10,15 @@ inputs: description: 'PTC project API token. Pass the secrets.PTC_API_TOKEN value. Sent to PTC via the PTC_API_TOKEN env var only — never on the command line.' required: true config-file: - description: 'Path to .ptc-config.yml (recommended — generate it once with `ptc init`). Takes precedence over source-locale/patterns.' + description: 'Path to .ptc-config.yml. Optional — a .ptc-config.yml committed at the repository root is picked up on its own, and with no config at all the action detects the layout itself (see below). Takes precedence over source-locale/patterns.' required: false default: '' source-locale: - description: 'Source language code (e.g. "en"). Use together with `patterns` when you are not using a config file.' + description: 'Source language code (e.g. "en"). Only needed to override detection; use together with `patterns`.' required: false default: '' patterns: - description: 'Comma-separated glob(s) where {{lang}} marks the language slot, e.g. "src/locales/{{lang}}.json".' + description: 'Comma-separated glob(s) where {{lang}} marks the language slot, e.g. "src/locales/{{lang}}.json". Only needed to override detection.' required: false default: '' file-tag-name: @@ -65,15 +65,6 @@ outputs: runs: using: 'composite' steps: - - name: Validate inputs - shell: bash - run: | - set -euo pipefail - if [ -z "${{ inputs.config-file }}" ] && { [ -z "${{ inputs.source-locale }}" ] || [ -z "${{ inputs.patterns }}" ]; }; then - echo "::error::Provide either 'config-file' OR both 'source-locale' and 'patterns'." - exit 1 - fi - - name: Translate with PTC id: ptc shell: bash @@ -83,12 +74,54 @@ runs: run: | set -euo pipefail echo "::add-mask::${PTC_API_TOKEN}" - CLI="${{ github.action_path }}/ptc-cli.sh" # vendored + pinned at v1.0.0, never curled at runtime + CLI="${{ github.action_path }}/ptc-cli.sh" # vendored + pinned, never curled at runtime chmod +x "$CLI" + + # ci18-7277 — api-token is the only required input. Resolution order, + # first match wins: + # + # 1. config-file — an explicit path the caller passed + # 2. .ptc-config.yml — one committed to the repository + # 3. source-locale + patterns — the caller spelled it out inline + # 4. nothing — run `ptc init` right here and use what it writes + # + # (4) is why the action exists in this shape: the CLI is already + # vendored inside it, and `ptc init` needs no token (it asks the public + # detect_config endpoint), so the action can work out the project's + # layout itself. Before this, a caller who had not run `ptc init` + # locally had to hand-write source-locale + patterns, which is exactly + # the copy-paste step the whole on-ramp was meant to remove. + CONFIG="${{ inputs.config-file }}" + SOURCE_LOCALE="${{ inputs.source-locale }}" + PATTERNS="${{ inputs.patterns }}" + + if [ -z "$CONFIG" ] && [ -f .ptc-config.yml ]; then + CONFIG=".ptc-config.yml" + echo "Using the .ptc-config.yml committed to this repository." + fi + + if [ -z "$CONFIG" ] && { [ -z "$SOURCE_LOCALE" ] || [ -z "$PATTERNS" ]; }; then + echo "No config and no source-locale/patterns — detecting the layout with \`ptc init\`." + if ! "$CLI" init --yes --api-url "${{ inputs.api-url }}"; then + echo "::error::Could not detect this project's translatable files. Pass config-file, or source-locale + patterns." + exit 1 + fi + CONFIG=".ptc-config.yml" + # A layout the classifier does not recognise still writes a config — + # a commented template with no files: block. Fail here, with the + # reason, rather than let the CLI stop on a config it cannot use. + if ! grep -qE '^files:' .ptc-config.yml; then + echo "::error::\`ptc init\` could not identify translatable files here. Pass config-file, or source-locale + patterns." + exit 1 + fi + echo "Detected layout:" + sed 's/^/ /' .ptc-config.yml + fi + args=( --api-url "${{ inputs.api-url }}" ) - [ -n "${{ inputs.config-file }}" ] && args+=( --config-file "${{ inputs.config-file }}" ) - [ -n "${{ inputs.source-locale }}" ] && args+=( --source-locale "${{ inputs.source-locale }}" ) - [ -n "${{ inputs.patterns }}" ] && args+=( --patterns "${{ inputs.patterns }}" ) + [ -n "$CONFIG" ] && args+=( --config-file "$CONFIG" ) + [ -n "$SOURCE_LOCALE" ] && [ -z "$CONFIG" ] && args+=( --source-locale "$SOURCE_LOCALE" ) + [ -n "$PATTERNS" ] && [ -z "$CONFIG" ] && args+=( --patterns "$PATTERNS" ) [ -n "${{ inputs.file-tag-name }}" ] && args+=( --file-tag-name "${{ inputs.file-tag-name }}" ) [ -n "${{ inputs.monitor-interval }}" ] && args+=( --monitor-interval "${{ inputs.monitor-interval }}" ) [ -n "${{ inputs.monitor-max-attempts }}" ] && args+=( --monitor-max-attempts "${{ inputs.monitor-max-attempts }}" )