fix(ci): publish container under token account namespace
This commit is contained in:
1 parent
c389c3893d
commit
867113d5d4
5 files changed
+35
-6
No files matched your search
@@ -98,6 +98,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
REGISTRY_SERVER: ${{ gitea.server_url }}
|
REGISTRY_SERVER: ${{ gitea.server_url }}
|
||||||
REGISTRY_REPOSITORY: ${{ gitea.repository }}
|
REGISTRY_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
REGISTRY_NAMESPACE: ${{ vars.CONTAINER_REGISTRY_NAMESPACE }}
|
||||||
REGISTRY_USER: ${{ secrets.CONTAINER_REGISTRY_USER }}
|
REGISTRY_USER: ${{ secrets.CONTAINER_REGISTRY_USER }}
|
||||||
REGISTRY_TOKEN: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}
|
||||||
run: bash scripts/publish-container.sh
|
run: bash scripts/publish-container.sh
|
||||||
@@ -29,6 +29,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
REGISTRY_SERVER: ${{ gitea.server_url }}
|
REGISTRY_SERVER: ${{ gitea.server_url }}
|
||||||
REGISTRY_REPOSITORY: ${{ gitea.repository }}
|
REGISTRY_REPOSITORY: ${{ gitea.repository }}
|
||||||
|
REGISTRY_NAMESPACE: ${{ vars.CONTAINER_REGISTRY_NAMESPACE }}
|
||||||
REGISTRY_USER: ${{ secrets.CONTAINER_REGISTRY_USER }}
|
REGISTRY_USER: ${{ secrets.CONTAINER_REGISTRY_USER }}
|
||||||
REGISTRY_TOKEN: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.CONTAINER_REGISTRY_TOKEN }}
|
||||||
run: bash scripts/publish-container.sh
|
run: bash scripts/publish-container.sh
|
||||||
@@ -219,9 +219,19 @@ After changing `.env`, recreate the container; an image rebuild is not required.
|
|||||||
|
|
||||||
To publish from Gitea:
|
To publish from Gitea:
|
||||||
|
|
||||||
|
Gitea packages belong to an account or organization, independently of repository
|
||||||
|
permissions. Publication defaults to the lowercase `CONTAINER_REGISTRY_USER`
|
||||||
|
namespace, so a Simo token publishes `simo/epicnext-cms` even though the Git
|
||||||
|
repository belongs to remco. Set the Actions variable
|
||||||
|
`CONTAINER_REGISTRY_NAMESPACE` only to override this (for example, an organization
|
||||||
|
where the token account has package write access). Keep the login username and
|
||||||
|
token from the same account. Changing namespace also changes the image URL used
|
||||||
|
by installations; existing remco image tags are not moved automatically.
|
||||||
|
|
||||||
1. In the repository's Actions secrets, configure `CONTAINER_REGISTRY_USER` and
|
1. In the repository's Actions secrets, configure `CONTAINER_REGISTRY_USER` and
|
||||||
`CONTAINER_REGISTRY_TOKEN`. Use a Gitea access token with package read/write
|
`CONTAINER_REGISTRY_TOKEN`. Use a Gitea access token with package read/write
|
||||||
permission belonging to an account allowed to publish under the repository owner.
|
permission belonging to the login account. For the Simo token, set
|
||||||
|
`CONTAINER_REGISTRY_USER=Simo`; the default package namespace will be `simo`.
|
||||||
2. Every push to `main` or `master` automatically builds and publishes the images
|
2. Every push to `main` or `master` automatically builds and publishes the images
|
||||||
after the CI checks and production deployment succeed. Pull requests do not
|
after the CI checks and production deployment succeed. Pull requests do not
|
||||||
publish images. The publication job builds from committed source only and checks
|
publish images. The publication job builds from committed source only and checks
|
||||||
@@ -235,12 +245,12 @@ To publish from Gitea:
|
|||||||
migrations image is used temporarily for the matching database migrations.
|
migrations image is used temporarily for the matching database migrations.
|
||||||
|
|
||||||
For this repository the image base is
|
For this repository the image base is
|
||||||
`gitlab.epicnabbo.nl/remco/epicnext-cms`. Package access is controlled by Gitea.
|
`gitlab.epicnabbo.nl/simo/epicnext-cms`. Package access is controlled by Gitea.
|
||||||
For private packages, run `docker login gitlab.epicnabbo.nl` on the installation
|
For private packages, run `docker login gitlab.epicnabbo.nl` on the installation
|
||||||
with a token that can read packages. Then update with:
|
with a token that can read packages. Then update with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
CMS_IMAGE_REPOSITORY=gitlab.epicnabbo.nl/remco/epicnext-cms \
|
CMS_IMAGE_REPOSITORY=gitlab.epicnabbo.nl/simo/epicnext-cms \
|
||||||
CMS_PUBLIC_URL=https://your-hotel.example \
|
CMS_PUBLIC_URL=https://your-hotel.example \
|
||||||
bash scripts/docker-update.sh
|
bash scripts/docker-update.sh
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ registry="${registry%/}"
|
|||||||
[[ "$REGISTRY_SERVER" = https://* && "$registry" != */* ]] || { echo "Registry must use HTTPS at the Gitea server root" >&2; exit 1; }
|
[[ "$REGISTRY_SERVER" = https://* && "$registry" != */* ]] || { echo "Registry must use HTTPS at the Gitea server root" >&2; exit 1; }
|
||||||
repository="${REGISTRY_REPOSITORY,,}"
|
repository="${REGISTRY_REPOSITORY,,}"
|
||||||
[[ "$repository" =~ ^[a-z0-9._-]+/[a-z0-9._-]+$ ]] || exit 1
|
[[ "$repository" =~ ^[a-z0-9._-]+/[a-z0-9._-]+$ ]] || exit 1
|
||||||
|
# Gitea packages belong to a user/organization, independently of repository ACLs.
|
||||||
|
# A collaborator token cannot publish to another user's personal namespace.
|
||||||
|
namespace="${REGISTRY_NAMESPACE:-$REGISTRY_USER}"
|
||||||
|
namespace="${namespace,,}"
|
||||||
|
[[ "$namespace" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || { echo "Invalid registry namespace; use a Gitea username or organization" >&2; exit 1; }
|
||||||
|
repository="$namespace/${repository#*/}"
|
||||||
image="$registry/$repository:$sha"
|
image="$registry/$repository:$sha"
|
||||||
# Isolate credentials from the self-hosted runner's normal Docker configuration.
|
# Isolate credentials from the self-hosted runner's normal Docker configuration.
|
||||||
export DOCKER_CONFIG
|
export DOCKER_CONFIG
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const bash =
|
|||||||
.find((path) => existsSync(path)) ?? "bash")
|
.find((path) => existsSync(path)) ?? "bash")
|
||||||
: "bash";
|
: "bash";
|
||||||
const sha = "a".repeat(40);
|
const sha = "a".repeat(40);
|
||||||
function simulate(scenario: string) {
|
function simulate(scenario: string, namespace = "") {
|
||||||
const dir = mkdtempSync(join(tmpdir(), "cms-publish-test-"));
|
const dir = mkdtempSync(join(tmpdir(), "cms-publish-test-"));
|
||||||
try {
|
try {
|
||||||
const result = spawnSync(
|
const result = spawnSync(
|
||||||
@@ -35,7 +35,8 @@ function simulate(scenario: string) {
|
|||||||
SCENARIO: scenario,
|
SCENARIO: scenario,
|
||||||
REGISTRY_SERVER: "https://registry.invalid",
|
REGISTRY_SERVER: "https://registry.invalid",
|
||||||
REGISTRY_REPOSITORY: "owner/cms",
|
REGISTRY_REPOSITORY: "owner/cms",
|
||||||
REGISTRY_USER: "fixture",
|
REGISTRY_USER: "Simo",
|
||||||
|
REGISTRY_NAMESPACE: namespace,
|
||||||
REGISTRY_TOKEN: "fixture-only",
|
REGISTRY_TOKEN: "fixture-only",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -51,7 +52,7 @@ describe("verified application image reuse", () => {
|
|||||||
it("reuses only the exact image digest that passed deployment checks", () => {
|
it("reuses only the exact image digest that passed deployment checks", () => {
|
||||||
const calls = simulate("verified");
|
const calls = simulate("verified");
|
||||||
expect(calls).toContain(
|
expect(calls).toContain(
|
||||||
`docker tag sha256:candidate registry.invalid/owner/cms:${sha}`,
|
`docker tag sha256:candidate registry.invalid/simo/cms:${sha}`,
|
||||||
);
|
);
|
||||||
expect(calls).not.toContain("docker build --network=host --build-arg");
|
expect(calls).not.toContain("docker build --network=host --build-arg");
|
||||||
expect(
|
expect(
|
||||||
@@ -67,3 +68,13 @@ describe("verified application image reuse", () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the token account namespace instead of the repository owner", () => {
|
||||||
|
const calls = simulate("verified");
|
||||||
|
expect(calls).toContain(`docker push registry.invalid/simo/cms:${sha}`);
|
||||||
|
expect(calls).not.toContain("registry.invalid/owner/cms");
|
||||||
|
});
|
||||||
|
it("supports an explicit organization namespace", () => {
|
||||||
|
const calls = simulate("verified", "My-Org");
|
||||||
|
expect(calls).toContain(`docker push registry.invalid/my-org/cms:${sha}`);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user