Read the release token from RELEASE_TOKEN
Publish plugin / publish (push) Successful in 3s

The publish step set GITEA_TOKEN from the org-level API_TOKEN secret, but
the Actions runner injects its own GITEA_TOKEN into every step and that
value overrides a step-level env: entry of the same name. The org secret
was silently ignored and the job failed.

Pass the secret as RELEASE_TOKEN instead, which the runner does not
reserve, and read that name in release.py. --token-env makes the source
configurable, so the runner-provided GITEA_TOKEN can still be used
deliberately by passing --token-env GITEA_TOKEN.
This commit is contained in:
lyuksovannyy committed 2026-09-13 00:10:01 +02:00
1 parent aec9f9376f
commit 85302e78f0
3 files changed
+27 -12

No files matched your search

+5 -5
View File
@@ -69,13 +69,13 @@ jobs:
fi fi
echo "tag=${tag}" >> "$GITHUB_OUTPUT" echo "tag=${tag}" >> "$GITHUB_OUTPUT"
echo "publishing as ${tag}" echo "publishing as ${tag}"
- name: Publish release and upload plugin - name: Publish release and upload plugin
env: env:
# Org-level secret holding a user token with write:repository scope. # Deliberately not named GITEA_TOKEN: the Actions runner injects its
# Not using a `||` fallback to GITEA_TOKEN: Gitea's expression engine # own GITEA_TOKEN into every step, and that value wins over a
# supports only always(), so the operator would not evaluate. # step-level `env:` of the same name, so the org secret would be
GITEA_TOKEN: ${{ secrets.API_TOKEN }} # silently ignored.
RELEASE_TOKEN: ${{ secrets.API_TOKEN }}
run: | run: |
python3 tools/release.py \ python3 tools/release.py \
--api "${{ github.server_url }}/api/v1/repos/${{ github.repository }}" \ --api "${{ github.server_url }}/api/v1/repos/${{ github.repository }}" \
+9 -4
View File
@@ -126,10 +126,15 @@ token with the `write:repository` scope (which covers releases). Set it once und
repo-level secret of the same name takes precedence if one repo ever needs different repo-level secret of the same name takes precedence if one repo ever needs different
credentials. credentials.
If you would rather not manage a token, Gitea also injects a per-job `secrets.GITEA_TOKEN` The workflow passes it to `release.py` as `RELEASE_TOKEN`, **not** `GITEA_TOKEN`. The Actions
that can create releases in its own repo — swap the `env:` line in the workflow and drop the runner injects its own `GITEA_TOKEN` into every step, and that value overrides a step-level
org secret. It is scoped to the single job and expires with it, so it is the safer default; `env:` entry of the same name, so the org secret would be silently ignored and the publish
`API_TOKEN` is what you want when publishing needs to reach beyond this repo. step would fail on permissions. Use `--token-env` if you need a different variable name.
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
scoped to the single job and expires with it, so it is the safer default; `API_TOKEN` is what
you want when publishing needs to reach beyond this repo.
## Writing your own plugin ## Writing your own plugin
+13 -3
View File
@@ -10,7 +10,9 @@ Stdlib only, so it runs on any runner image without a pip install step.
Usage: Usage:
tools/release.py --api <repo-api-url> --tag v1.0.0 --artifact dist/x.plugin tools/release.py --api <repo-api-url> --tag v1.0.0 --artifact dist/x.plugin
The token is read from GITEA_TOKEN (or GITHUB_TOKEN as a fallback). The token is read from RELEASE_TOKEN by default; override with --token-env.
Deliberately not GITEA_TOKEN: the Actions runner injects its own value for
that name into every step, which overrides a step-level `env:` entry.
""" """
from __future__ import annotations from __future__ import annotations
@@ -147,11 +149,19 @@ def main(argv: Optional[list] = None) -> int:
parser.add_argument("--artifact", required=True, help="file to attach") parser.add_argument("--artifact", required=True, help="file to attach")
parser.add_argument("--commitish", default="", help="target commit for a new tag") parser.add_argument("--commitish", default="", help="target commit for a new tag")
parser.add_argument("--asset-name", default="", help="attachment name; defaults to the file name") parser.add_argument("--asset-name", default="", help="attachment name; defaults to the file name")
parser.add_argument(
"--token-env",
default="RELEASE_TOKEN",
help="environment variable holding the API token (default: RELEASE_TOKEN)",
)
args = parser.parse_args(argv) args = parser.parse_args(argv)
token = os.environ.get("GITEA_TOKEN") or os.environ.get("GITHUB_TOKEN") or "" # 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, "")
if not token: if not token:
fail("GITEA_TOKEN is not set") fail(f"{args.token_env} is not set")
artifact = Path(args.artifact) artifact = Path(args.artifact)
if not artifact.is_file() or artifact.stat().st_size == 0: if not artifact.is_file() or artifact.stat().st_size == 0: