Thuta Learning
ProjectsDevOps & Toolsintermediate

Project: A Professional Release, Start to Finish

What you'll walk away with

  • Explain the core ideas behind Project: A Professional Release, Start to Finish
  • Read the diagram and trace how state or data flows through the Git/GitHub workflow
  • Decide how this applies to your own project or team

Build the mental model

This capstone chains together nearly everything the last two chapters covered into the single pipeline professional teams actually run, start to finish.

  • Issue -- captures why the work matters before code exists
  • Branch -- isolates the work off main until it's ready
  • Commits -- build the change up in small, reviewable steps
  • Pull request -- makes the change visible to the team
  • CI -- an automated, objective first check
  • Code review -- human judgment a machine can't provide
  • Merge (--no-ff) -- lands the change, preserving branch history
  • Tag + Release -- names and ships the exact state that shipped

Merging with --no-ff instead of letting Git fast-forward keeps the feature branch's commits visible as a distinct unit inside main, rather than flattening the work into one anonymous change.

StageWhy it exists
IssueRecords intent and context before any code is written
CIObjective, automated check before a human spends review time
Code reviewHuman judgment on correctness, style, and missed edge cases
Tag + ReleaseA permanent, meaningful name for exactly what shipped

Semantic Versioning: MAJOR.MINOR.PATCH

Bump MAJOR for breaking changes, MINOR for backward-compatible new features (like this lesson's farewell() function), and PATCH for backward-compatible bug fixes.

text
ISSUE TO RELEASE: THE FULL PROFESSIONAL PIPELINE
------------------------------------------------
1. ISSUE          Someone opens issue #42 on GitHub describing
   (GitHub)        the needed change. No local git involved.
        |
        v
2. BRANCH         git checkout -b feature/add-farewell-function
   (local)         Isolates the work away from main.
        |
        v
3. COMMITS        git commit  (pinned author + timestamp, x2)
   (local)         Small, focused, well-described changes.
        |
        v
4. PUSH           git push -u origin feature/add-farewell-function
   (local)         Branch now exists on the shared/GitHub repo.
        |
        v
5. PULL REQUEST   Open a PR: feature-branch -> main (GitHub website)
   (GitHub)
        |
        v
6. CI             GitHub Actions runs the test suite automatically
   (GitHub)         on every push to the PR. Pass/fail shown inline.
        |
        v
7. CODE REVIEW    A teammate reads the diff, comments, approves
   (GitHub)         (or requests changes -- loop back to step 3).
        |
        v
8. MERGE          git merge --no-ff feature/add-farewell-function
   (local)          Keeps the branch's commits visible in history.
        |
        v
9. TAG            git tag -a v1.1.0 -m "Release v1.1.0 ..."
   (local)          Semantic version: MAJOR.MINOR.PATCH.
        |
        v
10. RELEASE       git push origin v1.1.0, then "Draft a new release"
    (GitHub)        from that tag, with release notes, published.

Connect it to a real scenario

Open an issue (GitHub)

Someone opens issue #42 describing the needed feature. There's no local git equivalent -- issues live only on GitHub's servers.

Branch for the work

git checkout -b feature/add-farewell-function, named for the issue it addresses.

Commit in small, pinned steps

Make the change across two focused commits, each with a real pinned author and timestamp, then push the branch.

Open a pull request (GitHub)

Open a PR from your branch into main, describing the change and referencing issue #42.

CI runs automatically (GitHub)

Pushing the branch triggers GitHub Actions to run the test suite, reporting pass or fail directly on the pull request.

Code review (GitHub)

A teammate reads the diff, leaves comments, and approves it -- or requests changes, sending you back to step 3.

Merge with --no-ff

Once CI is green and review is approved, git merge --no-ff feature/add-farewell-function keeps the branch's commits visible in main's history.

Tag the release

git tag -a v1.1.0 on the merge commit, following semantic versioning, then push the tag.

Publish the release (GitHub)

On the Releases page, choose tag v1.1.0, write release notes, and publish -- this is the moment the release becomes something users can find and download.

Try the working example

bash
git init --bare /tmp/project.git
git clone /tmp/project.git /tmp/work
cd /tmp/work

export GIT_AUTHOR_NAME="Thuta Learner"
export GIT_AUTHOR_EMAIL="learner@example.com"
export GIT_COMMITTER_NAME="Thuta Learner"
export GIT_COMMITTER_EMAIL="learner@example.com"
export GIT_AUTHOR_DATE="2026-03-01T09:00:00"
export GIT_COMMITTER_DATE="2026-03-01T09:00:00"

cat > app.py <<'EOF'
def greet(name):
    return f"Hello, {name}!"

if __name__ == "__main__":
    print(greet("World"))
EOF
git add app.py
git commit -m "Initial commit: v1.0.0 baseline app"
git push origin main

# 1. Issue #42 opened on GitHub: "Add a farewell() function" (web UI only)

# 2. Branch, isolated for this issue's work
git checkout -b feature/add-farewell-function

# 3. Two focused, pinned commits
export GIT_AUTHOR_DATE="2026-03-01T10:00:00"
export GIT_COMMITTER_DATE="2026-03-01T10:00:00"
cat >> app.py <<'EOF'

def farewell(name):
    return f"Goodbye, {name}!"
EOF
git add app.py
git commit -m "Add farewell() function (closes #42)"

export GIT_AUTHOR_DATE="2026-03-01T11:00:00"
export GIT_COMMITTER_DATE="2026-03-01T11:00:00"
cat >> app.py <<'EOF'

if __name__ == "__main__":
    print(farewell("World"))
EOF
git add app.py
git commit -m "Call farewell() in main block for demo output"

# 4. Push the branch -- this is what triggers CI and lets you open a PR
git push -u origin feature/add-farewell-function

# 5. PR opened on GitHub: feature/add-farewell-function -> main (web UI)
# 6. CI runs the test suite on the PR automatically (GitHub Actions)
# 7. A teammate reviews the diff and approves it (GitHub web UI)

# 8. Merge, preserving the feature branch as a visible unit in history
git checkout main
export GIT_AUTHOR_DATE="2026-03-01T14:00:00"
export GIT_COMMITTER_DATE="2026-03-01T14:00:00"
git merge --no-ff feature/add-farewell-function -m "Merge pull request #7 from feature/add-farewell-function

Add farewell() function (closes #42)"
git push origin main

# 9. Tag the release commit with a semantic version
export GIT_AUTHOR_DATE="2026-03-01T14:15:00"
export GIT_COMMITTER_DATE="2026-03-01T14:15:00"
git tag -a v1.1.0 -m "Release v1.1.0: add farewell() function"
git push origin v1.1.0

# 10. On GitHub: Releases -> Draft a new release -> choose tag v1.1.0 ->
#     write release notes -> Publish release (web UI)
You should see
The git-mechanics half of this pipeline produces a fully reproducible history. git log --oneline --graph on main after the merge shows:

*   c9c7c8c Merge pull request #7 from feature/add-farewell-function
|\
| * a4f106a Call farewell() in main block for demo output
| * c3aea86 Add farewell() function (closes #42)
|/
* 4a2cea7 Initial commit: v1.0.0 baseline app

Pushing main after the merge reports:

   4a2cea7..c9c7c8c  main -> main

Pushing the tag reports:

 * [new tag]         v1.1.0 -> v1.1.0

And git show v1.1.0 --stat confirms the tag points at the merge commit, with the tagger identity and message intact:

tag v1.1.0
Tagger: Thuta Learner <learner@example.com>
Release v1.1.0: add farewell() function
commit c9c7c8c...
Merge: 4a2cea7 a4f106a

On GitHub itself, the issue, pull request, CI check runs, review comments, and the published Release page are real UI elements you would see and click through -- but they have no terminal output to capture, since they exist only as records on GitHub's servers.

5-minute try-it

Open a second, smaller issue (#43): 'Bump the version wherever it's hardcoded.' Branch off main (which already contains v1.1.0's merge commit), add a VERSION file containing 1.1.1, commit, merge with --no-ff again, and tag the result v1.1.1, following semantic versioning's rule that a small addition with no breaking changes only bumps the patch number. Confirm with git tag --list that both v1.1.0 and v1.1.1 now exist.

One important caution

Tagging and releasing main before CI has actually passed and the pull request has been reviewed, shipping a release built on unverified code.

Reusing or moving a published tag (like re-tagging v1.1.0 after finding a bug) instead of bumping to a new version -- published tags should be treated as permanent and immutable.

GitHub Docs: Managing releases in a repositoryGit & GitHub

Easy traps

  • Tagging and releasing main before CI has actually passed and the pull request has been reviewed, shipping a release built on unverified code.
  • Reusing or moving a published tag (like re-tagging v1.1.0 after finding a bug) instead of bumping to a new version -- published tags should be treated as permanent and immutable.
  • Always run `git status` before any destructive or history-rewriting command, to know exactly what state you're in.

Exercise

Open a second, smaller issue (#43): 'Bump the version wherever it's hardcoded.' Branch off main (which already contains v1.1.0's merge commit), add a VERSION file containing 1.1.1, commit, merge with --no-ff again, and tag the result v1.1.1, following semantic versioning's rule that a small addition with no breaking changes only bumps the patch number. Confirm with git tag --list that both v1.1.0 and v1.1.1 now exist.

You'll know it worked when: The git-mechanics half of this pipeline produces a fully reproducible history. git log --oneline --graph on main after the merge shows: * c9c7c8c Merge pull request #7 from feature/add-farewell-function |\ | * a4f106a Call farewell() in main block for demo output | * c3aea86 Add farewell() function (closes #42) |/ * 4a2cea7 Initial commit: v1.0.0 baseline app Pushing main after the merge reports: 4a2cea7..c9c7c8c main -> main Pushing the tag reports: * [new tag] v1.1.0 -> v1.1.0 And git show v1.1.0 --stat confirms the tag points at the merge commit, with the tagger identity and message intact: tag v1.1.0 Tagger: Thuta Learner <learner@example.com> Release v1.1.0: add farewell() function commit c9c7c8c... Merge: 4a2cea7 a4f106a On GitHub itself, the issue, pull request, CI check runs, review comments, and the published Release page are real UI elements you would see and click through -- but they have no terminal output to capture, since they exist only as records on GitHub's servers.

Project: A Professional Release, Start to Finish | Thuta Learning