RFC: Discourse를 위한 새로운 버전 관리 전략

Discourse에 새로운 버전 관리 시스템을 도입할 계획입니다. 커뮤니티 관리자에게 더 많은 선택지와 예측 가능성을 제공하되, 개발 속도는 유지하는 것이 목표입니다. 또한 다른 소프트웨어와 더 잘 조화되도록 일부 용어를 조정하고 있습니다.

이 문서는 댓글을 받고, 시스템 구현을 시작하며, 새로운 릴리스 스트림의 사용을 확장하면서 진화할 것입니다.

현재 단계에서 의견이나 제안이 있으시면 이 주제에 답글을 남겨 알려주세요!


목표

  1. 개발 속도와 안정성 사이의 균형을 제공하는 더 규칙적인 '릴리스’를 Discourse에 도입합니다

  2. 약 6개월 간격으로 제공되며, 장기간 지원되는 릴리스를 계속 제공합니다

  3. 일반 릴리스와 확장 지원 릴리스의 지원 기간이 겹도록 하여, 관리자가 업데이트 시점에 대해 더 유연성을 가질 수 있도록 하면서도 중요한 보안 업데이트를 계속 받을 수 있도록 합니다

  4. '릴리스’를 둘러싼 절차를 최소한으로 유지합니다. 가능한 한 많은 부분을 자동화하고, 핵심 개발자 경험을 늦추지 않아야 합니다. ESR 릴리스는 다른 어떤 릴리스와도 동일합니다.

  5. 개발자와 최종 사용자에게 설명하기 쉽게 하기 위해 명명 규칙과 절차가 업계 표준과 일치해야 합니다

고수준 개요

  • 약 월 1회 릴리스를 수행합니다. ‘메이저’ 버전은 현재 연도이며, 각 릴리스마다 ‘마이너’ 버전이 증가합니다. 백포트된 수정 사항이 있을 경우 패치 버전 번호가 증가합니다

    예: 2026년의 첫 번째 릴리스는 v2026.0이고, 그 다음은 v2026.1 등이 됩니다

    릴리스는 두 개의 완전한 릴리스 주기에 걸쳐 중요한 수정 사항을 받습니다. 예: 2026.0의 지원은 2026.2가 릴리스될 때까지 계속됩니다.

  • 약 6개월마다 해당 릴리스 중 하나를 확장 지원 릴리스(ESR)로 선언합니다. ESR 버전은 다음 ESR이 선언된 후 2개의 릴리스 동안 지원됩니다.

    예: v2026.0이 ESR이고 v2026.6이 다음 ESR이라면, v2026.0의 지원은 v2026.8이 릴리스될 때 종료됩니다. 월간 주기를 가정하면, 이는 ESR 지원 기간의 2개월 겹침을 의미합니다.

  • latest(최신 릴리스), 이전 릴리스, 그리고 활성 ESR 버전에 대해 중요한 수정 사항을 제공합니다.

  • tests-passed 분기를 latest로 이름 변경합니다

1년간의 지원 기간에 대한 예시 그래프:

gantt
    title Discourse Releases and Support Periods (Jan 2026 – Jan 2027)
    dateFormat  YYYY-MM-DD
    axisFormat  %b %Y

    2026.0 (ESR) :active, 2026-01-27, 2026-09-29
    2026.1 :done, 2026-02-24, 2026-04-28
    2026.2 :done, 2026-03-31, 2026-05-26
    2026.3 :done, 2026-04-28, 2026-06-30
    2026.4 :done, 2026-05-26, 2026-07-28
    2026.5 :done, 2026-06-30, 2026-08-25
    2026.6 (ESR) :active, 2026-07-28, 2027-01-26
    2026.7 :done, 2026-08-25, 2026-10-27
    2026.8 :done, 2026-09-29, 2026-11-24
    2026.9 :done, 2026-10-27, 2026-12-29
    2026.10 :done, 2026-11-24, 2027-01-26
    2026.11 :done, 2026-12-29, 2027-01-26

구현

  • 각 릴리스는 latest에서 분기됩니다. 이러한 분기는 네임스페이스가 지정되고 무기한 보존됩니다. 예를 들어, v2026.1에는 release/2026.1이라는 이름의 분기가 있습니다

  • 각 패치 릴리스는 태그가 지정됩니다. 예: v2026.1.0, v2026.1.1

  • 최신 릴리스는 release 태그가 지정됩니다. 최신 ESR은 esr 태그가 지정됩니다.

  • 이전 릴리스는 release-previous 태그가 지정됩니다. 이전 활성 ESR(있는 경우)은 esr-previous 태그가 지정됩니다.

  • 후방 호환성을 위해, 기존 릴리스 스트림과 일치하는 태그는 가장 가까운 새로운 동등한 것으로 별칭됩니다. stableesr. betarelease. tests-passedlatest.

    이들은 비권장(deprecated)으로 간주되며, 우리는 미래에 일부 또는 전부를 제거하는 것을 목표로 할 것입니다. 특히 'beta’는 Discourse가 프로덕션 준비가 되어 있지 않다는 인상을 주기 때문에 문제적입니다.

  • latest에서 버전 번호는 현재 개발 중인 버전으로, -latest 접미사가 붙습니다. 예: 2026.3.0-latest

자동화된 릴리스 프로세스

매월, GitHub 액션이 mainversion.rb을 다음 -latest 버전으로 증가시키는 단일 커밋을 포함하는 새 PR을 엽니다.

사람이 PR을 병합하면, 다른 GitHub 액션이 main이 다음 -latest로 이동하는 것을 감지하여 완료된 릴리스를 위한 분기를 생성합니다. 본질적으로, 이 분기는 '릴리스 후보’가 됩니다. -latest 접미사를 version.rb에서 제거하여 릴리스를 '완료’하는 업데이트를 포함하는 릴리스 분기 대상의 또 다른 자동화된 PR이 열립니다.

보통, 우리는 이 두 PR을 연속적으로 병합합니다. 그러나 릴리스 생성과 완료를 위한 별도의 PR을 가짐으로써, 완료 전에 분기의 문제를 처리할 수 있는 옵션을 제공합니다.

    %%{init: { 'logLevel': 'debug', 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchOrder': 2}} }%%
    gitGraph
       checkout main
       commit id:'version v2026.1-latest'
       commit id:'...'
       commit id:'....'
       branch 'release/2026.1'
       commit id:'version 2026.1'
       checkout 'main'
       commit id:'version v2026.2-latest'

별도로, 다른 GitHub 액션 워크플로우는 릴리스 분기로의 백포트된 커밋을 감시합니다. 발견되면, 해당 분기의 패치 버전을 증가시키는 새 PR이 생성됩니다. 사람은 이러한 PR을 언제 병합할지 결정할 수 있습니다.

이 모든 자동화는 다양한 태그(release, release-previous, esr, esr-previous, 그리고 후방 호환성 별칭)를 최신 상태로 유지합니다.

보안 수정 사항

보안 수정 사항 워크플로우는 크게 동일하지만, 이제 다음 세 곳 중 두 곳에서 수정 사항을 수행해야 합니다:

  • latest

  • esr

  • esr-previous :new_button:

  • release :new_button:

  • release-previous :new_button:

(이전 일러스트레이션에 따르면, esr-previous가 지원되고, 새로운 esrrelease 또는 release-previous와 같으며, 더 이상 그렇지 않을 때 esr-previous에 대한 지원을 중단하기 때문에 세 곳 중 두 곳만 해당됩니다.)

latest에 보안 수정 사항을 도입할 때, latest-security-fix 태그가 자동으로 해당 커밋으로 이동합니다. docker_manager는 이 태그를 모니터링하도록 업데이트되어 관리자에게 업데이트를 촉구합니다. 이를 통해 버전 증가를 서두르지 않고도 보안 수정 사항을 릴리스하고 통보할 수 있습니다.

번역

현재, stabletests-passed 분기는 CrowdIn에서 번역할 수 있으며, 그 결과는 정기적으로 통합됩니다. 새로운 시스템에서, 우리는 초기에 latestrelease가 CrowdIn에서 번역 가능하도록 계획하고 있습니다.

이상적으로는, releaserelease-previous 또는 esr이 될 때쯤 번역이 안정화되어야 합니다. 이러한 버전의 연속적인 번역에 대한 수요가 있다면, 그것은 미래에 고려될 수 있는 것입니다.

플러그인/테마 호환성

Discourse의 latest가 아닌 스트림의 사용을 증가시키면 discourse-compatibility 시스템에 대한 의존성이 증가합니다. 따라서 호환성 시스템에 대한 몇 가지 개선이 필요합니다.

main.discourse-compatibility 파일을 사용하는 대신, 특별히 이름이 지정된 분기/태그에 기반한 암시적 호환성을 지원할 수 있습니다. 이는 커밋 해시를 수동으로 관리하는 것보다 훨씬 쉬워야 합니다. 예를 들어, 플러그인에는 다음과 같은 분기가 있을 수 있습니다

  • d-compat/v2026.1
  • d-compat/v2026.2
  • d-compat/v2026.3
  • main (자신의 분기가 없는 모든 Discourse 버전에 사용됨)

플러그인을 설치할 때, Discourse는 현재 버전과 일치하는 분기를 확인할 수 있습니다. 존재하면, 그 분기를 체크아웃합니다. 그렇지 않으면, .discourse-compatibility 파일을 확인합니다. 그렇지 않으면, 기본 분기를 체크아웃합니다.

각 테마/플러그인에서 매일 실행되어 새로운 Discourse 릴리스를 확인하고 이러한 분기를 자동으로 생성하는 공개 GitHub 액션을 만들 수 있습니다. 각 테마/플러그인은 이 자동 고정(auto-pinning) 액션을 사용할지, 또는 더 ‘플로팅’ 전략을 취할지 선택할 수 있습니다.

discourse.org 호스팅

초기에는, 우리의 호스팅 제공은 Discourse의 latest 버전을 계속 실행할 것입니다. 미래에는, 엔터프라이즈 등급 고객이 ‘릴리스’ 버전을 선택할 수 있는 옵션을 탐색할 것입니다.

표준 설치 기본값

초기에는, 기본값은 latest로 유지됩니다. 관리자는 현재 stable에 옵트인하는 것과 동일한 방식으로 새로운 릴리스 스트림에 옵트인할 수 있습니다. 시스템이 더 성숙해지면, 우리는 미래에 릴리스 스트림 간에 더 쉬운 전환을 탐색할 수 있습니다.

조금 혼란스럽습니다. 즉, tests-passed라고도 불리는 latest(기본값이라고 가정합니다)는 여전히 최신 상태(가장 빈번한 업데이트를 받는 것; 이 부분은 변함없음)를 유지하지만, 현재 beta 브랜치였던 것이 이제 release 브랜치가 되면서 이전과 같은 방식으로 동작한다는 뜻인가요? 즉, 커밋/업데이트의 "그룹"을 형성한 다음, ESR(즉, stable) 브랜치는 beta/release 업데이트들의 더 큰 "그룹"이 되는 것인가요?

이 말은 release가 이제 기본 옵션이 된다는 뜻인가요, 아니면 '최신 릴리스’라고 할 때 'release 브랜치*의 최신 업데이트’를 가리키는 것인가요? 그렇다면 latest와의 차이가 있을까요?

감사합니다!

beta는 현재 태그이며, 백포트된 수정 사항이 적용되지 않습니다.

이 제안에 따르면, 각 버전별 릴리스에는 고유한 브랜치가 생성되며, 해당 버전이 “지원”되는 동안 보안 수정 사항이 적용됩니다. 사용자는 특정 버전 번호를 지정하여 설치하도록 설정한 후, 다른 릴리스가 출시된 후에도 계속 사용할 수 있습니다. 이는 현재 beta 또는 stable에서는 불가능합니다.

release는 보안 수정 사항을 위해 최신 릴리스(패치 릴리스 포함)를 따르는 태그가 됩니다.

아니요:

‘최신 릴리스’(또는 단순히 ‘release’) = 최신 릴리스 브랜치의 가장 최근 커밋

‘latest’ = tests-passed의 새로운 이름

고맙습니다. 이제 명확해졌어요!

이것은 긍정적인 발전으로 보입니다!

esr가 라벨링될 시점에, esr-previous에서 esr로의 업그레이드를 특별히 테스트할 계획이 있을까요? 저는 이러한 업그레이드가 원활하게 이루어지도록 구성되거나, 최대한 매끄럽게 수행하는 방법에 대한 좋은 설명이 제공되어야 한다고 생각합니다.

네, ESR 버전 간(또는 지원되는 모든 버전 간) 업그레이드는 여전히 매끄럽게 진행됩니다.

편의상 작은 요청 하나만 드리겠습니다. 포인터(예: 2026.0)를 릴리스가 배포된 달에 맞춰서 지정해 주실 수 있을까요? (즉, 1월은 2026.01, 2월은 2026.02 등)

특정 월에 항목을 명시적으로 연결하는 문제는 한 달을 건너뛰거나 한 달에 두 번 릴리스할 수 없다는 점입니다. 그래서 우리는 이를 단순한 증가하는 숫자로 유지할 계획입니다.

제가 많이 사용하는 프로젝트(mailcow)는 코어에 중요한 변경 사항이 없는 달은 건너뛰고 있습니다.

0부터 시작하는 것은 정말로 어색합니다. 프로그래머에게는 완벽하게 의미가 있지만, 비기술적인 사람들에게는 별로 말이 되지 않습니다.

수정:

한 달에 여러 번 릴리스해도 2026.02.x를 사용할 수 있고, 02에서 04로 쉽게 건너뛸 수 있습니다. 단, 순차적으로 증가만 한다면요?

언급된 x.0 릴리스에 대해 궁금했습니다. 월과 연동하면 어떤 시점에 릴리스가 나왔는지 바로 알 수 있어서 좋다고 생각합니다. 하지만 xx.8이 9월, 12월, 혹은 6월에 나왔는지는 사실 중요하지 않을 수도 있겠네요. 다만 현재 어느 버전인지 거의 기억하지 못해서, 지난주 버그인지 몇 개月前의 버그인지 커밋을 확인하지 않고 바로 알 수 있다면 정말 좋겠습니다.

Ubuntu는 YY.04와 YY.10을 사용하고 있습니다. 20년간 잘 작동해 왔습니다. 한 달을 건너뛰는 것은 어려운 일이 아닌 것 같습니다.

그것이 더 큰 문제일 수 있지만, 한 달에 두 번 릴리스해야 하는 상황이 발생한다면 22.1a 또는 22.01a와 같은 형식을 사용할 수 있을 것입니다.

얼마 전부터 우리 플랫폼에도 이 전략을 적용하기 시작했습니다. 브랜치와 패치까지 모든 면에서 똑같습니다. 강력히 추천합니다.

우리는 월간 릴리스를 사용하고 있습니다. 그래서 1~12번까지가 있습니다. 이 리듬은 모두에게 도움이 됩니다. 항상 릴리스할 만한 것이 항상 있습니다(안녕, dependabot) 그리고 아무도 한 달에 두 번이나 브랜치를 잘라내고 싶어 하지 않잖아요. 또한 제가 "2025.6을 사용한다"고 하면, 모두가 그것이 여름 휴가 직전 버전임을 알게 됩니다.

그럼에도 불구하고, 저는 현재의 프로세스에 꽤 만족하고 있었습니다 :slight_smile:

우선, :rocket: 이는 훌륭한 진전입니다!

좀 더 생각해 본 결과, 두 가지 작은 의견을 드립니다.

  1. git branch와 다른 많은 도구들은 버전 관리를 이해하지 못하며, 알파벳 순서나 숫자 순서로 정렬합니다. 두 경우 모두 2026.10은 2026.1과 2026.2 사이에 위치하게 됩니다. Ubuntu에서 영감을 받아, 릴리스와 패치 릴리스가 한 자리 숫자일 경우 앞자리 0을 도입할 것을 제안합니다. 이렇게 하면 v2026.01, v2026.02, v2026.10과 같이 되어 다시 모든 것이 제자리를 찾게 됩니다.

  2. 새로운 플러그인 호환성 방식은 지나치게 복잡하고 매우 취약해 보입니다.

즉, 제 플러그인에서 v2026.3이 필요한 새로운 기능을 개발한다고 가정해 보겠습니다. 저는 브랜치를 생성하고 그 안에 새로운 기능을 넣습니다. 이제 기능이 개발되었고 클라이언트가 만족하니, 잠시 휴식을 취하고 휴가를 즐길 수 있습니다 :palm_tree: :wine_glass: . 그러나 와인 세 잔째를 마신 후, 여러분이 v2026.4를 출시하고 제 클라이언트가 업데이트를 결정한다고 합시다. 그리고 펑, 제 플러그인에 v2026.4 브랜치가 없어 기능이 사라집니다 :sob:

따라서 저는 이 방식을 절대 사용하지 않고, 대신 계속 .discourse-compatibility를 사용할 것입니다.

실제로 의도는 그 반대입니다. 호환성(compat) 브랜치는 ‘출시된’ Discourse 브랜치를 위한 것뿐입니다. Discourse의 latest는 항상 플러그인의 main을 사용합니다. 새로운 기능을 개발하는 곳은 바로 그곳입니다.

즉, 이야기는 다음과 같습니다:

Discourse가 v2026.2를 출시합니다. 플러그인의 GitHub Actions가 이를 자동으로 감지하고 d-compat/v2026.2 브랜치를 생성합니다. 이제 Discourse v2026.2를 사용하는 모든 사람은 내 플러그인의 d-compat/v2026.2 버전을 사용하게 됩니다.

플러그인의 main에서 새 기능을 출시합니다. main 브랜치는 Discourse latest를 실행하는 사람들만 사용하므로 역호환성(backwards-compatibility)에 대해 걱정할 필요가 없습니다.

그리고 와인을 한 잔 더 마시고 있을 때 :wine_glass:, Discourse가 v2026.3을 출시합니다. 처음에는 이 버전에 대한 플러그인 브랜치가 없으므로 main이 사용됩니다. latest를 사용하는 사람들의 경우와 마찬가지로 모든 것이 계속 정상 작동합니다.

몇 시간 내에 GitHub Actions가 새 버전을 감지하고 d-compat/v2026.3을 고정(freeze)합니다. 이제 역호환성에 대한 걱정 없이 main에 다음 플러그인 기능이 올라올 준비가 된 것입니다.

이것은 본질적으로 CDCK에서 테마/플러그인의 안정적 호환성을 처리하기 위해 사용하는 워크플로우입니다. 각 안정 버전 출시 후, 수백 개의 테마/플러그인을 순회하며 .discourse-compatibility를 통해 이를 고정하는 스크립트를 실행합니다. 이 브랜치 기반 제안은 해당 워크플로우의 가벼운 버전이 되기를 목표로 합니다.

정말 멋지네요. 자세한 설명 감사드립니다.

네 번째 와인 잔을 들 수 있을 것 같네요 :wink:

다른 분들의 의견에 동의합니다. 제안된 변경 사항의 방향성이 좋습니다. 또한 제안된 브랜치 이름이 기존 이름보다 훨씬 직관적이라고 생각합니다. :+1:

아직 명확하지 않은 점은 releaseesr 브랜치(latest는 직관적으로 보입니다)의 업그레이드 프로세스가 어떻게 작동하는지입니다. 말씀하신 대로, 시점마다 현재 릴리스(이를 n이라고 하겠습니다)와 이전 릴리스(n-1)가 모두 지원되며, 관리자로서 언제 업그레이드할지 선택할 수 있다고 하셨죠.

다른 소프트웨어를 사용해 본 제 경험에 비추어 보면, 새 릴리스 버전(n+1)이 나올 때 버전 n+1의 사용 가능 여부를 알리는 알림을 받습니다. 그리고 그 후 메이저 업그레이드(Linux의 apt dist-upgrade와 유사)를 수행하거나, 마이너/표준 업데이트(Linux의 apt upgrade와 유사)를 수행하여 버전 n에 머물러 있을지 결정할 수 있었습니다. 이것이 Discourse 런처 스크립트에 구현될 기능인가요?

또한, 릴리스 의식/프로세스를 최소화하려는 의도는 이해하지만, 제 직관으로는 일반 릴리스와 ESR 릴리스 모두 출시 전에 최소한 추가 테스트를 거치기를 원합니다. 이는 엔터프라이즈 IT 분야에서 일한 기간이 너무 길어서 그런 편견일 수도 있습니다. :smile:

마지막으로, 월간 릴리스가 실제로는 “너무 빠르지는” 않은지 궁금합니다. 이는 인정하건대 주관적인 의견이기도 하지만, 부업으로 IT 업무를 관리하는 자원봉사자로서의 제 경험을 볼 때, 매월 큰 업데이트를 처리할 시간이 없을 수도 있습니다. 그리고 그 점을 고려해 볼 때, Discourse 개발자로서 여러분의 업무를 좀 더 쉽게 만들어 주려면 분기별로만 릴리스하고, 별도의 esr 브랜치 없이 release 브랜치만 유지하는 것도 하나의 방법이 될 수 있지 않을까 생각해 봅니다.

그렇게 되면 테마와 플러그인 개발자들의 삶은 더 힘들어질 것입니다. 왜냐하면 그(이) 상황에서 테마와 플러그인을 업데이트해야 하는 시간적 압박이 생기기 때문입니다. 그렇지 않으면 보안 업데이트를 받을 수 없게 됩니다. ESR 버전은 그런 압박을 덜어줄 것입니다.

완전히 이해하는지 확신이 서지 않습니다. 이전 게시물들을 바탕으로 한 제 이해는 릴리스 버전 브랜치를 만들면 해당 버전의 플러그인 브랜치가 자동으로 생성된다는 것이었습니다. 따라서 제 가정은 releaseesr를 하나로 통합하면 플러그인과 테마 개발자의 노력도 줄어들 것이라는 것입니다. 왜냐하면 수정 사항을 푸시할 때마다 더 적은 수의 브랜치(5개 대신 3개)로 푸시하면 되기 때문입니다. 하지만 제가 놓치고 있는 것이 있을까요?

테마/플러그인 개발자가 수정 사항을 배포하는 시점이 아니라, Discourse 코어에 보안 수정 사항이 적용되는 시점이 중요합니다.

따라서 releaseesr의 유일한 차이는 esr이 6 + 2 = 8개월 동안 보안 수정 사항을 제공한다는 점입니다.

현재 런처 툴링과 이 새로운 브랜치 구조를 사용하면, 다음과 같은 방식으로 업그레이드 시기를 제어할 수 있습니다:

  1. v2026.02 릴리스
  2. app.yml 파일에서 version: release/v2026.02로 설정
  3. v2026.03 릴리스
  4. 재빌드(rebuild)를 실행합니다. 여전히 2026.02 버전을 유지하며, 최근의 보안 패치가 적용됩니다.
  5. 준비가 되면 app.yml에서 version: release/v2026.03로 변경합니다.

하지만 매월 수동으로 app.yml을 편집하는 것은 이상적이지 않으므로, 프로세스를 더 사용자 친화적으로 만들 수 있는 시스템을 설계할 수 있기를 바랍니다.

OP(최초 게시자)의 프로세스는 브랜치를 실제 릴리스로 표시하기 전에 '릴리스 후보(release candidate)'로 취급할 수 있게 해줍니다. 이 기능을 정확히 어떻게/사용할지는 아직 명확하지 않으며, 새로운 시스템에 익숙해지면서 발전할 부분이 아닐까 생각합니다.

우리는 디스커스(Discourse) 개발의 속도와 광범위한 커스텀화를 가진 사용자들의 안정성 사이에서 균형을 맞추려 하고 있습니다. 기능들이 고객에게 전달되기까지 3개월 이상의 지연은 선택지가 아닙니다. 오히려 월간 릴리스는 우리에게 느린 편에 속합니다. 현재로써는 대부분의 호스팅에 latest를 사용하려는 의도를 가지고 있습니다.

하지만 물론, 디스커스를 자체 호스팅하는 사용자들의 경우 변경 사항이 덜 빈번하기를 원하는 점을 이해합니다. 그래서 ESR 릴리스가 중요한 역할을 하게 됩니다.

여기서는 플러그인/테마 개발자의 선택에 달려 있습니다. 현재 전략을 유지할 수도 있고, 이 경우 플러그인의 main 브랜치가 모든 '릴리스’된 Discourse 버전과 작동해야 합니다. 또는 자동 브랜치 전략을 사용할 수도 있는데, 이는 호환성을 쉽게 만들어 주지만, 중요한 버그/보안 수정 사항이 있을 경우 플러그인에서 상당한 '백포팅’이 필요할 수도 있습니다.

언제나 Discourse의 “지원 대상” 버전은 세 가지이며, 여기에 latest가 추가됩니다. 따라서 중요한 수정 사항은 최대 4개의 브랜치에 적용되어야 합니다.