GitHub Actions cung cấp một cách để tự động hóa quy trình phát triển phần mềm của bạn trên GitHub. Điều này bao gồm các tác vụ CI/CD truyền thống trên cả ba hệ điều hành chính như chạy bộ thử nghiệm, xây dựng ứng dụng và xuất bản gói. Nhưng nó cũng bao gồm lời chào tự động dành cho những người đóng góp mới, gắn nhãn các yêu cầu kéo dựa trên các tệp đã thay đổi hoặc thậm chí
Ngoài ra, GitHub Actions miễn phí cho các dự án mã nguồn mở 🎉
Trong Tác vụ GitHub, bạn tạo Quy trình công việc, các quy trình công việc này được tạo thành từ và các Công việc này được tạo thành từ
Các bước có thể chạy lệnh, chạy tác vụ thiết lập hoặc chạy một hành động trong kho lưu trữ của bạn, kho lưu trữ công cộng hoặc một hành động được xuất bản trong sổ đăng ký Docker. Không phải tất cả các bước chạy hành động, nhưng tất cả các hành động chạy như một bước
Trong trường hợp này, Hành động là một đoạn mã sẽ được chạy trên cơ sở hạ tầng Hành động GitHub. Nó sẽ có quyền truy cập vào không gian làm việc chứa dự án của bạn và có thể thực hiện bất kỳ tác vụ nào bạn muốn. Nó có thể nhận đầu vào, cung cấp đầu ra và truy cập các biến môi trường. Điều này cho phép bạn xâu chuỗi các Hành động lại với nhau để có khả năng vô tận. Bạn có thể sử dụng các Hành động dựng sẵn từ GitHub Marketplace hoặc tự viết
Trong bài đăng này, chúng tôi sẽ hướng dẫn cách tạo Hành động vùng chứa của riêng bạn bằng Python và xuất bản nó lên GitHub Marketplace để người khác sử dụng
Để tạo cho mình một khởi đầu thuận lợi, chúng ta sẽ sử dụng mẫu
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
3 này mà tôi đã xuất bản trên GitHub. Chúng tôi sẽ nói qua từng tệp chúng tôi cần tạo để bạn không nhất thiết phải sử dụng nó, nhưng nó sẽ giúp cuộc sống của chúng tôi dễ dàng hơn một chútSao chép mẫu
Để bắt đầu, hãy truy cập mẫu trên GitHub và nhấn nút “Sử dụng mẫu này” màu xanh lục lớn
Đặt cho Hành động mới của bạn một tên repo và một mô tả tùy chọn. Trong ví dụ này, chúng tôi sẽ thực hiện một Hành động tạo tệp YAML, vì vậy tôi sẽ đặt tên cho nó là
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
4Điều này sẽ cung cấp cho chúng tôi một kho lưu trữ mới về cơ bản là một nhánh của mẫu
Ngoài ra, mẫu của chúng tôi có một vài Tác vụ GitHub được định cấu hình để kiểm tra Hành động [thu hút đệ quy Hành động GitHub]. Chúng tôi có thể truy cập tab “Hành động” và hy vọng sẽ thấy cả hai bài kiểm tra đã vượt qua
Chúng ta sẽ quay lại các thử nghiệm này sau
Cuối cùng, về phía GitHub, bây giờ hãy sao chép kho lưu trữ của chúng tôi cục bộ
$ git clone
Viết mã hành động của chúng tôi
Nếu bạn tìm trong dự án, bạn sẽ tìm thấy một vài tệp. Hai thứ chúng ta sẽ tập trung vào bây giờ là
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 và FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
6Dockerfile
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 đây là bản dựng Python Docker khá tối giảnFROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
Chúng tôi bắt đầu với một thùng chứa
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
8 dựa trên FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
9. Chúng tôi sao chép mã của mình và chạy $ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
0 để cài đặt các phụ thuộc của bạn vào thư mục ứng dụng của chúng tôiSau đó, chúng tôi sử dụng một bước xây dựng nhiều tầng để chuyển sang vùng chứa không phân phối rất tối thiểu. Hình ảnh bộ chứa này không có hệ điều hành và chỉ chứa một số thứ như chứng chỉ SSL để đảm bảo ứng dụng của chúng tôi có thể hoạt động chính xác khi giao tiếp với các dịch vụ web một cách an toàn
Chúng tôi sao chép mã Python và các phần phụ thuộc từ bộ chứa
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
8 và đặt nó làm lệnh thời gian chạy của chúng tôiKết quả của việc này là một hình ảnh vùng chứa khá nhỏ. Ví dụ chúng tôi có ở đây nên xây dựng tới khoảng 50MB
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
Tất nhiên, điều này sẽ tăng lên một chút khi chúng tôi thêm nhiều mã hơn, nhưng nó vẫn nhỏ so với một thùng chứa có bản phân phối linux đầy đủ trong đó. Đây cũng là một cách rất an toàn để xây dựng các thùng chứa vì chúng tôi đã giảm đáng kể bề mặt tấn công của mình nếu một kẻ xấu khai thác ứng dụng của chúng tôi, họ thậm chí sẽ không có vỏ để xâm nhập vào thùng chứa
Bạn có thể tạo các vùng chứa nhỏ hơn nữa bằng cách sử dụng ngôn ngữ được biên dịch tĩnh như Go. Có một hướng dẫn và mẫu cho điều đó quá
chủ yếu. py
Chúng ta cũng hãy dành một phút để khám phá tệp
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
6 ví dụ mà chúng tôi có ở đây trước khi thay thế nó bằng mã linting YAML của chúng tôiimport os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
Điều này dài hơn một chút so với ví dụ thế giới xin chào Python tiêu chuẩn
Chúng tôi đã bọc logic của mình trong một hàm
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
3 và đang gọi nó từ bên trong một khối $ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
4. Chúng tôi cũng đang nhập $ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
5 một chút không cần thiết ở đây chỉ để chứng minh rằng cài đặt phụ thuộc của chúng tôi trong FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 đã hoạt động, chúng tôi sẽ không thực sự sử dụng nóDòng đầu tiên trong hàm
$ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
3 của chúng tôi đang nhận đầu vào Hành động GitHub từ một biến môi trường. Các hành động của GitHub có thể nhận các đối số bằng cách sử dụng câu lệnh $ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
8 và những đối số này được hiển thị thông qua các biến môi trường có tên $ docker build -t jacobtomlinson/gha-lint-yaml:latest .
...
$ docker images | grep gha-lint-yaml
jacobtomlinson/gha-lint-yaml latest 4eb311726658 9 seconds ago 54.1MB
9 theo sau là tên đối số viết hoa toàn bộname: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
Trong cấu hình trên, biến
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
0 sẽ được tạo với giá trị import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
1. Sau đó chúng tôi có thể lấy cái này trong mã của chúng tôiDòng mã Python tiếp theo đang định dạng biến này thành một chuỗi đầu ra, không có gì lạ mắt ở đây ngoài chuỗi f [chuỗi f woo 🎉]. Điều này có nghĩa là chuỗi đầu ra của chúng tôi bây giờ sẽ là
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
2 trong quy trình làm việc ví dụ nhỏ của chúng tôiSau đó, cuối cùng nó được in ra thiết bị xuất chuẩn bằng một số cú pháp Tác vụ GitHub cụ thể. Bạn có thể chuyển thông tin trở lại quy trình làm việc của GitHub Actions với một số cụm từ kiểm soát nhất định. Đây là ví dụ về thiết lập đầu ra của Hành động bằng cách in
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
3Điều này có nghĩa là chúng ta có thể truy cập giá trị đầu ra của mình trong các Tác vụ GitHub khác sau này trong quy trình làm việc
Viết kẻ nói dối YAML của chúng tôi
Bài viết này không nhằm mục đích hướng dẫn Python, vì vậy chúng tôi sẽ không đi vào chi tiết mã ở đây. Nhưng đây là một phác thảo chung
Ứng dụng của chúng tôi sẽ lấy một số đầu vào
4 vào tệp chúng tôi muốn lint liên quan đến thư mục gốc của dự ánimport os import requests # noqa We are just importing this to prove the dependency installed correctly def main[]: my_input = os.environ["INPUT_MYINPUT"] my_output = f"Hello {my_input}" print[f"::set-output name=myOutput::{my_output}"] if __name__ == "__main__": main[]
- Liệu chúng ta có nên
5 trong phần linting của mình hay không [lỗi khi cảnh báo]import os import requests # noqa We are just importing this to prove the dependency installed correctly def main[]: my_input = os.environ["INPUT_MYINPUT"] my_output = f"Hello {my_input}" print[f"::set-output name=myOutput::{my_output}"] if __name__ == "__main__": main[]
Sau đó, chúng tôi sẽ sử dụng thư viện của bên thứ ba
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
6 để cắt xén mã, đưa ra các lỗi và cảnh báo bằng cách sử dụng các cụm từ kiểm soát và thoát bằng một dấu ____13_______7 hoặc import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
8 tùy thuộc vào việc tệp có được ghép nối thành công hay khôngHành động của chúng tôi cũng sẽ in một đầu ra báo cáo số lượng cảnh báo nếu chúng tôi vượt qua linting
import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
Ngoài ra, khi chúng tôi đang sử dụng phụ thuộc
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
6 trong mã của mình, chúng tôi cần cập nhật name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
0 trong FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5 của mìnhRUN pip install --target=/app yamllint
Cập nhật hành động. yml
Bây giờ chúng tôi có một số mã, chúng tôi cần cập nhật tệp
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
2 của mình để cho GitHub Actions cách chạy mã của chúng tôi và mong đợi đầu vào và đầu ra nàoname: "YAML file linter"
description: "Lint a YAML file in your project"
author: "Jacob Tomlinson"
inputs:
path:
description: "Path to the YAML file to be linted"
required: true
strict:
description: "Run the linter in strict mode [error on warnings]"
required: false
default: false
outputs:
warnings:
description: "Number of warnings raised if lint was successful"
runs:
using: "docker"
image: "Dockerfile"
Chúng tôi bắt đầu ở đây bằng cách đặt một số siêu dữ liệu để mô tả Hành động, hành động đó làm gì và ai đã viết hành động đó
Sau đó, chúng tôi liệt kê các đầu vào và đầu ra của mình, đưa ra mô tả cho chúng, đặt bất kỳ giá trị mặc định nào và liệu chúng có phải là tùy chọn hay không. Đối với Hành động lint YAML của chúng tôi, chúng tôi đã chỉ định đầu vào
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
4 để chúng tôi có thể đặt tệp nào thành lint, đây không phải là tùy chọn. Chúng tôi cũng có đầu vào import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
5 nơi chúng tôi có thể tùy ý đặt kẻ nói dối của mình thành lỗi ở chế độ cảnh báo. Sau đó, chúng tôi cho GitHub Actions biết để mong đợi kết quả đầu ra là name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
5 từ mã của chúng tôiCuối cùng, chúng tôi cho GitHub biết cách chạy Hành động của chúng tôi. Trong trường hợp này, chúng tôi đang sử dụng
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
6 và chúng tôi có thể chỉ định một hình ảnh theo tên hoặc đặt nó thành FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
5, điều này sẽ khiến các Tác vụ GitHub tự xây dựng hình ảnh của chúng tôi khi chạy Hành độngMột cải tiến tốt đẹp trong tương lai có thể là thiết lập Triển khai liên tục để xây dựng và đẩy hình ảnh của chúng tôi lên Docker Hub. Điều này sẽ cho phép chúng tôi tham chiếu hình ảnh của mình theo tên ở đây và nó sẽ được tạo sẵn, giúp chúng tôi tiết kiệm thời gian bất cứ khi nào Hành động của chúng tôi được sử dụng. Hiện tại, hình ảnh của chúng tôi mất khoảng 7 giây để xây dựng, điều này không tệ lắm, nhưng nếu Hành động của chúng tôi phức tạp hơn thì sẽ lâu hơn
Cập nhật README
Chúng tôi cũng nên cập nhật tệp
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
8 của mình để phản ánh cấu hình này nhằm giúp mọi người hiểu cách sử dụng Hành động của chúng tôiTôi sẽ không bao gồm tệp này ở đây vì nó hơi dài nhưng bạn có thể kiểm tra nó trên GitHub
Tôi khuyên bạn nên bao gồm các phần sau
- Huy hiệu cho biết đó là Hành động trên thị trường
- Tổng quan về những gì Hành động làm
- Bảng đầu vào và đầu ra
- Ví dụ về các cách khác nhau để sử dụng Hành động của bạn
Viết bài kiểm tra
Bây giờ chúng ta đã có mã, cấu hình và tài liệu cho Hành động của mình, chúng ta nên kiểm tra nó
thử nghiệm cục bộ
Trong khi viết mã, tôi đã thực hiện một số thử nghiệm cục bộ. Tôi đã tạo một tệp mới trong thư mục có tên là
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
9 có tên là import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
0Sau đó, tôi chạy mã Hành động của mình với các biến môi trường được đặt nội tuyến để kiểm tra sự thay đổi
$ INPUT_PATH=tests/valid.yaml INPUT_STRICT=false python main.py
::warning file=tests/valid.yaml,line=1,col=1::missing document start "---" [document-start]
::set-output name=warnings::1
Chạy cái này đã báo cáo một cảnh báo bằng cách sử dụng định dạng cụm từ kiểm soát Hành động GitHub và báo cáo các cảnh báo
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
8 dưới dạng đầu ra. Tuyệt vờiTôi cũng đã tạo một tệp không hợp lệ có tên là
import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
2 để kiểm tra xem linting có bị lỗi không$ INPUT_PATH=tests/invalid.yaml INPUT_STRICT=false python main.py
::warning file=tests/invalid.yaml,line=1,col=1::missing document start "---" [document-start]
::error file=tests/invalid.yaml,line=4,col=1::syntax error: could not find expected ':' [None]
exit 1
Ở đây, chúng ta có thể thấy rằng đã xảy ra lỗi và lệnh đã thoát với trạng thái
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
8, điều này sẽ khiến quy trình làm việc của GitHub Actions không thành công. siêuBây giờ, hãy thiết lập một số Tích hợp liên tục để kiểm tra mã mỗi khi chúng tôi đẩy mạnh thay đổi hoặc ai đó mở Yêu cầu kéo. Chúng tôi có thể thiết lập điều này bằng GitHub Actions
Thử nghiệm bản dựng
Điều đơn giản nhất chúng ta có thể làm là kiểm tra xem mã Python của chúng ta có hợp lệ không. Khi chúng tôi sử dụng mẫu
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
3, chúng tôi đã có một Quy trình công việc được định cấu hình trong import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
5, quy trình này sẽ hoạt động tốt cho mã mới của chúng tôiFROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
0Quy trình công việc này chạy ba bước. Chúng tôi có một môi trường Python hoạt động với
import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
6 và sau đó chúng tôi kiểm tra mã của mình với import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
7. Đây đều là những Hành động chính thức do GitHub cung cấp và bạn cũng có thể tìm nguồn cho cả hai Hành động này trên GitHubSau đó, chúng tôi có một bước
import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
8 nơi chúng tôi đã cung cấp một tập lệnh shell ngắn để cài đặt import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
9 và sau đó sử dụng nó để tạo mã của chúng tôiChúng tôi không cần thay đổi bất cứ điều gì ở đây vì điều này là đủ cho hầu hết các ứng dụng Python đơn giản
Thử nghiệm hội nhập
Bây giờ chúng tôi đã kiểm tra mã của mình có hợp lệ hay không, chúng tôi cũng có thể kiểm tra xem Hành động của chúng tôi có chạy chính xác trên cơ sở hạ tầng Hành động GitHub không. Chúng tôi có thể làm điều này bằng cách để Hành động của chúng tôi tự chạy theo các yêu cầu đẩy và kéo
Khởi động Hành động này là một trong những tính năng yêu thích của tôi về Hành động GitHub vì chúng tôi có thể thực hiện kiểm tra tích hợp toàn diện từ đầu đến cuối mà không cần bất kỳ thiết lập phức tạp nào
Có một ví dụ trong
RUN pip install --target=/app yamllint
0 mà chúng tôi sẽ sửa đổi để giống như thế nàyFROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
1Trong quy trình làm việc này, chúng tôi bắt đầu bằng cách kiểm tra mã Hành động của mình. Sau đó, chúng tôi tự chạy Hành động của riêng mình và đặt đầu vào giống như chúng tôi đang thực hiện cục bộ [lưu ý rằng chúng tôi đã bỏ qua đầu vào
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
5 vì đây là tùy chọn và giá trị mặc định là ổn]Hành động này sẽ làm hỏng tệp
import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
0 của chúng tôi và đưa ra một cảnh báoSau đó, chúng ta có một Hành động
import os
import sys
from yamllint import linter
from yamllint.config import YamlLintConfig
def main[]:
yaml_path = os.environ["INPUT_PATH"]
strict = os.environ["INPUT_STRICT"] == "true"
conf = YamlLintConfig["extends: default"]
warning_count = 0
with open[yaml_path] as f:
problems = linter.run[f, conf, yaml_path]
for problem in problems:
if problem.level == "warning" and strict:
problem.level = "error"
print[
f"::{problem.level} file={yaml_path},line={problem.line},"
f"col={problem.column}::{problem.desc} [{problem.rule}]"
]
if problem.level == "warning":
warning_count = warning_count + 1
if problem.level == "error":
sys.exit[1]
print[f"::set-output name=warnings::{warning_count}"]
sys.exit[0]
if __name__ == "__main__":
main[]
8 để kiểm tra xem Hành động của chúng ta có hoạt động như chúng ta mong đợi hay không bằng cách kiểm tra xem đầu ra name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
5 có hiển thị chính xác import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
8 hay khôngĐẩy lên GitHub
Tiếp theo, chúng ta có thể đẩy Hành động của mình lên GitHub và chạy kiểm tra để đảm bảo mọi thứ hoạt động như mong đợi
FROM python:3-slim AS builder
ADD . /app
WORKDIR /app
# We are installing a dependency here directly into our app source dir
RUN pip install --target=/app requests
# A distroless container image with Python and some basics like SSL certificates
# //github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian10
COPY --from=builder /app /app
WORKDIR /app
ENV PYTHONPATH /app
CMD ["/cmd/main.py"]
2Chúng tôi có thể chuyển đến tab Hành động trên kho lưu trữ của mình và xem quy trình công việc
RUN pip install --target=/app yamllint
6 và RUN pip install --target=/app yamllint
7 của chúng tôi chạyBước lint của chúng tôi đã qua, vì vậy chúng tôi chắc chắn đã đẩy mã Python hợp lệ
Hoan hô bài kiểm tra tích hợp của chúng tôi cũng đã vượt qua. Chúng tôi có thể xem kết quả đầu ra từ Hành động của mình và kiểm tra để biết rằng Hành động của chúng tôi đã đưa ra cảnh báo màu cam như mong đợi và sau đó báo cáo chính xác các cảnh báo
import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
8. Lưu ý rằng giá trị mặc định import os
import requests # noqa We are just importing this to prove the dependency installed correctly
def main[]:
my_input = os.environ["INPUT_MYINPUT"]
my_output = f"Hello {my_input}"
print[f"::set-output name=myOutput::{my_output}"]
if __name__ == "__main__":
main[]
5 cũng được điền cho chúng tôiXuất bản ra thị trường
Bây giờ chúng tôi đã tạo Hành động của mình và xác minh rằng nó hoạt động như mong đợi, chúng tôi có thể xuất bản nó lên Thị trường để những người khác có thể tìm thấy nó
Về mặt kỹ thuật, bất kỳ ai cũng có thể sử dụng Hành động của chúng tôi ngay bây giờ, chúng tôi đã tự sử dụng nó trong thử nghiệm tích hợp của mình. Nhưng chi nhánh
name: "YAML file linter"
description: "Lint a YAML file in your project"
author: "Jacob Tomlinson"
inputs:
path:
description: "Path to the YAML file to be linted"
required: true
strict:
description: "Run the linter in strict mode [error on warnings]"
required: false
default: false
outputs:
warnings:
description: "Number of warnings raised if lint was successful"
runs:
using: "docker"
image: "Dockerfile"
0 là một mục tiêu di động và chúng tôi muốn giúp mọi người dễ dàng tìm thấy nó hơn một chútĐể xuất bản nó, chúng tôi cần thực hiện phát hành GitHub. Chúng tôi có thể thực hiện việc này theo cách thông thường thông qua trang phát hành, nhưng GitHub có thể đã phát hiện ra rằng chúng tôi đã viết một Hành động và bao gồm lời nhắc trên repo của chúng tôi. Nhấp vào nút "Dự thảo bản phát hành" trên hộp thoại "Xuất bản hành động này lên thị trường" hoặc đi tới trang phát hành và nhấp vào nó từ đó
Nếu bạn đã theo dõi hộp thoại xuất bản, hộp kiểm “Xuất bản hành động này lên GitHub Marketplace” đã được chọn. Nếu bạn truy cập trang phát hành, bạn sẽ cần tự mình đánh dấu vào ô này
GitHub đã chọn một số thông tin từ tệp
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
2 của chúng tôi bao gồm tên và mô tả. Nó cũng cảnh báo chúng tôi rằng chúng tôi chưa đặt logo hoặc màu cho Hành động của mình. Đây là cách bạn đặt biểu tượng mà bạn sẽ thấy bên cạnh Hành động của mình trên Thị trườngHiện tại, chúng tôi sẽ bỏ qua phần của chúng tôi, phần này sẽ chỉ sử dụng các giá trị mặc định nhưng bạn có thể quay lại
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
2 của mình và đặt phần của bạn theo bất kỳ cách nào bạn muốnBạn cũng cần chọn các danh mục mà Hành động của bạn thuộc về. Đối với Hành động kẻ nói dối tệp YAML của chúng tôi, tôi đã sử dụng “Tích hợp liên tục” và “Tiện ích”
Bạn cũng cần đặt thẻ phiên bản. Sở thích của tôi là sử dụng SemVer và bắt đầu từ
name: "YAML file linter"
description: "Lint a YAML file in your project"
author: "Jacob Tomlinson"
inputs:
path:
description: "Path to the YAML file to be linted"
required: true
strict:
description: "Run the linter in strict mode [error on warnings]"
required: false
default: false
outputs:
warnings:
description: "Number of warnings raised if lint was successful"
runs:
using: "docker"
image: "Dockerfile"
3Khi bạn nhấp vào "Xuất bản bản phát hành", bạn sẽ được đưa đến trang phát hành. Bạn nên chú ý rằng ở phía bên trái nó có huy hiệu “Thị trường” nhỏ màu xanh lam, nghĩa là bản phát hành này có sẵn trên Thị trường
Nếu bạn nhấp vào liên kết đó, bạn sẽ được đưa đến trang Thị trường cho hành động mới xuất bản của mình
Danh sách Thị trường sẽ sử dụng
name: My Workflow
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: jacobtomlinson/python-container-action@master
with:
myInput: world
8 từ kho lưu trữ của bạn và cung cấp một số liên kết nhanh để mọi người bắt đầu với Hành động của bạnPhần kết luận
Đó là nó. Chúng tôi đã xuất bản thành công Hành động của mình trên GitHub Marketplace để người khác sử dụng. Họ có thể chỉ định một phiên bản được gắn thẻ để có thể đảm bảo quy trình công việc của họ mang tính quyết định và sử dụng phiên bản đó trong bất kỳ dự án nào họ muốn
Vì vậy, để nhanh chóng tóm tắt
- Chúng tôi đã nhân bản mẫu
3FROM python:3-slim AS builder ADD . /app WORKDIR /app # We are installing a dependency here directly into our app source dir RUN pip install --target=/app requests # A distroless container image with Python and some basics like SSL certificates # //github.com/GoogleContainerTools/distroless FROM gcr.io/distroless/python3-debian10 COPY --from=builder /app /app WORKDIR /app ENV PYTHONPATH /app CMD ["/cmd/main.py"]
- Chúng tôi đã viết một ứng dụng tiện ích nhỏ bằng Python để tách các tệp YAML
- Chúng tôi đã cập nhật tệp
2 của mình để mô tả đầu vào và đầu ra của ứng dụng của chúng tôiname: My Workflow on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: jacobtomlinson/python-container-action@master with: myInput: world
- Chúng tôi đã cập nhật tài liệu về
8 của mình để giúp mọi người bắt đầu với Hành động của chúng tôiname: My Workflow on: [push, pull_request] jobs: build: runs-on: ubuntu-latest steps: - uses: jacobtomlinson/python-container-action@master with: myInput: world
- Chúng tôi đã viết một số thử nghiệm để đảm bảo rằng Hành động của chúng tôi hoạt động như mong đợi khi chạy trong Quy trình hành động GitHub
- Cuối cùng, chúng tôi đã xuất bản nó lên GitHub Marketplace bằng cách phát hành
Bạn có thể xem Hành động kẻ nói dối tệp YAML tại đây và thậm chí sử dụng nó trong quy trình làm việc của riêng bạn
Nếu bạn thấy hướng dẫn này hữu ích hoặc thậm chí xuất bản Hành động GitHub của riêng bạn trên Thị trường, hãy cho tôi biết trong các nhận xét bên dưới hoặc trên phương tiện truyền thông xã hội