detached 086173e

ci: add GitHub Actions deploy action for automated deployments

Add official Gordon deploy action:
- Composite action for building and pushing images
- Triggers on git tag push
- Auto-discovers Dockerfile
- Supports multi-platform builds and caching
- Includes workflow example and documentation
commit
086173e65124bfb3fdf7084c588242dfa26cb564
author
brice · · 2026-01-12 19:06 UTC
committer
brice · · 2026-01-12 20:57 UTC
parent
2a95454
signature
unsigned
3 files · +510 -0
A .github/actions/deploy/README.md +242 -0
@@ -0,0 +1,242 @@
1+# Gordon Deploy Action
2+
3+Build and push container images to a Gordon registry, with automatic deployment on push.
4+
5+## Features
6+
7+- Automatic image building from Dockerfile
8+- Tag-based versioning (uses git tag as image tag)
9+- Optional `:latest` tag pushing
10+- Multi-platform builds support
11+- Build caching with GitHub Actions cache
12+- Works with Gordon's token and password authentication
13+
14+## Usage
15+
16+### Basic Usage (Tag Push)
17+
18+```yaml
19+name: Deploy to Gordon
20+
21+on:
22+ push:
23+ tags:
24+ - 'v*'
25+
26+jobs:
27+ deploy:
28+ runs-on: ubuntu-latest
29+ steps:
30+ - uses: actions/checkout@v6
31+
32+ - name: Deploy to Gordon
33+ uses: bnema/gordon/.github/actions/deploy@main
34+ with:
35+ registry: registry.mydomain.com
36+ username: ${{ secrets.GORDON_USERNAME }}
37+ password: ${{ secrets.GORDON_TOKEN }}
38+```
39+
40+### Full Example with All Options
41+
42+```yaml
43+name: Deploy to Gordon
44+
45+on:
46+ push:
47+ tags:
48+ - 'v*'
49+ - 'release-*'
50+
51+jobs:
52+ deploy:
53+ runs-on: ubuntu-latest
54+ steps:
55+ - uses: actions/checkout@v6
56+
57+ - name: Deploy to Gordon
58+ id: deploy
59+ uses: bnema/gordon/.github/actions/deploy@main
60+ with:
61+ registry: registry.mydomain.com
62+ username: ${{ secrets.GORDON_USERNAME }}
63+ password: ${{ secrets.GORDON_TOKEN }}
64+ image: myapp # Custom image name
65+ dockerfile: ./docker/Dockerfile # Custom Dockerfile path
66+ context: . # Build context
67+ build-args: | # Build arguments
68+ NODE_ENV=production
69+ API_URL=https://api.example.com
70+ platforms: linux/amd64,linux/arm64 # Multi-platform
71+ push-latest: 'true' # Also push :latest
72+
73+ - name: Print outputs
74+ run: |
75+ echo "Image: ${{ steps.deploy.outputs.image }}"
76+ echo "Tag: ${{ steps.deploy.outputs.tag }}"
77+ echo "Digest: ${{ steps.deploy.outputs.digest }}"
78+```
79+
80+## Inputs
81+
82+| Input | Description | Required | Default |
83+|-------|-------------|----------|---------|
84+| `registry` | Gordon registry URL (e.g., `registry.mydomain.com`) | Yes | - |
85+| `username` | Registry username (token subject for token auth) | Yes | - |
86+| `password` | Registry password or token | Yes | - |
87+| `image` | Image name | No | Repository name |
88+| `dockerfile` | Path to Dockerfile | No | `./Dockerfile` |
89+| `context` | Build context path | No | `.` |
90+| `build-args` | Build arguments (one per line) | No | - |
91+| `platforms` | Target platforms | No | - |
92+| `push-latest` | Also push with `:latest` tag | No | `true` |
93+| `cache-from` | Cache source | No | `type=gha` |
94+| `cache-to` | Cache destination | No | `type=gha,mode=max` |
95+
96+## Outputs
97+
98+| Output | Description |
99+|--------|-------------|
100+| `image` | Full image reference that was pushed |
101+| `tag` | Tag that was pushed |
102+| `digest` | Image digest |
103+
104+## Authentication Setup
105+
106+### 1. Generate a Gordon Token
107+
108+On your Gordon server:
109+
110+```bash
111+# Generate a never-expiring token for CI
112+gordon auth token generate --subject github-actions --scopes push,pull --expiry 0
113+```
114+
115+### 2. Add GitHub Secrets
116+
117+In your repository settings, add:
118+
119+- `GORDON_USERNAME`: The token subject (e.g., `github-actions`)
120+- `GORDON_TOKEN`: The generated JWT token
121+
122+### 3. Configure Gordon
123+
124+Ensure your Gordon config uses token authentication:
125+
126+```toml
127+[secrets]
128+backend = "pass" # or "sops"
129+
130+[registry_auth]
131+enabled = true
132+type = "token"
133+token_secret = "gordon/registry/token_secret"
134+```
135+
136+## Examples
137+
138+### Deploy on Tag + Manual Trigger
139+
140+```yaml
141+name: Deploy
142+
143+on:
144+ push:
145+ tags:
146+ - 'v*'
147+ workflow_dispatch:
148+ inputs:
149+ tag:
150+ description: 'Tag to deploy'
151+ required: true
152+
153+jobs:
154+ deploy:
155+ runs-on: ubuntu-latest
156+ steps:
157+ - uses: actions/checkout@v6
158+ with:
159+ ref: ${{ github.event.inputs.tag || github.ref }}
160+
161+ - uses: bnema/gordon/.github/actions/deploy@main
162+ with:
163+ registry: registry.mydomain.com
164+ username: ${{ secrets.GORDON_USERNAME }}
165+ password: ${{ secrets.GORDON_TOKEN }}
166+```
167+
168+### Monorepo with Multiple Services
169+
170+```yaml
171+name: Deploy Services
172+
173+on:
174+ push:
175+ tags:
176+ - 'v*'
177+
178+jobs:
179+ deploy-api:
180+ runs-on: ubuntu-latest
181+ steps:
182+ - uses: actions/checkout@v6
183+ - uses: bnema/gordon/.github/actions/deploy@main
184+ with:
185+ registry: registry.mydomain.com
186+ username: ${{ secrets.GORDON_USERNAME }}
187+ password: ${{ secrets.GORDON_TOKEN }}
188+ image: myapp-api
189+ dockerfile: ./services/api/Dockerfile
190+ context: ./services/api
191+
192+ deploy-web:
193+ runs-on: ubuntu-latest
194+ steps:
195+ - uses: actions/checkout@v6
196+ - uses: bnema/gordon/.github/actions/deploy@main
197+ with:
198+ registry: registry.mydomain.com
199+ username: ${{ secrets.GORDON_USERNAME }}
200+ password: ${{ secrets.GORDON_TOKEN }}
201+ image: myapp-web
202+ dockerfile: ./services/web/Dockerfile
203+ context: ./services/web
204+```
205+
206+### Build with Secrets (BuildKit)
207+
208+```yaml
209+- uses: bnema/gordon/.github/actions/deploy@main
210+ with:
211+ registry: registry.mydomain.com
212+ username: ${{ secrets.GORDON_USERNAME }}
213+ password: ${{ secrets.GORDON_TOKEN }}
214+ build-args: |
215+ NPM_TOKEN=${{ secrets.NPM_TOKEN }}
216+```
217+
218+## Troubleshooting
219+
220+### Authentication Failed
221+
222+- Verify your token hasn't been revoked: `gordon auth token list`
223+- Ensure the token has `push` scope
224+- Check that `token_secret` matches between token generation and validation
225+
226+### Dockerfile Not Found
227+
228+- Verify the `dockerfile` input path is correct
229+- Paths are relative to the repository root
230+
231+### Push Failed
232+
233+- Ensure your Gordon server is accessible from GitHub Actions
234+- Check firewall rules allow connections from GitHub's IP ranges
235+- Verify the registry domain resolves correctly
236+
237+## Security Notes
238+
239+- Never commit tokens or passwords to your repository
240+- Use GitHub Secrets for all sensitive values
241+- Consider using short-lived tokens for production
242+- Each Gordon instance has unique tokens (tokens from one instance won't work on another)
@@ -0,0 +1,242 @@
1+# Gordon Deploy Action
2+
3+Build and push container images to a Gordon registry, with automatic deployment on push.
4+
5+## Features
6+
7+- Automatic image building from Dockerfile
8+- Tag-based versioning (uses git tag as image tag)
9+- Optional `:latest` tag pushing
10+- Multi-platform builds support
11+- Build caching with GitHub Actions cache
12+- Works with Gordon's token and password authentication
13+
14+## Usage
15+
16+### Basic Usage (Tag Push)
17+
18+```yaml
19+name: Deploy to Gordon
20+
21+on:
22+ push:
23+ tags:
24+ - 'v*'
25+
26+jobs:
27+ deploy:
28+ runs-on: ubuntu-latest
29+ steps:
30+ - uses: actions/checkout@v6
31+
32+ - name: Deploy to Gordon
33+ uses: bnema/gordon/.github/actions/deploy@main
34+ with:
35+ registry: registry.mydomain.com
36+ username: ${{ secrets.GORDON_USERNAME }}
37+ password: ${{ secrets.GORDON_TOKEN }}
38+```
39+
40+### Full Example with All Options
41+
42+```yaml
43+name: Deploy to Gordon
44+
45+on:
46+ push:
47+ tags:
48+ - 'v*'
49+ - 'release-*'
50+
51+jobs:
52+ deploy:
53+ runs-on: ubuntu-latest
54+ steps:
55+ - uses: actions/checkout@v6
56+
57+ - name: Deploy to Gordon
58+ id: deploy
59+ uses: bnema/gordon/.github/actions/deploy@main
60+ with:
61+ registry: registry.mydomain.com
62+ username: ${{ secrets.GORDON_USERNAME }}
63+ password: ${{ secrets.GORDON_TOKEN }}
64+ image: myapp # Custom image name
65+ dockerfile: ./docker/Dockerfile # Custom Dockerfile path
66+ context: . # Build context
67+ build-args: | # Build arguments
68+ NODE_ENV=production
69+ API_URL=https://api.example.com
70+ platforms: linux/amd64,linux/arm64 # Multi-platform
71+ push-latest: 'true' # Also push :latest
72+
73+ - name: Print outputs
74+ run: |
75+ echo "Image: ${{ steps.deploy.outputs.image }}"
76+ echo "Tag: ${{ steps.deploy.outputs.tag }}"
77+ echo "Digest: ${{ steps.deploy.outputs.digest }}"
78+```
79+
80+## Inputs
81+
82+| Input | Description | Required | Default |
83+|-------|-------------|----------|---------|
84+| `registry` | Gordon registry URL (e.g., `registry.mydomain.com`) | Yes | - |
85+| `username` | Registry username (token subject for token auth) | Yes | - |
86+| `password` | Registry password or token | Yes | - |
87+| `image` | Image name | No | Repository name |
88+| `dockerfile` | Path to Dockerfile | No | `./Dockerfile` |
89+| `context` | Build context path | No | `.` |
90+| `build-args` | Build arguments (one per line) | No | - |
91+| `platforms` | Target platforms | No | - |
92+| `push-latest` | Also push with `:latest` tag | No | `true` |
93+| `cache-from` | Cache source | No | `type=gha` |
94+| `cache-to` | Cache destination | No | `type=gha,mode=max` |
95+
96+## Outputs
97+
98+| Output | Description |
99+|--------|-------------|
100+| `image` | Full image reference that was pushed |
101+| `tag` | Tag that was pushed |
102+| `digest` | Image digest |
103+
104+## Authentication Setup
105+
106+### 1. Generate a Gordon Token
107+
108+On your Gordon server:
109+
110+```bash
111+# Generate a never-expiring token for CI
112+gordon auth token generate --subject github-actions --scopes push,pull --expiry 0
113+```
114+
115+### 2. Add GitHub Secrets
116+
117+In your repository settings, add:
118+
119+- `GORDON_USERNAME`: The token subject (e.g., `github-actions`)
120+- `GORDON_TOKEN`: The generated JWT token
121+
122+### 3. Configure Gordon
123+
124+Ensure your Gordon config uses token authentication:
125+
126+```toml
127+[secrets]
128+backend = "pass" # or "sops"
129+
130+[registry_auth]
131+enabled = true
132+type = "token"
133+token_secret = "gordon/registry/token_secret"
134+```
135+
136+## Examples
137+
138+### Deploy on Tag + Manual Trigger
139+
140+```yaml
141+name: Deploy
142+
143+on:
144+ push:
145+ tags:
146+ - 'v*'
147+ workflow_dispatch:
148+ inputs:
149+ tag:
150+ description: 'Tag to deploy'
151+ required: true
152+
153+jobs:
154+ deploy:
155+ runs-on: ubuntu-latest
156+ steps:
157+ - uses: actions/checkout@v6
158+ with:
159+ ref: ${{ github.event.inputs.tag || github.ref }}
160+
161+ - uses: bnema/gordon/.github/actions/deploy@main
162+ with:
163+ registry: registry.mydomain.com
164+ username: ${{ secrets.GORDON_USERNAME }}
165+ password: ${{ secrets.GORDON_TOKEN }}
166+```
167+
168+### Monorepo with Multiple Services
169+
170+```yaml
171+name: Deploy Services
172+
173+on:
174+ push:
175+ tags:
176+ - 'v*'
177+
178+jobs:
179+ deploy-api:
180+ runs-on: ubuntu-latest
181+ steps:
182+ - uses: actions/checkout@v6
183+ - uses: bnema/gordon/.github/actions/deploy@main
184+ with:
185+ registry: registry.mydomain.com
186+ username: ${{ secrets.GORDON_USERNAME }}
187+ password: ${{ secrets.GORDON_TOKEN }}
188+ image: myapp-api
189+ dockerfile: ./services/api/Dockerfile
190+ context: ./services/api
191+
192+ deploy-web:
193+ runs-on: ubuntu-latest
194+ steps:
195+ - uses: actions/checkout@v6
196+ - uses: bnema/gordon/.github/actions/deploy@main
197+ with:
198+ registry: registry.mydomain.com
199+ username: ${{ secrets.GORDON_USERNAME }}
200+ password: ${{ secrets.GORDON_TOKEN }}
201+ image: myapp-web
202+ dockerfile: ./services/web/Dockerfile
203+ context: ./services/web
204+```
205+
206+### Build with Secrets (BuildKit)
207+
208+```yaml
209+- uses: bnema/gordon/.github/actions/deploy@main
210+ with:
211+ registry: registry.mydomain.com
212+ username: ${{ secrets.GORDON_USERNAME }}
213+ password: ${{ secrets.GORDON_TOKEN }}
214+ build-args: |
215+ NPM_TOKEN=${{ secrets.NPM_TOKEN }}
216+```
217+
218+## Troubleshooting
219+
220+### Authentication Failed
221+
222+- Verify your token hasn't been revoked: `gordon auth token list`
223+- Ensure the token has `push` scope
224+- Check that `token_secret` matches between token generation and validation
225+
226+### Dockerfile Not Found
227+
228+- Verify the `dockerfile` input path is correct
229+- Paths are relative to the repository root
230+
231+### Push Failed
232+
233+- Ensure your Gordon server is accessible from GitHub Actions
234+- Check firewall rules allow connections from GitHub's IP ranges
235+- Verify the registry domain resolves correctly
236+
237+## Security Notes
238+
239+- Never commit tokens or passwords to your repository
240+- Use GitHub Secrets for all sensitive values
241+- Consider using short-lived tokens for production
242+- Each Gordon instance has unique tokens (tokens from one instance won't work on another)
A .github/actions/deploy/action.yml +140 -0
@@ -0,0 +1,140 @@
1+name: 'Gordon Deploy'
2+description: 'Build and push container images to a Gordon registry on tag push'
3+author: 'Gordon Contributors'
4+
5+branding:
6+ icon: 'upload-cloud'
7+ color: 'blue'
8+
9+inputs:
10+ registry:
11+ description: 'Gordon registry URL (e.g., registry.mydomain.com)'
12+ required: true
13+ username:
14+ description: 'Registry username (token subject for token auth)'
15+ required: true
16+ password:
17+ description: 'Registry password or token'
18+ required: true
19+ image:
20+ description: 'Image name (defaults to repository name)'
21+ required: false
22+ default: ''
23+ dockerfile:
24+ description: 'Path to Dockerfile (defaults to ./Dockerfile)'
25+ required: false
26+ default: './Dockerfile'
27+ context:
28+ description: 'Build context path (defaults to .)'
29+ required: false
30+ default: '.'
31+ build-args:
32+ description: 'Build arguments (one per line, KEY=value format)'
33+ required: false
34+ default: ''
35+ platforms:
36+ description: 'Target platforms (e.g., linux/amd64,linux/arm64)'
37+ required: false
38+ default: ''
39+ push-latest:
40+ description: 'Also push with :latest tag'
41+ required: false
42+ default: 'true'
43+ cache-from:
44+ description: 'Cache source (e.g., type=gha)'
45+ required: false
46+ default: 'type=gha'
47+ cache-to:
48+ description: 'Cache destination (e.g., type=gha,mode=max)'
49+ required: false
50+ default: 'type=gha,mode=max'
51+
52+outputs:
53+ image:
54+ description: 'Full image reference that was pushed'
55+ value: ${{ steps.meta.outputs.image }}
56+ tag:
57+ description: 'Tag that was pushed'
58+ value: ${{ steps.meta.outputs.tag }}
59+ digest:
60+ description: 'Image digest'
61+ value: ${{ steps.push.outputs.digest }}
62+
63+runs:
64+ using: 'composite'
65+ steps:
66+ - name: Validate inputs
67+ shell: bash
68+ run: |
69+ if [[ ! -f "${{ inputs.dockerfile }}" ]]; then
70+ echo "::error::Dockerfile not found at ${{ inputs.dockerfile }}"
71+ exit 1
72+ fi
73+
74+ - name: Extract metadata
75+ id: meta
76+ shell: bash
77+ run: |
78+ # Determine image name
79+ IMAGE_NAME="${{ inputs.image }}"
80+ if [[ -z "$IMAGE_NAME" ]]; then
81+ IMAGE_NAME="${GITHUB_REPOSITORY#*/}"
82+ fi
83+
84+ # Extract tag from ref
85+ if [[ "$GITHUB_REF" == refs/tags/* ]]; then
86+ TAG="${GITHUB_REF#refs/tags/}"
87+ else
88+ TAG="$(echo $GITHUB_SHA | head -c 7)"
89+ fi
90+
91+ # Full image reference
92+ FULL_IMAGE="${{ inputs.registry }}/${IMAGE_NAME}:${TAG}"
93+
94+ echo "image=${FULL_IMAGE}" >> $GITHUB_OUTPUT
95+ echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
96+ echo "tag=${TAG}" >> $GITHUB_OUTPUT
97+ echo "registry=${{ inputs.registry }}" >> $GITHUB_OUTPUT
98+
99+ echo "Building: ${FULL_IMAGE}"
100+
101+ - name: Set up Docker Buildx
102+ uses: docker/setup-buildx-action@v3
103+
104+ - name: Log in to Gordon registry
105+ uses: docker/login-action@v3
106+ with:
107+ registry: ${{ inputs.registry }}
108+ username: ${{ inputs.username }}
109+ password: ${{ inputs.password }}
110+
111+ - name: Build and push
112+ id: push
113+ uses: docker/build-push-action@v6
114+ env:
115+ DOCKER_BUILD_SUMMARY: false
116+ with:
117+ context: ${{ inputs.context }}
118+ file: ${{ inputs.dockerfile }}
119+ push: true
120+ tags: |
121+ ${{ steps.meta.outputs.image }}
122+ ${{ inputs.push-latest == 'true' && format('{0}/{1}:latest', inputs.registry, steps.meta.outputs.image_name) || '' }}
123+ build-args: ${{ inputs.build-args }}
124+ platforms: ${{ inputs.platforms }}
125+ cache-from: ${{ inputs.cache-from }}
126+ cache-to: ${{ inputs.cache-to }}
127+ provenance: false
128+
129+ - name: Summary
130+ shell: bash
131+ run: |
132+ echo "## Gordon Deploy Summary" >> $GITHUB_STEP_SUMMARY
133+ echo "" >> $GITHUB_STEP_SUMMARY
134+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
135+ echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
136+ echo "| Image | \`${{ steps.meta.outputs.image }}\` |" >> $GITHUB_STEP_SUMMARY
137+ echo "| Tag | \`${{ steps.meta.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
138+ echo "| Digest | \`${{ steps.push.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY
139+ echo "" >> $GITHUB_STEP_SUMMARY
140+ echo "Container deployed to Gordon registry and will be automatically deployed." >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,140 @@
1+name: 'Gordon Deploy'
2+description: 'Build and push container images to a Gordon registry on tag push'
3+author: 'Gordon Contributors'
4+
5+branding:
6+ icon: 'upload-cloud'
7+ color: 'blue'
8+
9+inputs:
10+ registry:
11+ description: 'Gordon registry URL (e.g., registry.mydomain.com)'
12+ required: true
13+ username:
14+ description: 'Registry username (token subject for token auth)'
15+ required: true
16+ password:
17+ description: 'Registry password or token'
18+ required: true
19+ image:
20+ description: 'Image name (defaults to repository name)'
21+ required: false
22+ default: ''
23+ dockerfile:
24+ description: 'Path to Dockerfile (defaults to ./Dockerfile)'
25+ required: false
26+ default: './Dockerfile'
27+ context:
28+ description: 'Build context path (defaults to .)'
29+ required: false
30+ default: '.'
31+ build-args:
32+ description: 'Build arguments (one per line, KEY=value format)'
33+ required: false
34+ default: ''
35+ platforms:
36+ description: 'Target platforms (e.g., linux/amd64,linux/arm64)'
37+ required: false
38+ default: ''
39+ push-latest:
40+ description: 'Also push with :latest tag'
41+ required: false
42+ default: 'true'
43+ cache-from:
44+ description: 'Cache source (e.g., type=gha)'
45+ required: false
46+ default: 'type=gha'
47+ cache-to:
48+ description: 'Cache destination (e.g., type=gha,mode=max)'
49+ required: false
50+ default: 'type=gha,mode=max'
51+
52+outputs:
53+ image:
54+ description: 'Full image reference that was pushed'
55+ value: ${{ steps.meta.outputs.image }}
56+ tag:
57+ description: 'Tag that was pushed'
58+ value: ${{ steps.meta.outputs.tag }}
59+ digest:
60+ description: 'Image digest'
61+ value: ${{ steps.push.outputs.digest }}
62+
63+runs:
64+ using: 'composite'
65+ steps:
66+ - name: Validate inputs
67+ shell: bash
68+ run: |
69+ if [[ ! -f "${{ inputs.dockerfile }}" ]]; then
70+ echo "::error::Dockerfile not found at ${{ inputs.dockerfile }}"
71+ exit 1
72+ fi
73+
74+ - name: Extract metadata
75+ id: meta
76+ shell: bash
77+ run: |
78+ # Determine image name
79+ IMAGE_NAME="${{ inputs.image }}"
80+ if [[ -z "$IMAGE_NAME" ]]; then
81+ IMAGE_NAME="${GITHUB_REPOSITORY#*/}"
82+ fi
83+
84+ # Extract tag from ref
85+ if [[ "$GITHUB_REF" == refs/tags/* ]]; then
86+ TAG="${GITHUB_REF#refs/tags/}"
87+ else
88+ TAG="$(echo $GITHUB_SHA | head -c 7)"
89+ fi
90+
91+ # Full image reference
92+ FULL_IMAGE="${{ inputs.registry }}/${IMAGE_NAME}:${TAG}"
93+
94+ echo "image=${FULL_IMAGE}" >> $GITHUB_OUTPUT
95+ echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
96+ echo "tag=${TAG}" >> $GITHUB_OUTPUT
97+ echo "registry=${{ inputs.registry }}" >> $GITHUB_OUTPUT
98+
99+ echo "Building: ${FULL_IMAGE}"
100+
101+ - name: Set up Docker Buildx
102+ uses: docker/setup-buildx-action@v3
103+
104+ - name: Log in to Gordon registry
105+ uses: docker/login-action@v3
106+ with:
107+ registry: ${{ inputs.registry }}
108+ username: ${{ inputs.username }}
109+ password: ${{ inputs.password }}
110+
111+ - name: Build and push
112+ id: push
113+ uses: docker/build-push-action@v6
114+ env:
115+ DOCKER_BUILD_SUMMARY: false
116+ with:
117+ context: ${{ inputs.context }}
118+ file: ${{ inputs.dockerfile }}
119+ push: true
120+ tags: |
121+ ${{ steps.meta.outputs.image }}
122+ ${{ inputs.push-latest == 'true' && format('{0}/{1}:latest', inputs.registry, steps.meta.outputs.image_name) || '' }}
123+ build-args: ${{ inputs.build-args }}
124+ platforms: ${{ inputs.platforms }}
125+ cache-from: ${{ inputs.cache-from }}
126+ cache-to: ${{ inputs.cache-to }}
127+ provenance: false
128+
129+ - name: Summary
130+ shell: bash
131+ run: |
132+ echo "## Gordon Deploy Summary" >> $GITHUB_STEP_SUMMARY
133+ echo "" >> $GITHUB_STEP_SUMMARY
134+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
135+ echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
136+ echo "| Image | \`${{ steps.meta.outputs.image }}\` |" >> $GITHUB_STEP_SUMMARY
137+ echo "| Tag | \`${{ steps.meta.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
138+ echo "| Digest | \`${{ steps.push.outputs.digest }}\` |" >> $GITHUB_STEP_SUMMARY
139+ echo "" >> $GITHUB_STEP_SUMMARY
140+ echo "Container deployed to Gordon registry and will be automatically deployed." >> $GITHUB_STEP_SUMMARY
A examples/github-workflow.yml +128 -0
@@ -0,0 +1,128 @@
1+# Example GitHub Actions workflow for deploying to Gordon
2+# Copy this file to your repository as .github/workflows/deploy.yml
3+#
4+# Prerequisites:
5+# 1. Generate a Gordon token on your server:
6+# gordon auth token generate --subject github-actions --scopes push,pull --expiry 0
7+#
8+# 2. Add these secrets to your GitHub repository:
9+# - GORDON_REGISTRY: Your registry URL (e.g., registry.mydomain.com)
10+# - GORDON_USERNAME: Token subject (e.g., github-actions)
11+# - GORDON_TOKEN: The generated JWT token
12+
13+name: Deploy to Gordon
14+
15+on:
16+ # Deploy on version tags
17+ push:
18+ tags:
19+ - 'v*' # Matches v1.0.0, v2.1.3, etc.
20+ - 'release-*' # Matches release-2024-01-15, etc.
21+
22+ # Optional: Allow manual deployments
23+ workflow_dispatch:
24+ inputs:
25+ tag:
26+ description: 'Tag/version to deploy (leave empty for latest commit)'
27+ required: false
28+ type: string
29+
30+jobs:
31+ deploy:
32+ runs-on: ubuntu-latest
33+
34+ # Optional: Only deploy from main branch for manual triggers
35+ # if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
36+
37+ steps:
38+ - name: Checkout code
39+ uses: actions/checkout@v6
40+ with:
41+ # For manual dispatch with specific tag
42+ ref: ${{ github.event.inputs.tag || github.ref }}
43+
44+ - name: Deploy to Gordon
45+ id: deploy
46+ uses: bnema/gordon/.github/actions/deploy@main
47+ with:
48+ registry: ${{ secrets.GORDON_REGISTRY }}
49+ username: ${{ secrets.GORDON_USERNAME }}
50+ password: ${{ secrets.GORDON_TOKEN }}
51+ # Optional customizations:
52+ # image: myapp # Custom image name (defaults to repo name)
53+ # dockerfile: ./Dockerfile # Custom Dockerfile path
54+ # context: . # Build context
55+ # push-latest: 'true' # Also push :latest tag
56+ # platforms: linux/amd64 # Target platform(s)
57+
58+ - name: Deployment summary
59+ run: |
60+ echo "## Deployment Complete" >> $GITHUB_STEP_SUMMARY
61+ echo "" >> $GITHUB_STEP_SUMMARY
62+ echo "**Image:** \`${{ steps.deploy.outputs.image }}\`" >> $GITHUB_STEP_SUMMARY
63+ echo "**Tag:** \`${{ steps.deploy.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
64+ echo "" >> $GITHUB_STEP_SUMMARY
65+ echo "Gordon will automatically deploy this image to your configured routes." >> $GITHUB_STEP_SUMMARY
66+
67+# ============================================================================
68+# ALTERNATIVE CONFIGURATIONS
69+# ============================================================================
70+
71+# --- Deploy on push to main (continuous deployment) ---
72+#
73+# on:
74+# push:
75+# branches:
76+# - main
77+#
78+# Then in the action, the image will be tagged with the commit SHA.
79+
80+# --- Deploy multiple services (monorepo) ---
81+#
82+# jobs:
83+# deploy-api:
84+# runs-on: ubuntu-latest
85+# steps:
86+# - uses: actions/checkout@v6
87+# - uses: bnema/gordon/.github/actions/deploy@main
88+# with:
89+# registry: ${{ secrets.GORDON_REGISTRY }}
90+# username: ${{ secrets.GORDON_USERNAME }}
91+# password: ${{ secrets.GORDON_TOKEN }}
92+# image: myapp-api
93+# dockerfile: ./services/api/Dockerfile
94+# context: ./services/api
95+#
96+# deploy-web:
97+# runs-on: ubuntu-latest
98+# steps:
99+# - uses: actions/checkout@v6
100+# - uses: bnema/gordon/.github/actions/deploy@main
101+# with:
102+# registry: ${{ secrets.GORDON_REGISTRY }}
103+# username: ${{ secrets.GORDON_USERNAME }}
104+# password: ${{ secrets.GORDON_TOKEN }}
105+# image: myapp-web
106+# dockerfile: ./services/web/Dockerfile
107+# context: ./services/web
108+
109+# --- Deploy with build arguments ---
110+#
111+# - uses: bnema/gordon/.github/actions/deploy@main
112+# with:
113+# registry: ${{ secrets.GORDON_REGISTRY }}
114+# username: ${{ secrets.GORDON_USERNAME }}
115+# password: ${{ secrets.GORDON_TOKEN }}
116+# build-args: |
117+# NODE_ENV=production
118+# API_URL=https://api.example.com
119+# BUILD_DATE=${{ github.event.head_commit.timestamp }}
120+
121+# --- Multi-platform builds ---
122+#
123+# - uses: bnema/gordon/.github/actions/deploy@main
124+# with:
125+# registry: ${{ secrets.GORDON_REGISTRY }}
126+# username: ${{ secrets.GORDON_USERNAME }}
127+# password: ${{ secrets.GORDON_TOKEN }}
128+# platforms: linux/amd64,linux/arm64
@@ -0,0 +1,128 @@
1+# Example GitHub Actions workflow for deploying to Gordon
2+# Copy this file to your repository as .github/workflows/deploy.yml
3+#
4+# Prerequisites:
5+# 1. Generate a Gordon token on your server:
6+# gordon auth token generate --subject github-actions --scopes push,pull --expiry 0
7+#
8+# 2. Add these secrets to your GitHub repository:
9+# - GORDON_REGISTRY: Your registry URL (e.g., registry.mydomain.com)
10+# - GORDON_USERNAME: Token subject (e.g., github-actions)
11+# - GORDON_TOKEN: The generated JWT token
12+
13+name: Deploy to Gordon
14+
15+on:
16+ # Deploy on version tags
17+ push:
18+ tags:
19+ - 'v*' # Matches v1.0.0, v2.1.3, etc.
20+ - 'release-*' # Matches release-2024-01-15, etc.
21+
22+ # Optional: Allow manual deployments
23+ workflow_dispatch:
24+ inputs:
25+ tag:
26+ description: 'Tag/version to deploy (leave empty for latest commit)'
27+ required: false
28+ type: string
29+
30+jobs:
31+ deploy:
32+ runs-on: ubuntu-latest
33+
34+ # Optional: Only deploy from main branch for manual triggers
35+ # if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
36+
37+ steps:
38+ - name: Checkout code
39+ uses: actions/checkout@v6
40+ with:
41+ # For manual dispatch with specific tag
42+ ref: ${{ github.event.inputs.tag || github.ref }}
43+
44+ - name: Deploy to Gordon
45+ id: deploy
46+ uses: bnema/gordon/.github/actions/deploy@main
47+ with:
48+ registry: ${{ secrets.GORDON_REGISTRY }}
49+ username: ${{ secrets.GORDON_USERNAME }}
50+ password: ${{ secrets.GORDON_TOKEN }}
51+ # Optional customizations:
52+ # image: myapp # Custom image name (defaults to repo name)
53+ # dockerfile: ./Dockerfile # Custom Dockerfile path
54+ # context: . # Build context
55+ # push-latest: 'true' # Also push :latest tag
56+ # platforms: linux/amd64 # Target platform(s)
57+
58+ - name: Deployment summary
59+ run: |
60+ echo "## Deployment Complete" >> $GITHUB_STEP_SUMMARY
61+ echo "" >> $GITHUB_STEP_SUMMARY
62+ echo "**Image:** \`${{ steps.deploy.outputs.image }}\`" >> $GITHUB_STEP_SUMMARY
63+ echo "**Tag:** \`${{ steps.deploy.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
64+ echo "" >> $GITHUB_STEP_SUMMARY
65+ echo "Gordon will automatically deploy this image to your configured routes." >> $GITHUB_STEP_SUMMARY
66+
67+# ============================================================================
68+# ALTERNATIVE CONFIGURATIONS
69+# ============================================================================
70+
71+# --- Deploy on push to main (continuous deployment) ---
72+#
73+# on:
74+# push:
75+# branches:
76+# - main
77+#
78+# Then in the action, the image will be tagged with the commit SHA.
79+
80+# --- Deploy multiple services (monorepo) ---
81+#
82+# jobs:
83+# deploy-api:
84+# runs-on: ubuntu-latest
85+# steps:
86+# - uses: actions/checkout@v6
87+# - uses: bnema/gordon/.github/actions/deploy@main
88+# with:
89+# registry: ${{ secrets.GORDON_REGISTRY }}
90+# username: ${{ secrets.GORDON_USERNAME }}
91+# password: ${{ secrets.GORDON_TOKEN }}
92+# image: myapp-api
93+# dockerfile: ./services/api/Dockerfile
94+# context: ./services/api
95+#
96+# deploy-web:
97+# runs-on: ubuntu-latest
98+# steps:
99+# - uses: actions/checkout@v6
100+# - uses: bnema/gordon/.github/actions/deploy@main
101+# with:
102+# registry: ${{ secrets.GORDON_REGISTRY }}
103+# username: ${{ secrets.GORDON_USERNAME }}
104+# password: ${{ secrets.GORDON_TOKEN }}
105+# image: myapp-web
106+# dockerfile: ./services/web/Dockerfile
107+# context: ./services/web
108+
109+# --- Deploy with build arguments ---
110+#
111+# - uses: bnema/gordon/.github/actions/deploy@main
112+# with:
113+# registry: ${{ secrets.GORDON_REGISTRY }}
114+# username: ${{ secrets.GORDON_USERNAME }}
115+# password: ${{ secrets.GORDON_TOKEN }}
116+# build-args: |
117+# NODE_ENV=production
118+# API_URL=https://api.example.com
119+# BUILD_DATE=${{ github.event.head_commit.timestamp }}
120+
121+# --- Multi-platform builds ---
122+#
123+# - uses: bnema/gordon/.github/actions/deploy@main
124+# with:
125+# registry: ${{ secrets.GORDON_REGISTRY }}
126+# username: ${{ secrets.GORDON_USERNAME }}
127+# password: ${{ secrets.GORDON_TOKEN }}
128+# platforms: linux/amd64,linux/arm64

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close