g1t has actions of its own: g1t/deploy builds the checkout a job is in and puts it on g1t.page, opening the build with the job's token, masking the build's token and the project's secrets, and running g1t-runner deploy, with the app's url, the deployment's id and its kind as outputs; g1t/detect says what such a build would find (kind, install, build and output-dir) without building anything. Both use the g1t-runner on the machine, or download the newest release from g1t.sh/downloads/runner and check it against its SHA256SUMS. They live under actions/, and the Actions release workflow pushes each folder to its own repository in the g1t workspace and moves its v1 tag whenever main changes them, with the G1T_ACTIONS_PUBLISH_TOKEN secret its header describes.
- Claude-Session:
- https://claude.ai/code/session_01PnwTRSNaC7vEHTAuBzjhrn
3 files+136−00/3 viewed
| 1 | + | # g1t/detect | |
| 2 | + | ||
| 3 | + | Says what a [g1t.page](https://docs.g1t.sh/guides/deployments/) build of | |
| 4 | + | the checkout would find, without building or deploying anything: the | |
| 5 | + | same rules [g1t/deploy](https://g1t.sh/g1t/deploy) builds with. | |
| 6 | + | ||
| 7 | + | ```yaml | |
| 8 | + | steps: | |
| 9 | + | - uses: actions/checkout@v5 | |
| 10 | + | - id: detect | |
| 11 | + | uses: g1t/detect@v1 | |
| 12 | + | - if: steps.detect.outputs.kind == 'none' | |
| 13 | + | run: echo "::error::Nothing here would be served." | |
| 14 | + | - if: steps.detect.outputs.install != '' | |
| 15 | + | run: ${{ steps.detect.outputs.install }} | |
| 16 | + | ``` | |
| 17 | + | ||
| 18 | + | ## Inputs | |
| 19 | + | ||
| 20 | + | | Input | Default | | | |
| 21 | + | | --- | --- | --- | | |
| 22 | + | | `root-dir` | The checkout | Where the project is in the checkout. | | |
| 23 | + | | `build-command` | None | A build command of your own, as `g1t/deploy` takes it. | | |
| 24 | + | | `output-dir` | None | An output folder of your own, as `g1t/deploy` takes it. | | |
| 25 | + | ||
| 26 | + | ## Outputs | |
| 27 | + | ||
| 28 | + | | Output | | | |
| 29 | + | | --- | --- | | |
| 30 | + | | `kind` | `workers` (a `wrangler.jsonc`, `wrangler.json` or `wrangler.toml`), `static` (a site a build writes), `html` (the files as they are) or `none`. | | |
| 31 | + | | `install` | How dependencies are installed, from the lockfile: `npm ci`, `pnpm install --frozen-lockfile`, and so on. Empty without a `package.json`. | | |
| 32 | + | | `build` | What builds it: your command, `npm run build` for a package with a build script, or `wrangler deploy --dry-run` for Workers. Empty when nothing does. | | |
| 33 | + | | `output-dir` | The folder served, relative to the project (`.` for the project itself): a Workers project's assets directory, or the first of `dist`, `build`, `out`, `public`, `_site`, `.output/public` that exists. Empty when a build has yet to write it. | | |
| 34 | + | ||
| 35 | + | It runs `g1t-runner detect`, found or downloaded as `g1t/deploy` finds it. |
| 1 | + | name: Detect a g1t.page project | |
| 2 | + | description: Says what a g1t.page build of this checkout would find, without building anything. Its kind, how it installs, what builds it and where its output is. | |
| 3 | + | author: g1t | |
| 4 | + | ||
| 5 | + | inputs: | |
| 6 | + | root-dir: | |
| 7 | + | description: Where the project is in the checkout. The checkout itself unless you say. | |
| 8 | + | required: false | |
| 9 | + | default: "" | |
| 10 | + | build-command: | |
| 11 | + | description: A build command of your own, as g1t/deploy takes it. | |
| 12 | + | required: false | |
| 13 | + | default: "" | |
| 14 | + | output-dir: | |
| 15 | + | description: An output folder of your own, as g1t/deploy takes it. | |
| 16 | + | required: false | |
| 17 | + | default: "" | |
| 18 | + | ||
| 19 | + | outputs: | |
| 20 | + | kind: | |
| 21 | + | description: workers (a Wrangler config), static (a site a build writes), html (the files as they are) or none. | |
| 22 | + | value: ${{ steps.detect.outputs.kind }} | |
| 23 | + | install: | |
| 24 | + | description: How its dependencies are installed, such as npm ci. Empty without a package.json. | |
| 25 | + | value: ${{ steps.detect.outputs.install }} | |
| 26 | + | build: | |
| 27 | + | description: What builds it. Empty when nothing does. | |
| 28 | + | value: ${{ steps.detect.outputs.build }} | |
| 29 | + | output-dir: | |
| 30 | + | description: The folder that is served, relative to the project (. for the project itself). Empty when a build has yet to write it. | |
| 31 | + | value: ${{ steps.detect.outputs.output-dir }} | |
| 32 | + | ||
| 33 | + | runs: | |
| 34 | + | using: composite | |
| 35 | + | steps: | |
| 36 | + | - id: detect | |
| 37 | + | shell: bash | |
| 38 | + | env: | |
| 39 | + | ROOT_DIR: ${{ inputs.root-dir }} | |
| 40 | + | BUILD_COMMAND: ${{ inputs.build-command }} | |
| 41 | + | OUTPUT_DIR: ${{ inputs.output-dir }} | |
| 42 | + | run: | | |
| 43 | + | set -euo pipefail | |
| 44 | + | runner="$(bash "$GITHUB_ACTION_PATH/runner.sh")" | |
| 45 | + | found="$("$runner" detect)" | |
| 46 | + | echo "$found" | |
| 47 | + | echo "$found" >> "$GITHUB_OUTPUT" |
| 1 | + | #!/usr/bin/env bash | |
| 2 | + | # Finds g1t-runner for the g1t/deploy and g1t/detect actions and prints its | |
| 3 | + | # path: G1T_RUNNER if it names one, else the one on PATH (g1t's machines | |
| 4 | + | # have it at /usr/local/bin/g1t-runner), else the newest release from | |
| 5 | + | # g1t.sh/downloads/runner, checked against that release's SHA256SUMS, as | |
| 6 | + | # docs.g1t.sh/guides/self-hosted-runners/ downloads it. | |
| 7 | + | # | |
| 8 | + | # actions/deploy/runner.sh and actions/detect/runner.sh are the same file: | |
| 9 | + | # each action is published as a repository of its own. | |
| 10 | + | set -euo pipefail | |
| 11 | + | ||
| 12 | + | if [ -n "${G1T_RUNNER:-}" ] && [ -x "${G1T_RUNNER}" ]; then | |
| 13 | + | echo "${G1T_RUNNER}" | |
| 14 | + | exit 0 | |
| 15 | + | fi | |
| 16 | + | if found="$(command -v g1t-runner 2>/dev/null)"; then | |
| 17 | + | echo "${found}" | |
| 18 | + | exit 0 | |
| 19 | + | fi | |
| 20 | + | ||
| 21 | + | case "$(uname -s)-$(uname -m)" in | |
| 22 | + | Linux-x86_64 | Linux-amd64) platform=linux-x64 ;; | |
| 23 | + | Linux-aarch64 | Linux-arm64) platform=linux-arm64 ;; | |
| 24 | + | Darwin-arm64) platform=macos-arm64 ;; | |
| 25 | + | Darwin-x86_64) platform=macos-x64 ;; | |
| 26 | + | *) | |
| 27 | + | echo "::error::g1t-runner is not on PATH, and g1t has no release of it for $(uname -s) $(uname -m)." >&2 | |
| 28 | + | exit 1 | |
| 29 | + | ;; | |
| 30 | + | esac | |
| 31 | + | ||
| 32 | + | downloads="${G1T_RUNNER_DOWNLOADS:-https://g1t.sh/downloads/runner/latest}" | |
| 33 | + | dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/g1t-runner-${platform}" | |
| 34 | + | file="g1t-runner-${platform}" | |
| 35 | + | if [ ! -x "${dir}/g1t-runner" ]; then | |
| 36 | + | mkdir -p "${dir}" | |
| 37 | + | echo "Downloading ${file} from ${downloads}" >&2 | |
| 38 | + | curl -fsSL --retry 3 -o "${dir}/${file}" "${downloads}/${file}" | |
| 39 | + | curl -fsSL --retry 3 -o "${dir}/SHA256SUMS" "${downloads}/SHA256SUMS" | |
| 40 | + | expected="$(awk -v name="${file}" '$2 == name { print $1 }' "${dir}/SHA256SUMS")" | |
| 41 | + | if command -v sha256sum >/dev/null 2>&1; then | |
| 42 | + | actual="$(sha256sum "${dir}/${file}" | awk '{ print $1 }')" | |
| 43 | + | else | |
| 44 | + | actual="$(shasum -a 256 "${dir}/${file}" | awk '{ print $1 }')" | |
| 45 | + | fi | |
| 46 | + | if [ -z "${expected}" ] || [ "${expected}" != "${actual}" ]; then | |
| 47 | + | echo "::error::The downloaded ${file} does not match the release's SHA256SUMS." >&2 | |
| 48 | + | rm -f "${dir}/${file}" | |
| 49 | + | exit 1 | |
| 50 | + | fi | |
| 51 | + | chmod +x "${dir}/${file}" | |
| 52 | + | mv "${dir}/${file}" "${dir}/g1t-runner" | |
| 53 | + | fi | |
| 54 | + | echo "${dir}/g1t-runner" |