Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/skills/dev/git-workflow/open-pull-request/skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
---
name: open-pull-request
description: Open a pull request from a feature branch using GitHub CLI (preferred) or GitHub MCP tools. Covers pre-flight checks, correct base/head configuration for fork workflows, title/body conventions, and post-creation validation. Use when asked to "open PR", "create pull request", or "submit branch for review".
metadata:
author: torrust
version: "1.0"
---

# Open a Pull Request

This skill explains how to create a pull request for this repository in a repeatable way.

## CLI vs MCP decision rule

Use the tool that matches the loop:

- **Inner loop (fast local branch work):** prefer GitHub CLI (`gh`) because it is fast and low overhead.
- **Outer loop (cross-system coordination):** use MCP when you need structured/authenticated access across shared systems.

For opening a PR from the current local branch, prefer `gh pr create`.

## Pre-flight checks

Before opening a PR:

- [ ] Working tree is clean (`git status`)
- [ ] Branch is pushed to remote
- [ ] Commits are signed (`git log --show-signature -n 1`)
- [ ] Required checks have been run (`./scripts/pre-commit.sh`)

## Title and description convention

Use conventional commit style in the PR title when possible, including issue reference.

Examples:

- `ci: [#448] add crate publish workflow`
- `docs: [#448] define release process`

Include in PR body:

- Summary of changes
- Files/workflows touched
- Validation performed
- Issue link (`Closes #<issue-number>`)

## Option A (Preferred): GitHub CLI

### Same-repo branch

```bash
gh pr create \
--repo torrust/torrust-tracker-deployer \
--base main \
--head <branch-name> \
--title "<title>" \
--body "<body>"
```

### Fork branch (common maintainer flow)

```bash
gh pr create \
--repo torrust/torrust-tracker-deployer \
--base main \
--head <fork-owner>:<branch-name> \
--title "<title>" \
--body "<body>"
```

If successful, `gh` prints the PR URL.

## Option B: GitHub MCP tools

When MCP pull request management tools are available:

1. Create branch remotely if needed
2. Open PR with base `main` and correct head branch
3. Capture and share resulting PR URL

## Post-creation validation

After PR creation:

- [ ] Verify PR points to `torrust/torrust-tracker-deployer:main`
- [ ] Verify head branch is correct
- [ ] Confirm CI workflows started
- [ ] Confirm issue is linked in description

## Troubleshooting

- `fatal: ... does not appear to be a git repository`: push to correct remote (`git remote -v`)
- `A pull request already exists`: open existing PR URL instead of creating a new one
- Permission errors on upstream repo: create PR from your fork branch (`owner:branch`)

## References

- [`docs/contributing/commit-process.md`](../../../../../docs/contributing/commit-process.md)
- [`docs/contributing/pr-review-guide.md`](../../../../../docs/contributing/pr-review-guide.md)
- Existing branch skill: `.github/skills/dev/git-workflow/create-feature-branch/skill.md`
102 changes: 102 additions & 0 deletions .github/skills/dev/git-workflow/release-new-version/skill.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
name: release-new-version
description: Guide for releasing a new version of the deployer using the standard branch/tag workflow. Covers version bump, signed release commit, pushing main, creating signed tag, creating release branch, and verifying Docker + crate publication workflows. Use when asked to "release", "cut a version", "publish a new version", or "create release vX.Y.Z".
metadata:
author: torrust
version: "1.0"
---

# Release New Version

This skill provides the canonical workflow to release a new version of the Torrust Tracker Deployer.

Primary reference: [`docs/release-process.md`](../../../../../docs/release-process.md)

## Release Order (Mandatory)

Execute these steps in order:

1. Update versions in manifests
2. Create release commit
3. Push release commit to `main`
4. Create and push signed tag `vX.Y.Z`
5. Create and push release branch `releases/vX.Y.Z`
6. Verify release workflows
7. Create GitHub release

Do not reorder these steps.

## Version and Naming Rules

- Git tag: `vX.Y.Z`
- Release branch: `releases/vX.Y.Z`
- Docker release tag: `X.Y.Z` (no `v` prefix)
- Crate version: `X.Y.Z`

## Pre-Flight Checklist

Before starting:

- [ ] Clean working tree (`git status`)
- [ ] Up to date with `origin/main`
- [ ] GitHub environment `dockerhub-torrust` configured
- [ ] GitHub environment `crates-io` configured with `CARGO_REGISTRY_TOKEN`
- [ ] Releaser has permissions for `main`, tags, and release branches

## Commands

### 1) Update versions

Update `version` in:

- `Cargo.toml`
- `packages/sdk/Cargo.toml`

### 2) Commit and push

```bash
git add Cargo.toml packages/sdk/Cargo.toml
git commit -S -m "release: version vX.Y.Z"
git push origin main
```

### 3) Tag and release branch

```bash
git tag -s -a vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.Z

git checkout -b releases/vX.Y.Z
git push origin releases/vX.Y.Z
```

### 4) Verify workflows

- Container workflow: publishes Docker image from release branch
- Publish Crate workflow: publishes `torrust-tracker-deployer-sdk`

Workflow files:

- `.github/workflows/container.yaml`
- `.github/workflows/publish-crate.yaml`

### 5) Create GitHub release

Create the release manually from tag `vX.Y.Z` after both workflows pass.

## Failure Handling

- Docker failed, crate not started: fix Docker workflow and rerun on same release branch
- Docker passed, crate failed before upload: fix issue and rerun crate workflow on same release branch
- Crate already published: do not republish same version; cut a patch release
- Ref already exists (tag/branch): stop and investigate partial release state before continuing

## Quick Validation

```bash
# Verify refs exist remotely
git ls-remote --tags origin vX.Y.Z
git ls-remote --heads origin releases/vX.Y.Z
```

For full operational guidance, troubleshooting, and rollback/yank policy, use [`docs/release-process.md`](../../../../../docs/release-process.md).
107 changes: 53 additions & 54 deletions .github/workflows/container.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
# Following patterns from torrust/torrust-tracker container.yaml workflow.
#
# Triggers:
# - Push to main/develop branches
# - Push to main/develop/releases/** branches
# - Pull requests to main/develop
# - Manual dispatch
#
# Publishing:
# - Images are pushed to Docker Hub on push to main/develop (not PRs)
# - Requires Docker Hub credentials in repository secrets
# - Images are pushed to Docker Hub on push to main/develop/release branches (not PRs)
# - Release branches (releases/vX.Y.Z) publish versioned Docker tags (X.Y.Z)
# - Release Docker tags use bare semver without the v prefix
# - Requires Docker Hub credentials in the dockerhub-torrust GitHub Environment

name: Container

Expand All @@ -19,6 +21,7 @@ on:
branches:
- "develop"
- "main"
- "releases/**/*"
paths:
- "src/**"
- "Cargo.toml"
Expand Down Expand Up @@ -100,6 +103,7 @@ jobs:
outputs:
continue: ${{ steps.check.outputs.continue }}
type: ${{ steps.check.outputs.type }}
version: ${{ steps.check.outputs.version }}

steps:
- name: Check Context
Expand All @@ -108,10 +112,15 @@ jobs:
if [[ "${{ github.repository }}" == "torrust/torrust-tracker-deployer" ]]; then
if [[ "${{ github.event_name }}" == "push" ]]; then
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "type=production" >> $GITHUB_OUTPUT
echo "type=main" >> $GITHUB_OUTPUT
echo "continue=true" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/develop" ]]; then
echo "type=development" >> $GITHUB_OUTPUT
echo "type=develop" >> $GITHUB_OUTPUT
echo "continue=true" >> $GITHUB_OUTPUT
elif [[ $(echo "${{ github.ref }}" | grep -P '^refs/heads/releases/v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$') ]]; then
version=$(echo "${{ github.ref }}" | sed -n -E 's/^refs\/heads\/releases\///p')
echo "version=$version" >> $GITHUB_OUTPUT
echo "type=release" >> $GITHUB_OUTPUT
echo "continue=true" >> $GITHUB_OUTPUT
fi
fi
Expand All @@ -122,71 +131,54 @@ jobs:
echo "continue=false" >> $GITHUB_OUTPUT
fi

publish_development:
name: Publish (Development)
publish:
name: Publish (${{ needs.context.outputs.type }})
environment: dockerhub-torrust
needs: context
if: needs.context.outputs.continue == 'true' && needs.context.outputs.type == 'development'
if: needs.context.outputs.continue == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v5

- name: Docker Meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.DOCKER_HUB_USERNAME }}/tracker-deployer
tags: |
type=ref,event=branch
type=sha,prefix=dev-

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ env.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and Push
uses: docker/build-push-action@v6
with:
context: .
file: ./docker/deployer/Dockerfile
target: release
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

publish_production:
name: Publish (Production)
environment: dockerhub-torrust
needs: context
if: needs.context.outputs.continue == 'true' && needs.context.outputs.type == 'production'
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v5
- name: Configure Docker Tag Strategy
id: tag_config
run: |
if [[ "${{ needs.context.outputs.type }}" == "develop" ]]; then
{
echo "tags<<EOF"
echo "type=ref,event=branch"
echo "type=sha,prefix=dev-"
echo "EOF"
} >> "$GITHUB_OUTPUT"
elif [[ "${{ needs.context.outputs.type }}" == "main" ]]; then
{
echo "tags<<EOF"
echo "type=raw,value=latest"
echo "type=ref,event=branch"
echo "type=sha"
echo "EOF"
} >> "$GITHUB_OUTPUT"
elif [[ "${{ needs.context.outputs.type }}" == "release" ]]; then
{
echo "tags<<EOF"
echo "type=semver,value=${{ needs.context.outputs.version }},pattern={{version}}"
echo "EOF"
} >> "$GITHUB_OUTPUT"
else
echo "Unsupported publish type: ${{ needs.context.outputs.type }}" >&2
exit 1
fi

- name: Docker Meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.DOCKER_HUB_USERNAME }}/tracker-deployer
tags: |
type=raw,value=latest
type=ref,event=branch
type=sha
tags: ${{ steps.tag_config.outputs.tags }}

- name: Login to Docker Hub
uses: docker/login-action@v3
Expand All @@ -208,3 +200,10 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Inspect Published Image
if: needs.context.outputs.type == 'release'
run: |
version=$(echo "${{ needs.context.outputs.version }}" | sed 's/^v//')
docker pull ${{ env.DOCKER_HUB_USERNAME }}/tracker-deployer:"$version"
docker image inspect ${{ env.DOCKER_HUB_USERNAME }}/tracker-deployer:"$version"
Loading
Loading