# Nykyaikaiset julkaisustrategiat GitHub-toiminnoilla

<!--category-- DevOps, CI/CD, GitHub Actions -->
<datetime class="hidden">2025-12-04T12:00</datetime>

**Periaate on, että vapautat nopeasti ja kivuttomasti, ja vapaudut useammin.** Ketterän kehityksen kannalta tämä on tärkeää: nopeat ja luotettavat käyttöönottot luovat nopean palautesilmukan, jota tarvitset. Kun käyttöönotto on pelottavaa tai hidasta, tiimit vaihtavat ja lisäävät riskejä. Kun käyttöönotto on tylsää ja automaattista, lähetät pieniä muutoksia usein.

Tämä opas kattaa julkaisustrategiat, joita käytän tuotannossa yksinkertaisista tag-pohjaisista julkaisuista monipakettiseen monorepojulkaisuun. Kaikki esimerkit tulevat todellisista julkaisuista. [GitHub-toimet](https://docs.github.com/en/actions) työnkulkua tässä arkistossa.

[TOC]

## Vapauttamisstrategiat glögissä

Ennen kuin sukeltat yksityiskohtiin, kerro, miten yhteisiä strategioita verrataan:

Strategia parasta monimutkaisuutta varten Yhteinen toiminta
|----------|----------|------------|-----------|
| **Merkintäpohjainen** Sovellukset, selvät julkaisut Alhaiset startupit, soolodevitsit, kypsät tiimit
| **Oksapohjaiset** Jatkuva käyttöönotto Alhaiset startupit, nopeasti liikkuvat tiimit
| **GitFlow** Säänneltyjä julkaisuja, useita versioita Yritykselle, compliance-raskas
| **Trunk-pohjaiset + Feature-liput** Huippunopeat tiimit Keskinkertainen teknologia, kypsät startupit
| **Monorepon monipakkaus** Kirjastot, yhteiset koodikannat Medium-alustatiimit, OSS-projektit

### Mikä toimii missä

**Startups & Small Teams**: Aloita tag-pohjainen tai haarapohjainen käyttöönotto. Pidä se yksinkertaisena - voit aina lisätä monimutkaisuutta myöhemmin. Trunk-pohjainen kehitys ominaisuuslipuilla on suosittua mittakaavassa, mutta yliampuvaa pienille tiimeille.

**Yritystoiminta**: Usein tarvitaan GitFlow'ta tai vastaavaa vaatimustenmukaisuuteen, kirjausketjuihin ja julkaisun hallintaan. Useita ympäristöjä (dev, QA, lavastus, prod) hyväksyntäporteilla. Toimitusketjun turvallisuus edellyttää yhä enemmän esineiden todistuksia.

**Solon kehittäjät**Tag-pohjainen on ihanteellista. Paina tagia, käyttöönotto tapahtuu. Ei seremonioita, ei yläpuolella.

**Alustat/kirjastoryhmät**: Tarvitaan monorepo-strategioita, joissa on itsenäinen versiointi pakettia kohden. Semanttinen versiointi on ratkaisevaa loppupään kuluttajille.

## Tagipohjainen käyttöönotto

Yksinkertaisin ja luotettavin strategia. Erilaiset tagien etuliitteet johtavat erilaisiin ympäristöihin tai laukaisevat erilaisia toimia.

### Ympäristön kohdentaminen

```yaml
on:
  push:
    tags:
      - 'release-*'  # Production
      - 'local-*'    # Dev environment
```

```yaml
- name: Build Docker image
  run: |
    TAG_NAME=${GITHUB_REF#refs/tags/}
    if [[ "$TAG_NAME" == local-* ]]; then
      docker build -t myapp:local .
    else
      docker build -t myapp:latest -t myapp:$(date +%s) .
    fi
```

**Miksi se toimii?**: Yksinkertainen henkinen malli, selvät käyttöönotot, helppo rullaus ajastettujen tunnisteiden kautta.

```bash
# Deploy to production
git tag release-v2024.12.01 && git push origin release-v2024.12.01

# Deploy to dev
git tag local-feature-test && git push origin local-feature-test
```

### Multi-Package-versiointi

Monorepoissa, joissa on useita julkaisukelpoisia paketteja, käytetään tunnisteen etuliitteitä, jotta voidaan tunnistaa, minkä paketin voi julkistaa:

```yaml
# Different workflows, different tag patterns
# umami-net.yml
on:
  push:
    tags: ['umamiv*.*.*']  # e.g., umamiv1.0.5

# fetchextension.yml
on:
  push:
    tags: ['fetchextension-v*.*.*']  # e.g., fetchextension-v1.2.0
```

Pura versio ja käytä sitä kauttaaltaan:

```yaml
- name: Extract version
  run: echo "VERSION=${GITHUB_REF#refs/tags/umamiv}" >> $GITHUB_OUTPUT

- name: Build & Pack
  run: |
    dotnet build -c Release -p:Version=${{ steps.version.outputs.VERSION }}
    dotnet pack -c Release -p:PackageVersion=${{ steps.version.outputs.VERSION }}
```

Automaattista versiointia varten [MinVer](https://github.com/adamralph/minver) Laskee versiot Git-tageista:

```xml
<PackageReference Include="MinVer" Version="6.0.0" PrivateAssets="all" />
<PropertyGroup>
  <MinVerTagPrefix>umamiv</MinVerTagPrefix>
</PropertyGroup>
```

## Oksapohjainen käyttöönotto

Siirry automaattisesti, kun koodi laskeutuu tietyille sivukonttoreille. Tavallista startupeissa ja nopeasti liikkuvissa tiimeissä.

```yaml
on:
  push:
    branches: [main]      # → Production
    # branches: [develop] # → Staging
```

**Plussat**: Ei manuaalista askelta, ottaa käyttöön yhdistämisen
**Miinukset**: Tapaturmakäynnillä mahdollinen, vähemmän selkeä historia

Käytän hybridilähestymistapaa - rakennan haaratyöntöä (CI-varmennusta), mutta julkaisen vain tageilla:

```yaml
on:
  push:
    tags: ['scheduler-*']
    branches: [main, local]

jobs:
  build:
    # Always build
  publish:
    if: startsWith(github.ref, 'refs/tags/')  # Only publish on tags
```

## Päivittää automaattisesti Vartiotornia

Itseohjautuvia komennuksia varten [Vartiotorni](https://containrrr.dev/watchtower/) vetää automaattisesti uusia kuvia:

```yaml
services:
  app:
    image: myapp:latest
    labels:
      - "com.centurylinklabs.watchtower.enable=true"

  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 300  # Check every 5 minutes
```

**Käyttöönottovirta**: Push tag → GitHub Actions building → Push to Register → Watchtower vetää → Kontti käynnistyy uudelleen. Kokonaisaika: ~5 minuuttia.

## Laatuportit

Nopeat käyttöönotot ovat hyödyttömiä, jos käytät vikoja. Jokaisessa työnkulussa on oltava portit:

```yaml
- name: Run tests
  run: dotnet test --configuration Release

- name: Build
  run: dotnet build --configuration Release

# Tests or build fail → workflow stops → no publish
```

Moniosaisten pakettien osalta rakenna kaikki tavoitteet:

```yaml
- run: dotnet build -c Release --framework net8.0
- run: dotnet build -c Release --framework net9.0
```

## Salasanaton julkaisu OIDC:llä

Moderni lähestymistapa - ei salaisuuksia pyöritettäväksi, ei avaimia vuotoon:

```yaml
permissions:
  id-token: write
  contents: read

- uses: NuGet/login@v1
  with:
    user: 'myusername'

- run: dotnet nuget push *.nupkg --api-key ${{ steps.login.outputs.NUGET_API_KEY }}
```

GitHub vaihtaa OIDC-kylttinsä lyhytikäiseen NuGet API -avaimeen. NuGet.org:n luota tähän.

## Toimitusketjun turvallisuus

[Artifaktia koskevat todistukset](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations) Todista, että esineesi on rakennettu CI:hen eikä niitä ole peukaloitu.

```yaml
permissions:
  id-token: write
  attestations: write

- uses: docker/build-push-action@v6
  id: push
  with:
    push: true
    tags: myapp:latest

- uses: actions/attest-build-provenance@v2
  with:
    subject-name: index.docker.io/myuser/myapp
    subject-digest: ${{ steps.push.outputs.digest }}
    push-to-registry: true
```

Kuluttajat vahvistavat seuraavat tiedot: `gh attestation verify oci://index.docker.io/myuser/myapp:latest --owner myuser`

Näin saavutetaan [SLSA-taso 2](https://slsa.dev/spec/v1.0/levels). Tasolle 3. [Uudelleenkäytettävät työnvirrat](https://docs.github.com/en/actions/sharing-automations/reusing-workflows).

## Esikatseluympäristöt

Tiimit tarvitsevat sidosryhmiltä ennakkotietoja muutoksista ennen yhdistämistä.

**Yritystoiminta** - Oksapohjaiset esikatseluympäristöt:

```yaml
on:
  pull_request:
    types: [opened, synchronize]

- run: |
    BRANCH=$(echo ${{ github.head_ref }} | sed 's/[^a-zA-Z0-9]/-/g')
    docker build -t myapp:preview-$BRANCH .
    # Deploy to k8s namespace, cloud platform, etc.
```

**Startup/solo-lähestymistapa** - Tunneloida paikallinen kone:

```bash
# Cloudflare Tunnel (free)
cloudflared tunnel run --url http://localhost:5000 my-preview
# → https://my-preview.cfargotunnel.com
```

Tai käyttää [Liitäntälaitteet](https://www.wireguard.com/) VPN sisäisen joukkueen pääsyyn dev-koneeseen.

## Käytännöllisiä vihjeitä

**Tagin puhdistus**Git-tagit jäävät ikuisiksi ajoiksi.

```bash
git tag -d old-test-tag                      # Delete local
git push origin :refs/tags/old-test-tag      # Delete remote
```

**Nimeämiskonventit**Ole johdonmukainen.

- `release-YYYY.MM.DD` tuotannolle
- `local-feature-name` dev:lle
- `packagev1.2.3` kirjastoille

**Salaisuudet**: Säilytä [GitHub-salaisuuksia](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions), ei koskaan koodilla. Vaaditut salaisuudet tyypillisesti:

- `DOCKER_HUB_ACCESS_TOKEN`
- `NUGET_API_KEY` (tai käyttää OIDC:tä)
- `NPM_TOKEN`

**Monorepon kehitys**: Käytä projektiviitteitä kehitystyön aikana, siirry käyttöönottovarmennuksen pakettiviitteisiin:

```xml
<ProjectReference Include="..\MyLib\MyLib.csproj" />
<!-- <PackageReference Include="MyLib" Version="1.0.0" /> -->
```

## Muutto Kubernetiin

Samat periaatteet, erilaiset työkalut:

Dockerin sävellys Kuberneetes
|----------------|------------|
Vartiotorni [ArgoCD](https://argo-cd.readthedocs.io/) / [Fluksi](https://fluxcd.io/docs/) |
Docker-compose.yml-ympäristöt Nimitilat
Kontti käynnistää uudelleen liikkuvan kaluston käyttöönoton
Manuaalinen rollback GitOps rollback

## Yhteenveto

1. **Aloita yksinkertainen**: Tag-pohjainen käyttöönotto kattaa suurimman osan tarpeista
2. **Lisää monimutkaisuutta tarvittaessa**: Haarakonttoripohjaiset, moniympäristöiset, todistukset
3. **Automatisoi laatuportit**: Testien on läpäistävä ennen julkaisua
4. **Käytä rollbackia**: Aikaleimatut tagit tai GitOps-historia
5. **Varmista putkesi**: Salaisuuksia koskeva OIDC, alkuperätodistukset
6. **Sovita asiayhteytesi**: Solo dev . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Tässä näkyvät työnvirrat suoritetaan tässä arkistossa - tarkista `.github/workflows/` täydelliset täytäntöönpanot.

**Muista**: Nopeat, luotettavat käyttöönottot ovat ketterän kehityksen perusta. Investoi vapautumisputkeen aikaisin.