I Automated 80% of My Open Source Maintenance with GitHub Actions — Here’s the Exact Setup

1 comment
(GitHub and Open Source) - Maintaining open source is exhausting. I automated releases, changelogs, dependency updates, and issue triage with GitHub Actions. Here's the exact YAML configs I use across 12 repos.

I Automated 80% of My Open Source Maintenance with GitHub Actions — Here’s the Exact Setup

I maintain 12 open source repos. Not huge ones — think a few thousand stars each, a handful of active contributors, and a steady stream of issues and PRs.

For years, I did everything manually. Cut a release? Tag it, write changelog notes, update the README, publish to npm/PyPI. Dependency updates? Open Dependabot PRs, review, merge, repeat. Issue triage? Read every new issue, add labels, assign priorities.

Build a Custom Multi-Agent Code Analysis Pipeline with ECOA AI Platform ACP: A Step-by-Step Developer Tutorial

Build a Custom Multi-Agent Code Analysis Pipeline with ECOA AI Platform ACP: A Step-by-Step Developer Tutorial

Build a Custom Multi-Agent Code Analysis Pipeline with ECOA AI Platform ACP: A Step-by-Step Developer Tutorial You’ve got… ...

It was killing my weekends. Honestly, it made me resent projects I once loved.

So I automated it. Not with some third-party SaaS tool. Just plain GitHub Actions, a few community actions, and some shell scripts. Here’s the exact stack I use across all my repos today.

Why You Should Hire Vietnamese Developers: A CTO’s Guide to Offshore Success

Why You Should Hire Vietnamese Developers: A CTO’s Guide to Offshore Success

4. What time zone is best for collaboration with Vietnam? If your team is in the US, the… ...

The Maintenance Tax Is Real

Let’s be blunt: open source maintenance is unpaid labor. You’re doing it because you care about the project, not because someone’s paying you. But when maintenance takes 5-10 hours a week per repo, that math stops working.

I’ve seen great projects die because the maintainer burned out. They didn’t abandon the code — they abandoned the *process*.

So I asked myself a simple question: *Which parts of this can a machine do better than me?*

Turns out, most of it.

My GitHub Actions Automation Stack

Here’s what I automated and the actions I use for each:

Task GitHub Action / Tool Time Saved
Automated releases `release-drafter/release-drafter` ~45 min/release
Changelog generation `release-drafter` + custom template ~30 min/release
Dependency updates `dependabot` + `auto-merge` ~2 hrs/week
Issue triage `actions/stale` + labeler ~3 hrs/week
CI/CD testing Standard `actions/checkout` + test runners ~1 hr/release
Auto-merge PRs `pascalgn/automerge-action` ~1 hr/week

Total: roughly 7-8 hours saved per week per repo. That’s a full working day. Every week.

1. Automated Releases with Release Drafter

This is the biggest time-saver. Instead of manually drafting release notes, I use `release-drafter`. It watches for PRs merged to `main` and automatically builds a changelog based on labels.

Here’s my `.github/release-drafter.yml`:

yaml
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
categories:
  - title: '🚀 Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
  - title: '🧰 Maintenance'
    labels:
      - 'chore'
      - 'dependencies'
      - 'refactor'
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
change-title-escapes: '\<*_&'
version-resolver:
  major:
    labels:
      - 'major'
  minor:
    labels:
      - 'minor'
  patch:
    labels:
      - 'patch'
  default: patch
template: |
  ## What's Changed

  $CHANGES

  **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION

And the workflow that triggers it:

yaml
# .github/workflows/release-drafter.yml
name: Release Drafter

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, reopened, synchronize]

permissions:
  contents: read

jobs:
  update_release_draft:
    permissions:
      contents: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: release-drafter/release-drafter@v6
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

That's it. Every time a PR with the `feature` label merges, it goes under "Features". Bug fixes? Same thing. When I'm ready to ship, I just publish the draft. No copy-pasting, no forgetting to mention a contributor.

2. Changelog That Doesn't Suck

I used to hand-write changelogs. They were inconsistent. Sometimes I'd forget a PR. Sometimes I'd miss crediting a first-time contributor.

Release Drafter solves this, but I also added a custom template that includes a "New Contributors" section. That matters more than you think — first-time contributors are more likely to come back if they see their name in the release notes.

I added this to the template section:

yaml
template: |
  ## What's Changed

  $CHANGES

  ## New Contributors
  
  $CONTRIBUTORS
  
  **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION

It automatically detects first-time contributors from the PR data. Zero effort on my part.

3. Dependency Updates That Merge Themselves

Dependabot is great, but it generates noise. Every week, 5-10 PRs for patch updates. Reviewing each one is a waste of time for low-risk dependencies.

I configured Dependabot to auto-merge patch and minor updates if CI passes. Here's the setup:

yaml
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 10
    labels:
      - "dependencies"
      - "auto-merge"

And the auto-merge workflow:

yaml
# .github/workflows/auto-merge.yml
name: Auto-merge Dependencies

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: write
  pull-requests: write

jobs:
  auto-merge:
    if: contains(github.event.pull_request.labels.*.name, 'auto-merge')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Enable auto-merge
        run: |
          gh pr merge --auto --squash "$PR_URL"
        env:
          PR_URL: ${{ github.event.pull_request.html_url }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Major updates still get manual review. But patch and minor? They merge automatically as long as tests pass. I've been running this for 8 months across 12 repos. Zero incidents.

4. Issue Triage That Never Sleeps

New issues come in at all hours. I'm in Ho Chi Minh City, but contributors are in San Francisco, Berlin, Tokyo. I can't be awake for all of them.

I use a labeler action that automatically categorizes issues based on keywords, plus a stale action that closes inactive issues after 60 days.

yaml
# .github/workflows/issue-triage.yml
name: Issue Triage

on:
  issues:
    types: [opened]

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/github-script@v7
        with:
          script: |
            const title = context.payload.issue.title.toLowerCase()
            const body = context.payload.issue.body.toLowerCase()
            
            if (title.includes('bug') || body.includes('reproduce')) {
              await github.rest.issues.addLabels({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.payload.issue.number,
                labels: ['bug', 'needs-triage']
              })
            } else if (title.includes('feature') || title.includes('request')) {
              await github.rest.issues.addLabels({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.payload.issue.number,
                labels: ['enhancement']
              })
            } else if (body.includes('how') || body.includes('question')) {
              await github.rest.issues.addLabels({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.payload.issue.number,
                labels: ['question']
              })
            }

And the stale action:

yaml
# .github/workflows/stale.yml
name: Close Stale Issues

on:
  schedule:
    - cron: '0 0 * * 1'

jobs:
  stale:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          stale-issue-message: 'This issue has been inactive for 60 days. Please update or it will be closed.'
          days-before-stale: 60
          days-before-close: 14
          stale-issue-label: 'stale'
          exempt-issue-labels: 'bug,enhancement,priority'

Now, when I wake up in Ho Chi Minh City, issues are already labeled. I can focus on the ones that matter instead of sorting through the noise.

5. The CI/CD That Catches Everything

This one's standard, but I'll include it because it's the foundation. Every PR must pass linting, tests, and type checking before it can merge.

yaml
# .github/workflows/ci.yml
name: CI

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18.x, 20.x, 22.x]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test -- --coverage
      - uses: codecov/codecov-action@v4

Three Node versions, lint, type check, test, coverage. If any of these fail, the PR doesn't merge. Non-negotiable.

What I Still Do Manually

I'm not saying automation replaces everything. Here's what I still do by hand:

  • Reviewing complex PRs — especially logic changes or architectural decisions
  • Responding to nuanced issues — the ones that need context or design discussion
  • Community management — thanking contributors, writing docs, fostering culture
  • Roadmap decisions — what to build next, what to deprecate

But those are the *fun* parts of maintenance. The parts that actually need a human.

Why This Matters for Teams

Here's the thing: if you're running a remote team — say, you've hired developers in Vietnam through a platform like ECOAAI — this automation isn't just nice to have. It's how you scale without burning out your senior engineers.

I've seen teams where senior devs spend 30

Related: software outsourcing Vietnam — Learn more about how ECOA AI can help your team.

Related: offshore team in Vietnam — Learn more about how ECOA AI can help your team.

Related: Vietnam outsourcing — Learn more about how ECOA AI can help your team.

Related reading: Why Smart CTOs Hire Vietnamese Developers: A Data-Driven Guide to Offshore Engineering

Leave a Comment

Your email address will not be published. Required fields are marked *

Ready to Build with AI-Powered Developers?

Hire Vietnamese engineers augmented by ECOA AI Platform + Claude Code. 5x faster, 40% cheaper.