From 1ef4e61334e4ffca6339d58b4706b5ceb67997a7 Mon Sep 17 00:00:00 2001 From: lyuksovannyy Date: Sun, 13 Sep 2026 00:20:58 +0200 Subject: [PATCH] Fall back to the runner token when the org secret is absent pluginBase failed with "RELEASE_TOKEN is not set" while chatSummaries published fine, from the same organisation secret. An organisation secret is only exposed to a repository that has Actions enabled, so it can be empty in one repo and present in another. Try RELEASE_TOKEN first, then the runner-provided GITEA_TOKEN, which can publish releases in its own repository, and report which one was used. When none is set, name every variable that was tried and point at the repository Actions setting rather than failing with a bare message. The publish step also reports whether the organisation secret arrived, printing only its length, so the fallback is visible in the job log. --- .gitea/workflows/release.yml | 18 ++++++++++++++---- README.md | 9 +++++++-- tools/release.py | 31 +++++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 92ab75b..46e90b6 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -71,12 +71,22 @@ jobs: echo "publishing as ${tag}" - name: Publish release and upload plugin env: - # Deliberately not named GITEA_TOKEN: the Actions runner injects its - # own GITEA_TOKEN into every step, and that value wins over a - # step-level `env:` of the same name, so the org secret would be - # silently ignored. + # Passed under a name the runner does not reserve. A step-level + # GITEA_TOKEN is overridden by the runner's own job token, so the + # org secret would be silently ignored under that name. + # + # RELEASE_TOKEN is the org-wide API_TOKEN secret. If it is empty -- + # which happens when Actions is disabled on the repository, so org + # secrets are never exposed to it -- release.py falls back to the + # runner's GITEA_TOKEN, which can still publish to its own repo. RELEASE_TOKEN: ${{ secrets.API_TOKEN }} run: | + set -eu + if [ -n "${RELEASE_TOKEN:-}" ]; then + echo "org secret API_TOKEN is present (${#RELEASE_TOKEN} chars)" + else + echo "org secret API_TOKEN is empty; falling back to the runner token" + fi python3 tools/release.py \ --api "${{ github.server_url }}/api/v1/repos/${{ github.repository }}" \ --tag "${{ steps.tag.outputs.tag }}" \ diff --git a/README.md b/README.md index cf2006c..1c35ab1 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,13 @@ credentials. The workflow passes it to `release.py` as `RELEASE_TOKEN`, **not** `GITEA_TOKEN`. The Actions runner injects its own `GITEA_TOKEN` into every step, and that value overrides a step-level -`env:` entry of the same name, so the org secret would be silently ignored and the publish -step would fail on permissions. Use `--token-env` if you need a different variable name. +`env:` entry of the same name, so the org secret would be silently ignored. + +If the org secret does not reach a repository the publish still works: `release.py` tries +`RELEASE_TOKEN` first and then falls back to the runner's `GITEA_TOKEN`, which can publish +releases in its own repository, and prints which one it used. An organisation secret is only +exposed to a repository that has Actions enabled, so check **Repo → Settings → Actions** if a +repo unexpectedly falls back. If you would rather not manage a token at all, that injected `GITEA_TOKEN` can create releases in its own repo: pass `--token-env GITEA_TOKEN` and drop the org secret. It is diff --git a/tools/release.py b/tools/release.py index c8f8365..2d11a08 100755 --- a/tools/release.py +++ b/tools/release.py @@ -152,16 +152,35 @@ def main(argv: Optional[list] = None) -> int: parser.add_argument( "--token-env", default="RELEASE_TOKEN", - help="environment variable holding the API token (default: RELEASE_TOKEN)", + help="preferred environment variable holding the API token", ) args = parser.parse_args(argv) - # Read from an explicitly named variable rather than GITEA_TOKEN: the - # Actions runner injects its own GITEA_TOKEN into every step, which - # silently wins over a step-level `env:` of the same name. - token = os.environ.get(args.token_env, "") + # Preferred name first, then the runner-provided job token. GITEA_TOKEN is + # not used as the primary name because the Actions runner injects its own + # value for it into every step, which overrides a step-level `env:` entry + # of the same name -- but it is a valid last resort, since it can publish + # releases in its own repository. + candidates = [args.token_env, "RELEASE_TOKEN", "GITEA_TOKEN", "GITHUB_TOKEN"] + token, token_source = "", "" + for name in candidates: + value = os.environ.get(name, "").strip() + if value: + token, token_source = value, name + break if not token: - fail(f"{args.token_env} is not set") + present = sorted(n for n in candidates if n in os.environ) + fail( + f"no API token found; looked at {', '.join(dict.fromkeys(candidates))}. " + + ( + f"These exist but are empty: {', '.join(present)}. " + "An organisation secret is only exposed to a repository when Actions " + "is enabled on that repository, so check Repo > Settings > Actions." + if present + else "None of them are set in the environment." + ) + ) + print(f"using token from {token_source}") artifact = Path(args.artifact) if not artifact.is_file() or artifact.stat().st_size == 0: