From 7341dee951ade9d79aa349afde4edf90e4279d99 Mon Sep 17 00:00:00 2001 From: darrenoh <2293701+darrenoh@users.noreply.github.com> Date: Wed, 11 Nov 2026 14:19:06 +0000 Subject: [PATCH 1/5] Build DevPanel CLI using upstream go-build target Resolve the upstream default branch HEAD, create bin/, and use make go-build to build main.go on both native runners. Scope revision caches to the destination branch and keep upstream credentials out of global Git configuration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/update-devpanel-cli.yml | 110 +++++++--------------- AGENTS.md | 2 +- README.md | 21 +++-- TODO.md | 7 +- 4 files changed, 52 insertions(+), 88 deletions(-) diff --git a/.github/workflows/update-devpanel-cli.yml b/.github/workflows/update-devpanel-cli.yml index 85c907b2..deffd0c7 100644 --- a/.github/workflows/update-devpanel-cli.yml +++ b/.github/workflows/update-devpanel-cli.yml @@ -1,13 +1,13 @@ name: Update DevPanel CLI binaries # Build linux/amd64 and linux/arm64 devpanel binaries from -# https://github.com/devpanel/cli/tree/HEAD/cmd/devpanel-client and commit +# https://github.com/devpanel/cli using its go-build Make target and commit # base/bin/devpanel-amd64 and base/bin/devpanel-arm64. # -# Run it manually. The Actions cache records the latest commit that changed -# cmd/devpanel-client. Delete that cache entry to rebuild the same commit. +# Run it manually on a branch. The Actions cache records the upstream default +# branch HEAD after both binaries are committed to this branch. Delete that +# cache entry to rebuild the same commit. # -# The plan job reads the latest cmd/devpanel-client commit from GitHub GraphQL. # The build clones devpanel/cli under RUNNER_TEMP, outside this repository. # GHCR_TOKEN must be able to read that repository. GHCR_USER is the username # that owns the token. The workflow token commits the binaries. @@ -21,7 +21,8 @@ concurrency: jobs: plan: - name: Check cached devpanel-client revision + name: Check cached DevPanel CLI revision + if: github.ref_type == 'branch' runs-on: ubuntu-26.04 permissions: actions: read @@ -30,11 +31,14 @@ jobs: sha: ${{ steps.upstream.outputs.sha }} changed: ${{ steps.decision.outputs.changed }} steps: - - name: Require GHCR credentials + - name: Resolve upstream default branch HEAD + id: upstream env: GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} GHCR_USER: ${{ vars.GHCR_USER }} + GIT_TERMINAL_PROMPT: "0" run: | + set -euo pipefail if [[ -z "${GHCR_TOKEN}" ]]; then echo "::error::GHCR_TOKEN is required to read devpanel/cli." exit 1 @@ -44,83 +48,38 @@ jobs: exit 1 fi - - name: Resolve latest cmd/devpanel-client commit - id: upstream - env: - GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} - run: | - set -euo pipefail - query='query { - repository(owner: "devpanel", name: "cli") { - defaultBranchRef { - target { - ... on Commit { - history(first: 1, path: "cmd/devpanel-client") { - nodes { oid } - } - } - } - } - } - }' - auth=$(printf '%s %s' 'Bearer' "$GHCR_TOKEN") - body=$(mktemp) - code=$( - curl -sS \ - -H "Authorization: ${auth}" \ - -H "Accept: application/vnd.github+json" \ - -H "Content-Type: application/json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - -o "$body" \ - -w "%{http_code}" \ - https://api.github.com/graphql \ - --data "$(jq -n --arg query "$query" '{query: $query}')" \ - || true - ) - if [[ "$code" != "200" ]]; then - echo "::error::Unable to read cmd/devpanel-client history from GitHub GraphQL (HTTP ${code:-unknown})." - jq -r '.message // empty' "$body" >&2 || true - rm -f "$body" - exit 1 - fi - if jq -e '.errors | length > 0' "$body" >/dev/null; then - echo "::error::GitHub GraphQL returned an error while reading cmd/devpanel-client history." - jq -r '.errors[].message' "$body" >&2 - rm -f "$body" - exit 1 - fi - sha=$(jq -r '.data.repository.defaultBranchRef.target.history.nodes[0].oid // empty' "$body") - rm -f "$body" + auth=$(printf '%s:%s' "${GHCR_USER}" "${GHCR_TOKEN}" | base64 -w0) + echo "::add-mask::${auth}" + head=$(git -c "http.extraheader=AUTHORIZATION: basic ${auth}" \ + ls-remote https://github.com/devpanel/cli.git HEAD) + sha=$(awk '$2 == "HEAD" {print $1}' <<< "$head") if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then - echo "::error::No commit on the default branch changes cmd/devpanel-client." + echo "::error::Unable to resolve the devpanel/cli default branch HEAD." exit 1 fi echo "sha=${sha}" >> "$GITHUB_OUTPUT" - echo "Latest cmd/devpanel-client commit is ${sha}" + echo "devpanel/cli default branch HEAD is ${sha}" - name: Restore the built revision id: revision-cache uses: actions/cache/restore@v5 with: path: ${{ runner.temp }}/devpanel-cli-revision - key: devpanel-client-${{ steps.upstream.outputs.sha }} + key: devpanel-cli-go-build-v1-${{ github.ref }}-${{ steps.upstream.outputs.sha }} + lookup-only: true - name: Decide whether to rebuild id: decision env: CACHE_HIT: ${{ steps.revision-cache.outputs.cache-hit }} - REF_TYPE: ${{ github.ref_type }} UPSTREAM_SHA: ${{ steps.upstream.outputs.sha }} run: | - if [[ "${REF_TYPE}" == "tag" ]]; then - echo "changed=false" >> "$GITHUB_OUTPUT" - echo "Tagged commits are left unchanged." - elif [[ "${CACHE_HIT}" == "true" ]]; then + if [[ "${CACHE_HIT}" == "true" ]]; then echo "changed=false" >> "$GITHUB_OUTPUT" - echo "cmd/devpanel-client ${UPSTREAM_SHA} is already built." + echo "devpanel/cli ${UPSTREAM_SHA} is already built for this branch." else echo "changed=true" >> "$GITHUB_OUTPUT" - echo "cmd/devpanel-client ${UPSTREAM_SHA} is not in the Actions cache." + echo "devpanel/cli ${UPSTREAM_SHA} has not been built for this branch." fi build: @@ -150,6 +109,7 @@ jobs: dest="${RUNNER_TEMP}/devpanel-cli" mkdir "$dest" auth=$(printf '%s:%s' "${GHCR_USER}" "${GHCR_TOKEN}" | base64 -w0) + echo "::add-mask::${auth}" git -C "$dest" init git -C "$dest" remote add origin https://github.com/devpanel/cli.git git -C "$dest" -c "http.extraheader=AUTHORIZATION: basic ${auth}" \ @@ -162,21 +122,17 @@ jobs: go-version-file: ${{ runner.temp }}/devpanel-cli/go.mod cache: false - - name: Build native binary + - name: Build native binary with upstream go-build target + working-directory: ${{ runner.temp }}/devpanel-cli env: - GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} - GHCR_USER: ${{ vars.GHCR_USER }} + ARCH: ${{ matrix.arch }} run: | set -euo pipefail - git config --global url."https://${GHCR_USER}:${GHCR_TOKEN}@github.com/".insteadOf "https://github.com/" - dest="${RUNNER_TEMP}/devpanel-${{ matrix.arch }}" - ( - cd "${RUNNER_TEMP}/devpanel-cli" - env -u GOOS -u GOARCH CGO_ENABLED=0 \ - go build -trimpath -buildvcs=false -o "$dest" ./cmd/devpanel-client - ) - chmod 0755 "$dest" - "$dest" version + mkdir -p bin + env -u GOOS -u GOARCH make go-build + dest="${RUNNER_TEMP}/devpanel-${ARCH}" + install -m 0755 bin/devpanel "$dest" + "$dest" --callback=false version - name: Upload binary uses: actions/upload-artifact@v7 @@ -225,7 +181,7 @@ jobs: git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git commit \ -m "Update devpanel CLI from devpanel/cli@${UPSTREAM_SHA}" \ - -m "Built linux/amd64 and linux/arm64 from cmd/devpanel-client at that commit." + -m "Built linux/amd64 and linux/arm64 with make go-build (main.go) at that commit." git push origin "HEAD:${GITHUB_REF_NAME}" fi @@ -240,4 +196,4 @@ jobs: uses: actions/cache/save@v5 with: path: ${{ runner.temp }}/devpanel-cli-revision - key: devpanel-client-${{ needs.plan.outputs.sha }} + key: devpanel-cli-go-build-v1-${{ github.ref }}-${{ needs.plan.outputs.sha }} diff --git a/AGENTS.md b/AGENTS.md index 1b51e905..b901bc5a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -129,7 +129,7 @@ Baseline JSON files in `tests/baselines/` track pre-existing lint violations per #### Workflows - **`docker-build.yml`** — triggered on pushes to `main` or `develop` and by manual `workflow_dispatch`; detects changed versions, builds images per-platform in parallel, and merges platform-specific digests into multi-arch manifests. -- **`update-devpanel-cli.yml`** — manual `workflow_dispatch`. Builds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` from the latest `devpanel/cli` commit that changed `cmd/devpanel-client`, when the Actions cache does not already contain that commit. A new run cancels one already in progress. The workflow token commits the binaries. +- **`update-devpanel-cli.yml`** — manual `workflow_dispatch` on branches only. Resolves the `devpanel/cli` default branch HEAD and builds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` in parallel on native runners using `mkdir -p bin` and `make go-build` (the root `main.go`, Linux, CGO disabled). The Go version comes from upstream `go.mod`. A branch-specific Actions cache records the revision only after the workflow token commits both binaries. A new run cancels one already in progress. Upstream Git authentication is scoped to individual commands, not global configuration. - **`test.yml`** — runs YAML, shell, and Dockerfile linting plus build/run tests on pushes and pull requests. - Production images are tagged without suffix (e.g. `devpanel/php:8.3-base`); `develop` branch builds use the `-rc` suffix. - Required repository secrets: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`. Optional: `GHCR_TOKEN` (falls back to `GITHUB_TOKEN` for GHCR pushes). `update-devpanel-cli.yml` requires `GHCR_TOKEN` with access to `devpanel/cli`, and the `GHCR_USER` repository variable set to the username that owns that token. diff --git a/README.md b/README.md index caedc07f..e84ca92e 100644 --- a/README.md +++ b/README.md @@ -291,13 +291,20 @@ for setup instructions. Native AMD64 builds skip QEMU. - `update-devpanel-cli.yml` rebuilds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` from the latest - [devpanel/cli](https://github.com/devpanel/cli) commit that changed - `cmd/devpanel-client`. Run it manually. It caches that commit and skips - the build when the cache entry exists. A new run cancels one already in - progress. The workflow token commits the binaries. Delete the cache - entry to rebuild the same commit. The `devpanel/cli` checkout is - placed outside this repository. AMD64 and ARM64 binaries build in - parallel on `ubuntu-26.04` and `ubuntu-26.04-arm`. + [devpanel/cli](https://github.com/devpanel/cli) default branch HEAD. + Run it manually on a branch; tags are left unchanged. Each native build + creates `bin/` and runs the upstream `make go-build` target, which compiles + `main.go` for Linux with CGO disabled. Both builds use the same resolved + commit and the Go version from its `go.mod`. The workflow caches the + revision per destination branch only after both binaries are committed, + and skips subsequent builds on a cache hit. Delete that cache entry to + rebuild the same commit. A new run cancels one already in progress. + The workflow token commits the binaries; `GHCR_TOKEN` and `GHCR_USER` + authenticate reads of the private upstream repository without storing + credentials in global Git configuration. The `devpanel/cli` checkout is + placed outside this repository. AMD64 and ARM64 binaries build in parallel + on `ubuntu-26.04` and `ubuntu-26.04-arm`. This workflow does not change + which binary the Dockerfile copies into images. - The `advance` variant depends on `secure`, which depends on `base`. The build chain is enforced via the bake target dependency graph. diff --git a/TODO.md b/TODO.md index 0d6826c4..9f29b941 100644 --- a/TODO.md +++ b/TODO.md @@ -107,8 +107,9 @@ Use this file to track work that needs to be done. Lint scripts automatically ap ## Steps - [x] Add a manual workflow based on `main`. The workflow token commits the binaries. -- [x] Cache the latest commit that changed `cmd/devpanel-client` and skip the build when that entry exists. +- [x] Resolve and cache the upstream default branch HEAD per destination branch, and skip the build when that entry exists. - [x] Build linux/amd64 and linux/arm64 in parallel on native runners. +- [x] Create `bin/` and run the upstream `make go-build` target to compile the root `main.go` for Linux with CGO disabled. - [x] Clone `devpanel/cli` outside this repository and commit only the two binaries. ## Definition of Done @@ -118,9 +119,9 @@ Use this file to track work that needs to be done. Lint scripts automatically ap ## Steps - [x] Require the `GHCR_USER` repository variable with `GHCR_TOKEN`. -- [x] Read the latest `cmd/devpanel-client` commit from the commits API as that user. +- [x] Resolve the default branch HEAD with authenticated `git ls-remote`. - [x] Clone and fetch `devpanel/cli` for the build with Basic auth for that username and token. ## Definition of Done -- The plan job records the commit the API returns for `cmd/devpanel-client`. +- The plan job records the upstream default branch HEAD, not history for an assumed source path. - The build job authenticates to `devpanel/cli` as `vars.GHCR_USER`. From f11ec31ac984ea93e7d4ff683ac11e6300f35b02 Mon Sep 17 00:00:00 2001 From: darrenoh <2293701+darrenoh@users.noreply.github.com> Date: Wed, 11 Nov 2026 16:53:01 +0000 Subject: [PATCH 2/5] Authenticate CLI update workflow to GitLab Use a GitLab read-only token and username for the upstream fetch and private module access, scope credentials to GitLab Git requests, and build the client package identified in the existing binary. Keep the workflow in draft pending verification against the actual GitLab source. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/update-devpanel-cli.yml | 79 ++++++++++++++--------- AGENTS.md | 4 +- README.md | 18 ++++-- TODO.md | 15 +++-- 4 files changed, 68 insertions(+), 48 deletions(-) diff --git a/.github/workflows/update-devpanel-cli.yml b/.github/workflows/update-devpanel-cli.yml index deffd0c7..ab4b8a67 100644 --- a/.github/workflows/update-devpanel-cli.yml +++ b/.github/workflows/update-devpanel-cli.yml @@ -1,16 +1,16 @@ name: Update DevPanel CLI binaries # Build linux/amd64 and linux/arm64 devpanel binaries from -# https://github.com/devpanel/cli using its go-build Make target and commit +# https://gitlab.com/devpanel-vn/cli and commit # base/bin/devpanel-amd64 and base/bin/devpanel-arm64. # # Run it manually on a branch. The Actions cache records the upstream default # branch HEAD after both binaries are committed to this branch. Delete that # cache entry to rebuild the same commit. # -# The build clones devpanel/cli under RUNNER_TEMP, outside this repository. -# GHCR_TOKEN must be able to read that repository. GHCR_USER is the username -# that owns the token. The workflow token commits the binaries. +# The build clones the GitLab repository under RUNNER_TEMP. +# GITLAB_TOKEN and GITLAB_USER must authenticate with read_repository access. +# The workflow token commits the binaries. on: workflow_dispatch: @@ -34,38 +34,38 @@ jobs: - name: Resolve upstream default branch HEAD id: upstream env: - GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} - GHCR_USER: ${{ vars.GHCR_USER }} + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_USER: ${{ vars.GITLAB_USER }} GIT_TERMINAL_PROMPT: "0" run: | set -euo pipefail - if [[ -z "${GHCR_TOKEN}" ]]; then - echo "::error::GHCR_TOKEN is required to read devpanel/cli." + if [[ -z "${GITLAB_TOKEN}" ]]; then + echo "::error::GITLAB_TOKEN is required to read gitlab.com/devpanel-vn/cli." exit 1 fi - if [[ -z "${GHCR_USER}" ]]; then - echo "::error::GHCR_USER is required. Set it to the username that owns GHCR_TOKEN." + if [[ -z "${GITLAB_USER}" ]]; then + echo "::error::GITLAB_USER is required. Set it to the GitLab token username." exit 1 fi - auth=$(printf '%s:%s' "${GHCR_USER}" "${GHCR_TOKEN}" | base64 -w0) + auth=$(printf '%s:%s' "${GITLAB_USER}" "${GITLAB_TOKEN}" | base64 -w0) echo "::add-mask::${auth}" - head=$(git -c "http.extraheader=AUTHORIZATION: basic ${auth}" \ - ls-remote https://github.com/devpanel/cli.git HEAD) + head=$(git -c "http.https://gitlab.com/.extraheader=AUTHORIZATION: basic ${auth}" \ + ls-remote https://gitlab.com/devpanel-vn/cli.git HEAD) sha=$(awk '$2 == "HEAD" {print $1}' <<< "$head") if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then - echo "::error::Unable to resolve the devpanel/cli default branch HEAD." + echo "::error::Unable to resolve the GitLab CLI default branch HEAD." exit 1 fi echo "sha=${sha}" >> "$GITHUB_OUTPUT" - echo "devpanel/cli default branch HEAD is ${sha}" + echo "GitLab CLI default branch HEAD is ${sha}" - name: Restore the built revision id: revision-cache uses: actions/cache/restore@v5 with: path: ${{ runner.temp }}/devpanel-cli-revision - key: devpanel-cli-go-build-v1-${{ github.ref }}-${{ steps.upstream.outputs.sha }} + key: gitlab-devpanel-client-v1-${{ github.ref }}-${{ steps.upstream.outputs.sha }} lookup-only: true - name: Decide whether to rebuild @@ -76,10 +76,10 @@ jobs: run: | if [[ "${CACHE_HIT}" == "true" ]]; then echo "changed=false" >> "$GITHUB_OUTPUT" - echo "devpanel/cli ${UPSTREAM_SHA} is already built for this branch." + echo "GitLab CLI ${UPSTREAM_SHA} is already built for this branch." else echo "changed=true" >> "$GITHUB_OUTPUT" - echo "devpanel/cli ${UPSTREAM_SHA} has not been built for this branch." + echo "GitLab CLI ${UPSTREAM_SHA} has not been built for this branch." fi build: @@ -98,21 +98,21 @@ jobs: permissions: contents: read steps: - - name: Clone devpanel/cli + - name: Fetch GitLab CLI env: - GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }} - GHCR_USER: ${{ vars.GHCR_USER }} + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_USER: ${{ vars.GITLAB_USER }} UPSTREAM_SHA: ${{ needs.plan.outputs.sha }} GIT_TERMINAL_PROMPT: "0" run: | set -euo pipefail dest="${RUNNER_TEMP}/devpanel-cli" mkdir "$dest" - auth=$(printf '%s:%s' "${GHCR_USER}" "${GHCR_TOKEN}" | base64 -w0) + auth=$(printf '%s:%s' "${GITLAB_USER}" "${GITLAB_TOKEN}" | base64 -w0) echo "::add-mask::${auth}" git -C "$dest" init - git -C "$dest" remote add origin https://github.com/devpanel/cli.git - git -C "$dest" -c "http.extraheader=AUTHORIZATION: basic ${auth}" \ + git -C "$dest" remote add origin https://gitlab.com/devpanel-vn/cli.git + git -C "$dest" -c "http.https://gitlab.com/.extraheader=AUTHORIZATION: basic ${auth}" \ fetch --depth 1 origin "$UPSTREAM_SHA" git -C "$dest" checkout --detach FETCH_HEAD @@ -122,17 +122,32 @@ jobs: go-version-file: ${{ runner.temp }}/devpanel-cli/go.mod cache: false - - name: Build native binary with upstream go-build target + - name: Build native client binary working-directory: ${{ runner.temp }}/devpanel-cli env: ARCH: ${{ matrix.arch }} + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_USER: ${{ vars.GITLAB_USER }} + GIT_TERMINAL_PROMPT: "0" + GOPRIVATE: gitlab.com/devpanel-vn/* run: | set -euo pipefail - mkdir -p bin - env -u GOOS -u GOARCH make go-build + if [[ ! -d cmd/devpanel-client ]]; then + echo "::error::GitLab source does not contain cmd/devpanel-client; verify the client build path." + exit 1 + fi + auth=$(printf '%s:%s' "${GITLAB_USER}" "${GITLAB_TOKEN}" | base64 -w0) + echo "::add-mask::${auth}" + export GIT_CONFIG_COUNT=1 + export GIT_CONFIG_KEY_0=http.https://gitlab.com/.extraheader + export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${auth}" dest="${RUNNER_TEMP}/devpanel-${ARCH}" - install -m 0755 bin/devpanel "$dest" - "$dest" --callback=false version + env -u GOOS -u GOARCH CGO_ENABLED=0 \ + go build -trimpath -o "$dest" ./cmd/devpanel-client + unset GITLAB_TOKEN GITLAB_USER GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0 auth + chmod 0755 "$dest" + "$dest" version + "$dest" app trigger --help - name: Upload binary uses: actions/upload-artifact@v7 @@ -180,8 +195,8 @@ jobs: git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git commit \ - -m "Update devpanel CLI from devpanel/cli@${UPSTREAM_SHA}" \ - -m "Built linux/amd64 and linux/arm64 with make go-build (main.go) at that commit." + -m "Update devpanel CLI from GitLab devpanel-vn/cli@${UPSTREAM_SHA}" \ + -m "Built linux/amd64 and linux/arm64 from cmd/devpanel-client at that commit." git push origin "HEAD:${GITHUB_REF_NAME}" fi @@ -196,4 +211,4 @@ jobs: uses: actions/cache/save@v5 with: path: ${{ runner.temp }}/devpanel-cli-revision - key: devpanel-cli-go-build-v1-${{ github.ref }}-${{ needs.plan.outputs.sha }} + key: gitlab-devpanel-client-v1-${{ github.ref }}-${{ needs.plan.outputs.sha }} diff --git a/AGENTS.md b/AGENTS.md index b901bc5a..7ff84942 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -129,10 +129,10 @@ Baseline JSON files in `tests/baselines/` track pre-existing lint violations per #### Workflows - **`docker-build.yml`** — triggered on pushes to `main` or `develop` and by manual `workflow_dispatch`; detects changed versions, builds images per-platform in parallel, and merges platform-specific digests into multi-arch manifests. -- **`update-devpanel-cli.yml`** — manual `workflow_dispatch` on branches only. Resolves the `devpanel/cli` default branch HEAD and builds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` in parallel on native runners using `mkdir -p bin` and `make go-build` (the root `main.go`, Linux, CGO disabled). The Go version comes from upstream `go.mod`. A branch-specific Actions cache records the revision only after the workflow token commits both binaries. A new run cancels one already in progress. Upstream Git authentication is scoped to individual commands, not global configuration. +- **`update-devpanel-cli.yml`** — manual `workflow_dispatch` on branches only. Resolves the GitLab `devpanel-vn/cli` default branch HEAD and builds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` in parallel on native runners from `cmd/devpanel-client` (the original binary's embedded package path; confirm against GitLab source). The Go version comes from upstream `go.mod`. A branch-specific Actions cache records the revision only after the workflow token commits both binaries. A new run cancels one already in progress. Upstream Git authentication is scoped to GitLab requests, not global configuration. - **`test.yml`** — runs YAML, shell, and Dockerfile linting plus build/run tests on pushes and pull requests. - Production images are tagged without suffix (e.g. `devpanel/php:8.3-base`); `develop` branch builds use the `-rc` suffix. -- Required repository secrets: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`. Optional: `GHCR_TOKEN` (falls back to `GITHUB_TOKEN` for GHCR pushes). `update-devpanel-cli.yml` requires `GHCR_TOKEN` with access to `devpanel/cli`, and the `GHCR_USER` repository variable set to the username that owns that token. +- Required repository secrets: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`. Optional: `GHCR_TOKEN` (falls back to `GITHUB_TOKEN` for GHCR pushes). `update-devpanel-cli.yml` requires the `GITLAB_TOKEN` repository secret with `read_repository` access and the `GITLAB_USER` repository variable containing its GitLab username. ## Environment Variables (base images) diff --git a/README.md b/README.md index e84ca92e..ba68e8ca 100644 --- a/README.md +++ b/README.md @@ -291,17 +291,21 @@ for setup instructions. Native AMD64 builds skip QEMU. - `update-devpanel-cli.yml` rebuilds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` from the latest - [devpanel/cli](https://github.com/devpanel/cli) default branch HEAD. - Run it manually on a branch; tags are left unchanged. Each native build - creates `bin/` and runs the upstream `make go-build` target, which compiles - `main.go` for Linux with CGO disabled. Both builds use the same resolved + [GitLab CLI repository](https://gitlab.com/devpanel-vn/cli) default branch + HEAD. Run it manually on a branch; tags are left unchanged. Each native build + compiles `cmd/devpanel-client` for Linux with CGO disabled. This client path + is based on the existing binary's metadata and must be confirmed against + the GitLab source. Both builds use the same resolved commit and the Go version from its `go.mod`. The workflow caches the revision per destination branch only after both binaries are committed, and skips subsequent builds on a cache hit. Delete that cache entry to rebuild the same commit. A new run cancels one already in progress. - The workflow token commits the binaries; `GHCR_TOKEN` and `GHCR_USER` - authenticate reads of the private upstream repository without storing - credentials in global Git configuration. The `devpanel/cli` checkout is + Set the `GITLAB_TOKEN` repository secret to a GitLab deploy token with + `read_repository` access to the project and the `GITLAB_USER` repository + variable to that deploy token's username. A personal access token with + `read_repository` access and a non-empty GitLab username also works. + The workflow token commits the binaries. GitLab credentials are scoped + to GitLab Git requests and not stored in global Git configuration. The checkout is placed outside this repository. AMD64 and ARM64 binaries build in parallel on `ubuntu-26.04` and `ubuntu-26.04-arm`. This workflow does not change which binary the Dockerfile copies into images. diff --git a/TODO.md b/TODO.md index 9f29b941..abe65dfc 100644 --- a/TODO.md +++ b/TODO.md @@ -103,25 +103,26 @@ Use this file to track work that needs to be done. Lint scripts automatically ap - OPcache remains loaded on every PHP version without enabling a nonexistent PHP 8.5 shared module. - Focused checks pass; any remaining build or CI blockers are explicitly reported. -# Task: Rebuild the DevPanel CLI from devpanel/cli +# Task: Rebuild the DevPanel CLI from GitLab ## Steps - [x] Add a manual workflow based on `main`. The workflow token commits the binaries. - [x] Resolve and cache the upstream default branch HEAD per destination branch, and skip the build when that entry exists. - [x] Build linux/amd64 and linux/arm64 in parallel on native runners. -- [x] Create `bin/` and run the upstream `make go-build` target to compile the root `main.go` for Linux with CGO disabled. -- [x] Clone `devpanel/cli` outside this repository and commit only the two binaries. +- [x] Configure GitLab authentication and fetch the resolved revision outside this repository. +- [ ] Confirm `cmd/devpanel-client`, Go setup, build dependencies and smoke checks against the actual GitLab source. +- [ ] Run the workflow with GitLab credentials and verify both binaries and revision caching. ## Definition of Done - A cache miss builds both binaries on `ubuntu-26.04` and `ubuntu-26.04-arm` and commits `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64`. -# Task: Authenticate to devpanel/cli with GHCR_USER +# Task: Authenticate to GitLab CLI ## Steps -- [x] Require the `GHCR_USER` repository variable with `GHCR_TOKEN`. +- [x] Require the `GITLAB_USER` repository variable with `GITLAB_TOKEN`. - [x] Resolve the default branch HEAD with authenticated `git ls-remote`. -- [x] Clone and fetch `devpanel/cli` for the build with Basic auth for that username and token. +- [x] Fetch `devpanel-vn/cli` with GitLab-scoped Basic auth for that username and token. ## Definition of Done - The plan job records the upstream default branch HEAD, not history for an assumed source path. -- The build job authenticates to `devpanel/cli` as `vars.GHCR_USER`. +- The build job authenticates to GitLab as `vars.GITLAB_USER` without persisting credentials. From 3fb71c01edb0f6cf08674770db49d19eef7333b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:16:43 +0000 Subject: [PATCH 3/5] Resolve DevPanel CLI revision via GitLab API Co-authored-by: darrenoh <2293701+darrenoh@users.noreply.github.com> --- .github/workflows/update-devpanel-cli.yml | 20 +++++++------------- TODO.md | 10 ++++++++++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/update-devpanel-cli.yml b/.github/workflows/update-devpanel-cli.yml index ab4b8a67..f65ce3e5 100644 --- a/.github/workflows/update-devpanel-cli.yml +++ b/.github/workflows/update-devpanel-cli.yml @@ -8,8 +8,9 @@ name: Update DevPanel CLI binaries # branch HEAD after both binaries are committed to this branch. Delete that # cache entry to rebuild the same commit. # -# The build clones the GitLab repository under RUNNER_TEMP. -# GITLAB_TOKEN and GITLAB_USER must authenticate with read_repository access. +# The plan job queries GitLab's API; the build fetches the commit under RUNNER_TEMP. +# GITLAB_TOKEN must have read_api and read_repository access. +# GITLAB_USER is used to authenticate the Git fetch. # The workflow token commits the binaries. on: @@ -35,24 +36,17 @@ jobs: id: upstream env: GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} - GITLAB_USER: ${{ vars.GITLAB_USER }} - GIT_TERMINAL_PROMPT: "0" run: | set -euo pipefail if [[ -z "${GITLAB_TOKEN}" ]]; then echo "::error::GITLAB_TOKEN is required to read gitlab.com/devpanel-vn/cli." exit 1 fi - if [[ -z "${GITLAB_USER}" ]]; then - echo "::error::GITLAB_USER is required. Set it to the GitLab token username." - exit 1 - fi - auth=$(printf '%s:%s' "${GITLAB_USER}" "${GITLAB_TOKEN}" | base64 -w0) - echo "::add-mask::${auth}" - head=$(git -c "http.https://gitlab.com/.extraheader=AUTHORIZATION: basic ${auth}" \ - ls-remote https://gitlab.com/devpanel-vn/cli.git HEAD) - sha=$(awk '$2 == "HEAD" {print $1}' <<< "$head") + commits=$(curl --fail --silent --show-error \ + --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" \ + "https://gitlab.com/api/v4/projects/devpanel-vn%2Fcli/repository/commits?per_page=1") + sha=$(jq -r '.[0].id // empty' <<< "$commits") if [[ ! "$sha" =~ ^[0-9a-f]{40}$ ]]; then echo "::error::Unable to resolve the GitLab CLI default branch HEAD." exit 1 diff --git a/TODO.md b/TODO.md index abe65dfc..48c377ef 100644 --- a/TODO.md +++ b/TODO.md @@ -126,3 +126,13 @@ Use this file to track work that needs to be done. Lint scripts automatically ap ## Definition of Done - The plan job records the upstream default branch HEAD, not history for an assumed source path. - The build job authenticates to GitLab as `vars.GITLAB_USER` without persisting credentials. + +# Task: Resolve DevPanel CLI revision with GitLab API + +## Steps +- [x] Resolve the default branch's latest commit through the authenticated GitLab API. +- [x] Preserve the build job's shallow fetch and validate the workflow changes. + +## Definition of Done +- The plan job obtains the latest default-branch commit SHA through GitLab's API using a token with `read_api` access. +- The build job still fetches only the resolved commit and authenticates with `read_repository` access. From 853d0ee294cde34655ab05743ff23b1de4640021 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Nov 2026 22:59:08 +0000 Subject: [PATCH 4/5] Consolidate DevPanel CLI TODO items Co-authored-by: darrenoh <2293701+darrenoh@users.noreply.github.com> --- TODO.md | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/TODO.md b/TODO.md index 48c377ef..5f710450 100644 --- a/TODO.md +++ b/TODO.md @@ -107,32 +107,12 @@ Use this file to track work that needs to be done. Lint scripts automatically ap ## Steps - [x] Add a manual workflow based on `main`. The workflow token commits the binaries. -- [x] Resolve and cache the upstream default branch HEAD per destination branch, and skip the build when that entry exists. -- [x] Build linux/amd64 and linux/arm64 in parallel on native runners. -- [x] Configure GitLab authentication and fetch the resolved revision outside this repository. +- [x] Resolve and cache the upstream default branch HEAD through the authenticated GitLab commits API, and skip revisions already built for the destination branch. +- [x] Build linux/amd64 and linux/arm64 in parallel on native runners, then commit both binaries. +- [x] Configure GitLab API and source-fetch authentication with `GITLAB_TOKEN` and `GITLAB_USER`. - [ ] Confirm `cmd/devpanel-client`, Go setup, build dependencies and smoke checks against the actual GitLab source. - [ ] Run the workflow with GitLab credentials and verify both binaries and revision caching. ## Definition of Done -- A cache miss builds both binaries on `ubuntu-26.04` and `ubuntu-26.04-arm` and commits `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64`. - -# Task: Authenticate to GitLab CLI - -## Steps -- [x] Require the `GITLAB_USER` repository variable with `GITLAB_TOKEN`. -- [x] Resolve the default branch HEAD with authenticated `git ls-remote`. -- [x] Fetch `devpanel-vn/cli` with GitLab-scoped Basic auth for that username and token. - -## Definition of Done -- The plan job records the upstream default branch HEAD, not history for an assumed source path. -- The build job authenticates to GitLab as `vars.GITLAB_USER` without persisting credentials. - -# Task: Resolve DevPanel CLI revision with GitLab API - -## Steps -- [x] Resolve the default branch's latest commit through the authenticated GitLab API. -- [x] Preserve the build job's shallow fetch and validate the workflow changes. - -## Definition of Done -- The plan job obtains the latest default-branch commit SHA through GitLab's API using a token with `read_api` access. -- The build job still fetches only the resolved commit and authenticates with `read_repository` access. +- A cache miss resolves the latest default-branch commit through the GitLab API, builds both binaries on `ubuntu-26.04` and `ubuntu-26.04-arm`, and commits `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64`. +- GitLab access uses `GITLAB_TOKEN` with `read_api` and `read_repository` access and the `GITLAB_USER` repository variable; the built revision is cached after the binaries are committed. From c2c878fe31c21f0b6c5d6e52709b3c6fafd4e6ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Nov 2026 03:42:10 +0000 Subject: [PATCH 5/5] Document GitLab updater token scopes Co-authored-by: darrenoh <2293701+darrenoh@users.noreply.github.com> --- .github/workflows/update-devpanel-cli.yml | 4 ++-- AGENTS.md | 2 +- README.md | 9 +++++---- TODO.md | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/update-devpanel-cli.yml b/.github/workflows/update-devpanel-cli.yml index f65ce3e5..1b067831 100644 --- a/.github/workflows/update-devpanel-cli.yml +++ b/.github/workflows/update-devpanel-cli.yml @@ -8,8 +8,8 @@ name: Update DevPanel CLI binaries # branch HEAD after both binaries are committed to this branch. Delete that # cache entry to rebuild the same commit. # -# The plan job queries GitLab's API; the build fetches the commit under RUNNER_TEMP. -# GITLAB_TOKEN must have read_api and read_repository access. +# The plan job queries GitLab's commits API; the build fetches the commit under RUNNER_TEMP. +# GITLAB_TOKEN needs read_api for the API and read_repository for Git fetches. # GITLAB_USER is used to authenticate the Git fetch. # The workflow token commits the binaries. diff --git a/AGENTS.md b/AGENTS.md index 7ff84942..76262305 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -132,7 +132,7 @@ Baseline JSON files in `tests/baselines/` track pre-existing lint violations per - **`update-devpanel-cli.yml`** — manual `workflow_dispatch` on branches only. Resolves the GitLab `devpanel-vn/cli` default branch HEAD and builds `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64` in parallel on native runners from `cmd/devpanel-client` (the original binary's embedded package path; confirm against GitLab source). The Go version comes from upstream `go.mod`. A branch-specific Actions cache records the revision only after the workflow token commits both binaries. A new run cancels one already in progress. Upstream Git authentication is scoped to GitLab requests, not global configuration. - **`test.yml`** — runs YAML, shell, and Dockerfile linting plus build/run tests on pushes and pull requests. - Production images are tagged without suffix (e.g. `devpanel/php:8.3-base`); `develop` branch builds use the `-rc` suffix. -- Required repository secrets: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`. Optional: `GHCR_TOKEN` (falls back to `GITHUB_TOKEN` for GHCR pushes). `update-devpanel-cli.yml` requires the `GITLAB_TOKEN` repository secret with `read_repository` access and the `GITLAB_USER` repository variable containing its GitLab username. +- Required repository secrets: `DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN`. Optional: `GHCR_TOKEN` (falls back to `GITHUB_TOKEN` for GHCR pushes). `update-devpanel-cli.yml` requires the service account's `GITLAB_TOKEN` with both `read_api` (for the commits API) and `read_repository` (for Git fetches) scopes, plus the `GITLAB_USER` repository variable containing that account's GitLab username. A deploy token with only `read_repository` cannot authenticate the API request. ## Environment Variables (base images) diff --git a/README.md b/README.md index ba68e8ca..61d09f18 100644 --- a/README.md +++ b/README.md @@ -300,10 +300,11 @@ for setup instructions. revision per destination branch only after both binaries are committed, and skips subsequent builds on a cache hit. Delete that cache entry to rebuild the same commit. A new run cancels one already in progress. - Set the `GITLAB_TOKEN` repository secret to a GitLab deploy token with - `read_repository` access to the project and the `GITLAB_USER` repository - variable to that deploy token's username. A personal access token with - `read_repository` access and a non-empty GitLab username also works. + Set the `GITLAB_TOKEN` repository secret to the service account's GitLab + token with both `read_api` (for the commits API) and `read_repository` (for + Git fetches) scopes. Set the `GITLAB_USER` repository variable to the + service account's username. A deploy token with only `read_repository` is + insufficient because the workflow also queries the GitLab API. The workflow token commits the binaries. GitLab credentials are scoped to GitLab Git requests and not stored in global Git configuration. The checkout is placed outside this repository. AMD64 and ARM64 binaries build in parallel diff --git a/TODO.md b/TODO.md index 5f710450..b86faf92 100644 --- a/TODO.md +++ b/TODO.md @@ -109,10 +109,10 @@ Use this file to track work that needs to be done. Lint scripts automatically ap - [x] Add a manual workflow based on `main`. The workflow token commits the binaries. - [x] Resolve and cache the upstream default branch HEAD through the authenticated GitLab commits API, and skip revisions already built for the destination branch. - [x] Build linux/amd64 and linux/arm64 in parallel on native runners, then commit both binaries. -- [x] Configure GitLab API and source-fetch authentication with `GITLAB_TOKEN` and `GITLAB_USER`. +- [x] Configure GitLab API and source-fetch authentication with the service account's `GITLAB_TOKEN` (`read_api` and `read_repository`) and `GITLAB_USER`. - [ ] Confirm `cmd/devpanel-client`, Go setup, build dependencies and smoke checks against the actual GitLab source. - [ ] Run the workflow with GitLab credentials and verify both binaries and revision caching. ## Definition of Done - A cache miss resolves the latest default-branch commit through the GitLab API, builds both binaries on `ubuntu-26.04` and `ubuntu-26.04-arm`, and commits `base/bin/devpanel-amd64` and `base/bin/devpanel-arm64`. -- GitLab access uses `GITLAB_TOKEN` with `read_api` and `read_repository` access and the `GITLAB_USER` repository variable; the built revision is cached after the binaries are committed. +- GitLab API access uses the service account's `GITLAB_TOKEN` with `read_api` and `read_repository` scopes plus its username in `GITLAB_USER`; the built revision is cached after the binaries are committed.