diff --git a/.github/changelog-configuration.json b/.github/changelog-configuration.json new file mode 100644 index 0000000..93f6aee --- /dev/null +++ b/.github/changelog-configuration.json @@ -0,0 +1,30 @@ +{ + "pr_template": "- ${{TITLE}} (#${{NUMBER}})", + "categories": [ + { + "title": "## 🚀 Features", + "labels": ["enhancement", "feature"] + }, + { + "title": "## 🛠️ Minor Changes", + "labels": ["change"] + }, + { + "title": "## 🔎 Breaking Changes", + "labels": ["breaking"] + }, + { + "title": "## 🐛 Fixes", + "labels": ["bug", "fix"] + }, + { + "title": "## 📄 Documentation", + "labels": ["documentation"] + }, + { + "title": "## 🔗 Dependency Updates", + "labels": ["dependency"] + } + ], + "template": "${{CATEGORIZED_COUNT}} changes since ${{FROM_TAG}}\n\n${{CHANGELOG}}" +} diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 0000000..644e799 --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,5 @@ +{ + "extends": ["config:base"], + "labels": ["dependency"], + "postUpdateOptions": ["gomodTidy"] +} diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..b98988b --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,33 @@ +--- +name: golangci-lint +on: + push: + tags: + - v* + branches: + - master + paths-ignore: + - .github/** + pull_request: + paths-ignore: + - .github/** + +permissions: + contents: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Determine Go version from go.mod + run: echo "GO_VERSION=$(grep "go 1." go.mod | cut -d " " -f 2)" >> $GITHUB_ENV + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + args: --issues-exit-code=0 --timeout=3m ./... diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..acde9d4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,47 @@ +--- +name: build + +on: + push: + tags: + - "v*" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Determine Go version from go.mod + run: echo "GO_VERSION=$(grep "go 1." go.mod | cut -d " " -f 2)" >> $GITHUB_ENV + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - name: Cache Go modules + uses: actions/cache@v4 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Build changelog from PRs with labels + id: build_changelog + uses: mikepenz/release-changelog-builder-action@v5 + with: + configuration: ".github/changelog-configuration.json" + ignorePreReleases: "${{ !contains(github.ref, '-rc') }}" + outputFile: .github/release-notes.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + if: success() && startsWith(github.ref, 'refs/tags/') + with: + version: latest + args: release --release-notes .github/release-notes.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml new file mode 100644 index 0000000..7364041 --- /dev/null +++ b/.goreleaser.yaml @@ -0,0 +1,32 @@ +--- +project_name: cfop-generator +before: + hooks: + - go mod tidy +builds: + - main: ./main.go + env: + - CGO_ENABLED=0 + goos: + - linux + - darwin + - windows + goarch: + - amd64 + - arm64 + - arm +brews: + - name: cfop-generator + repository: + owner: containeroo + name: homebrew-tap + token: "{{ .Env.TAP_GITHUB_TOKEN }}" + directory: Formula + homepage: https://containeroo.ch + description: CLI tool to convert exported Cloudflare zones to cloudflare-operator DNSRecord objects + license: GNU General Public License v3.0 + dependencies: + - name: go + type: optional + install: |- + bin.install "cfop-generator" diff --git a/dnsrecord.yaml.tmpl b/dnsrecord.yaml.tmpl new file mode 100644 index 0000000..f78842d --- /dev/null +++ b/dnsrecord.yaml.tmpl @@ -0,0 +1,26 @@ +--- +apiVersion: cloudflare-operator.io/v1 +kind: DNSRecord +metadata: + name: {{ .Name | cleanName }} +spec: + name: {{ .Name }} + proxied: true + ttl: {{ .TTL }} + type: {{ .Type }} +{{- if and (ne .Type "SRV") (ne .Type "MX") }} + content: {{ .Content | trimDot }} +{{- end }} +{{- if (eq .Type "SRV") }} +{{- $d := split .Content " " }} + data: + priority: {{ index $d 0 }} + weight: {{ index $d 1 }} + port: {{ index $d 2 }} + target: {{ index $d 3 | trimDot }} +{{- end }} +{{- if (eq .Type "MX") }} +{{ $d := split .Content " " }} + priority: {{ index $d 0 }} + content: {{ index $d 1 | trimDot }} +{{- end }} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..34f21a2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/containeroo/cfop-generator + +go 1.23.1 diff --git a/main.go b/main.go new file mode 100644 index 0000000..c637b67 --- /dev/null +++ b/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "embed" + "flag" + "fmt" + "io" + "os" + "regexp" + "strings" + "text/template" +) + +type DNSRecord struct { + Name string + Type string + TTL string + Content string +} + +var zonefilePath string + +const zonefileParseRegex string = `(.*)\s(\d+)\sIN\s([A-Z]+)\s(.*)` + +//go:embed dnsrecord.yaml.tmpl +var dnsrecordTemplate embed.FS + +func init() { + flag.StringVar(&zonefilePath, "file", "", "Path to the exported zonefile") + flag.Parse() +} + +func run(out io.Writer) error { + zonefile, err := os.ReadFile(zonefilePath) + if err != nil { + return err + } + + regex := regexp.MustCompile(zonefileParseRegex) + + var records []DNSRecord + + for _, line := range strings.Split(string(zonefile), "\n") { + if match := regex.MatchString(line); match { + name := strings.TrimSuffix(regex.FindStringSubmatch(line)[1], ".") + ttl := regex.FindStringSubmatch(line)[2] + recordType := regex.FindStringSubmatch(line)[3] + content := regex.FindStringSubmatch(line)[4] + + if recordType == "SOA" || recordType == "NS" { + continue + } + + record := DNSRecord{ + Name: name, + Type: recordType, + TTL: ttl, + Content: content, + } + + records = append(records, record) + } + } + + funcMap := template.FuncMap{ + "split": strings.Split, + "trimDot": func(s string) string { + return strings.TrimSuffix(s, ".") + }, + "cleanName": func(s string) string { + noDots := strings.ReplaceAll(s, ".", "-") + noUnderscores := strings.ReplaceAll(noDots, "_", "-") + return noUnderscores + }, + } + + tmpl, err := template.New("dnsrecord.yaml.tmpl").Funcs(funcMap).ParseFS(dnsrecordTemplate, "dnsrecord.yaml.tmpl") + + for _, record := range records { + if err != nil { + return err + } + + err = tmpl.Execute(out, record) + if err != nil { + return err + } + } + + return nil +} + +func main() { + if err := run(os.Stdout); err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } +}